summaryrefslogtreecommitdiffstats
path: root/core/java/android
diff options
context:
space:
mode:
Diffstat (limited to 'core/java/android')
-rw-r--r--core/java/android/app/ActivityThread.java13
-rw-r--r--core/java/android/database/sqlite/SQLiteConnection.java208
-rw-r--r--core/java/android/database/sqlite/SQLiteConnectionPool.java9
-rw-r--r--core/java/android/database/sqlite/SQLiteCursor.java1
-rw-r--r--core/java/android/database/sqlite/SQLiteDatabase.java8
-rw-r--r--core/java/android/database/sqlite/SQLiteDebug.java107
-rw-r--r--core/java/android/database/sqlite/SQLiteSession.java62
-rw-r--r--core/java/android/database/sqlite/SQLiteUnfinalizedObjectsException.java31
-rw-r--r--core/java/android/inputmethodservice/ExtractEditText.java3
-rw-r--r--core/java/android/nfc/LlcpPacket.aidl22
-rw-r--r--core/java/android/nfc/LlcpPacket.java85
-rw-r--r--core/java/android/os/Process.java48
-rw-r--r--core/java/android/provider/UserDictionary.java75
-rw-r--r--core/java/android/view/GLES20Canvas.java86
-rw-r--r--core/java/android/view/VelocityTracker.java3
-rw-r--r--core/java/android/view/View.java44
-rw-r--r--core/java/android/view/ViewGroup.java7
-rwxr-xr-xcore/java/android/view/WindowOrientationListener.java386
-rw-r--r--core/java/android/webkit/HTML5VideoFullScreen.java4
-rw-r--r--core/java/android/webkit/HTML5VideoView.java44
-rw-r--r--core/java/android/webkit/HTML5VideoViewProxy.java29
-rw-r--r--core/java/android/webkit/WebView.java82
-rw-r--r--core/java/android/webkit/WebViewCore.java54
-rw-r--r--core/java/android/widget/AbsListView.java23
-rw-r--r--core/java/android/widget/AbsSeekBar.java13
-rw-r--r--core/java/android/widget/AbsSpinner.java15
-rw-r--r--core/java/android/widget/AdapterView.java2
-rw-r--r--core/java/android/widget/AdapterViewAnimator.java17
-rw-r--r--core/java/android/widget/AdapterViewFlipper.java16
-rw-r--r--core/java/android/widget/Button.java17
-rw-r--r--core/java/android/widget/CalendarView.java14
-rw-r--r--core/java/android/widget/CheckBox.java13
-rw-r--r--core/java/android/widget/CheckedTextView.java2
-rw-r--r--core/java/android/widget/Chronometer.java14
-rw-r--r--core/java/android/widget/CompoundButton.java2
-rw-r--r--core/java/android/widget/DatePicker.java13
-rw-r--r--core/java/android/widget/DigitalClock.java14
-rw-r--r--core/java/android/widget/EditText.java14
-rw-r--r--core/java/android/widget/ExpandableListView.java13
-rw-r--r--core/java/android/widget/FrameLayout.java15
-rw-r--r--core/java/android/widget/Gallery.java14
-rw-r--r--core/java/android/widget/GridLayout.java15
-rw-r--r--core/java/android/widget/GridView.java17
-rw-r--r--core/java/android/widget/HorizontalScrollView.java2
-rw-r--r--core/java/android/widget/ImageButton.java14
-rw-r--r--core/java/android/widget/ImageSwitcher.java17
-rw-r--r--core/java/android/widget/ImageView.java13
-rw-r--r--core/java/android/widget/LinearLayout.java16
-rw-r--r--core/java/android/widget/ListView.java17
-rw-r--r--core/java/android/widget/MediaController.java14
-rw-r--r--core/java/android/widget/MultiAutoCompleteTextView.java15
-rw-r--r--core/java/android/widget/NumberPicker.java13
-rw-r--r--core/java/android/widget/ProgressBar.java8
-rw-r--r--core/java/android/widget/QuickContactBadge.java14
-rw-r--r--core/java/android/widget/RadioButton.java13
-rw-r--r--core/java/android/widget/RadioGroup.java14
-rw-r--r--core/java/android/widget/RatingBar.java13
-rw-r--r--core/java/android/widget/RelativeLayout.java13
-rw-r--r--core/java/android/widget/RemoteViewsAdapter.java39
-rw-r--r--core/java/android/widget/ScrollView.java2
-rw-r--r--core/java/android/widget/SearchView.java15
-rw-r--r--core/java/android/widget/SeekBar.java15
-rw-r--r--core/java/android/widget/SlidingDrawer.java13
-rw-r--r--core/java/android/widget/Spinner.java16
-rw-r--r--core/java/android/widget/StackView.java14
-rw-r--r--core/java/android/widget/Switch.java13
-rw-r--r--core/java/android/widget/TabHost.java14
-rw-r--r--core/java/android/widget/TabWidget.java29
-rw-r--r--core/java/android/widget/TableLayout.java14
-rw-r--r--core/java/android/widget/TableRow.java14
-rw-r--r--core/java/android/widget/TextSwitcher.java14
-rw-r--r--core/java/android/widget/TextView.java94
-rw-r--r--core/java/android/widget/TimePicker.java13
-rw-r--r--core/java/android/widget/ToggleButton.java13
-rw-r--r--core/java/android/widget/TwoLineListItem.java18
-rw-r--r--core/java/android/widget/VideoView.java14
-rw-r--r--core/java/android/widget/ViewAnimator.java14
-rw-r--r--core/java/android/widget/ViewFlipper.java14
-rw-r--r--core/java/android/widget/ViewSwitcher.java14
-rw-r--r--core/java/android/widget/ZoomButton.java14
-rw-r--r--core/java/android/widget/ZoomControls.java14
81 files changed, 1642 insertions, 662 deletions
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index bac3c6c..455d2f0 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -486,7 +486,6 @@ public final class ActivityThread {
private static final String HEAP_COLUMN = "%13s %8s %8s %8s %8s %8s %8s";
private static final String ONE_COUNT_COLUMN = "%21s %8d";
private static final String TWO_COUNT_COLUMNS = "%21s %8d %21s %8d";
- private static final String TWO_COUNT_COLUMNS_DB = "%21s %8d %21s %8d";
private static final String DB_INFO_FORMAT = " %8s %8s %14s %14s %s";
// Formatting for checkin service - update version if row format changes
@@ -867,7 +866,6 @@ public final class ActivityThread {
int binderProxyObjectCount = Debug.getBinderProxyObjectCount();
int binderDeathObjectCount = Debug.getBinderDeathObjectCount();
long openSslSocketCount = Debug.countInstancesOfClass(OpenSSLSocketImpl.class);
- long sqliteAllocated = SQLiteDebug.getHeapAllocatedSize() / 1024;
SQLiteDebug.PagerStats stats = SQLiteDebug.getDatabaseInfo();
// For checkin, we print one long comma-separated list of values
@@ -935,9 +933,9 @@ public final class ActivityThread {
pw.print(openSslSocketCount); pw.print(',');
// SQL
- pw.print(sqliteAllocated); pw.print(',');
pw.print(stats.memoryUsed / 1024); pw.print(',');
- pw.print(stats.pageCacheOverflo / 1024); pw.print(',');
+ pw.print(stats.memoryUsed / 1024); pw.print(',');
+ pw.print(stats.pageCacheOverflow / 1024); pw.print(',');
pw.print(stats.largestMemAlloc / 1024);
for (int i = 0; i < stats.dbStats.size(); i++) {
DbStats dbStats = stats.dbStats.get(i);
@@ -1003,10 +1001,9 @@ public final class ActivityThread {
// SQLite mem info
pw.println(" ");
pw.println(" SQL");
- printRow(pw, TWO_COUNT_COLUMNS_DB, "heap:", sqliteAllocated, "MEMORY_USED:",
- stats.memoryUsed / 1024);
- printRow(pw, TWO_COUNT_COLUMNS_DB, "PAGECACHE_OVERFLOW:",
- stats.pageCacheOverflo / 1024, "MALLOC_SIZE:", stats.largestMemAlloc / 1024);
+ printRow(pw, ONE_COUNT_COLUMN, "MEMORY_USED:", stats.memoryUsed / 1024);
+ printRow(pw, TWO_COUNT_COLUMNS, "PAGECACHE_OVERFLOW:",
+ stats.pageCacheOverflow / 1024, "MALLOC_SIZE:", stats.largestMemAlloc / 1024);
pw.println(" ");
int N = stats.dbStats.size();
if (N > 0) {
diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java
index e45d66d..2ea936e 100644
--- a/core/java/android/database/sqlite/SQLiteConnection.java
+++ b/core/java/android/database/sqlite/SQLiteConnection.java
@@ -74,10 +74,17 @@ import java.util.regex.Pattern;
* queues.
* </p>
*
+ * <h2>Reentrance</h2>
+ * <p>
+ * This class must tolerate reentrant execution of SQLite operations because
+ * triggers may call custom SQLite functions that perform additional queries.
+ * </p>
+ *
* @hide
*/
public final class SQLiteConnection {
private static final String TAG = "SQLiteConnection";
+ private static final boolean DEBUG = false;
private static final String[] EMPTY_STRING_ARRAY = new String[0];
private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
@@ -205,13 +212,13 @@ public final class SQLiteConnection {
}
if (mConnectionPtr != 0) {
- mRecentOperations.beginOperation("close", null, null);
+ final int cookie = mRecentOperations.beginOperation("close", null, null);
try {
mPreparedStatementCache.evictAll();
nativeClose(mConnectionPtr);
mConnectionPtr = 0;
} finally {
- mRecentOperations.endOperation();
+ mRecentOperations.endOperation(cookie);
}
}
}
@@ -304,9 +311,9 @@ public final class SQLiteConnection {
throw new IllegalArgumentException("sql must not be null.");
}
- mRecentOperations.beginOperation("prepare", sql, null);
+ final int cookie = mRecentOperations.beginOperation("prepare", sql, null);
try {
- PreparedStatement statement = acquirePreparedStatement(sql);
+ final PreparedStatement statement = acquirePreparedStatement(sql);
try {
if (outStatementInfo != null) {
outStatementInfo.numParameters = statement.mNumParameters;
@@ -328,10 +335,10 @@ public final class SQLiteConnection {
releasePreparedStatement(statement);
}
} catch (RuntimeException ex) {
- mRecentOperations.failOperation(ex);
+ mRecentOperations.failOperation(cookie, ex);
throw ex;
} finally {
- mRecentOperations.endOperation();
+ mRecentOperations.endOperation(cookie);
}
}
@@ -349,9 +356,9 @@ public final class SQLiteConnection {
throw new IllegalArgumentException("sql must not be null.");
}
- mRecentOperations.beginOperation("execute", sql, bindArgs);
+ final int cookie = mRecentOperations.beginOperation("execute", sql, bindArgs);
try {
- PreparedStatement statement = acquirePreparedStatement(sql);
+ final PreparedStatement statement = acquirePreparedStatement(sql);
try {
throwIfStatementForbidden(statement);
bindArguments(statement, bindArgs);
@@ -361,10 +368,10 @@ public final class SQLiteConnection {
releasePreparedStatement(statement);
}
} catch (RuntimeException ex) {
- mRecentOperations.failOperation(ex);
+ mRecentOperations.failOperation(cookie, ex);
throw ex;
} finally {
- mRecentOperations.endOperation();
+ mRecentOperations.endOperation(cookie);
}
}
@@ -384,9 +391,9 @@ public final class SQLiteConnection {
throw new IllegalArgumentException("sql must not be null.");
}
- mRecentOperations.beginOperation("executeForLong", sql, bindArgs);
+ final int cookie = mRecentOperations.beginOperation("executeForLong", sql, bindArgs);
try {
- PreparedStatement statement = acquirePreparedStatement(sql);
+ final PreparedStatement statement = acquirePreparedStatement(sql);
try {
throwIfStatementForbidden(statement);
bindArguments(statement, bindArgs);
@@ -396,10 +403,10 @@ public final class SQLiteConnection {
releasePreparedStatement(statement);
}
} catch (RuntimeException ex) {
- mRecentOperations.failOperation(ex);
+ mRecentOperations.failOperation(cookie, ex);
throw ex;
} finally {
- mRecentOperations.endOperation();
+ mRecentOperations.endOperation(cookie);
}
}
@@ -419,9 +426,9 @@ public final class SQLiteConnection {
throw new IllegalArgumentException("sql must not be null.");
}
- mRecentOperations.beginOperation("executeForString", sql, bindArgs);
+ final int cookie = mRecentOperations.beginOperation("executeForString", sql, bindArgs);
try {
- PreparedStatement statement = acquirePreparedStatement(sql);
+ final PreparedStatement statement = acquirePreparedStatement(sql);
try {
throwIfStatementForbidden(statement);
bindArguments(statement, bindArgs);
@@ -431,10 +438,10 @@ public final class SQLiteConnection {
releasePreparedStatement(statement);
}
} catch (RuntimeException ex) {
- mRecentOperations.failOperation(ex);
+ mRecentOperations.failOperation(cookie, ex);
throw ex;
} finally {
- mRecentOperations.endOperation();
+ mRecentOperations.endOperation(cookie);
}
}
@@ -456,9 +463,10 @@ public final class SQLiteConnection {
throw new IllegalArgumentException("sql must not be null.");
}
- mRecentOperations.beginOperation("executeForBlobFileDescriptor", sql, bindArgs);
+ final int cookie = mRecentOperations.beginOperation("executeForBlobFileDescriptor",
+ sql, bindArgs);
try {
- PreparedStatement statement = acquirePreparedStatement(sql);
+ final PreparedStatement statement = acquirePreparedStatement(sql);
try {
throwIfStatementForbidden(statement);
bindArguments(statement, bindArgs);
@@ -470,10 +478,10 @@ public final class SQLiteConnection {
releasePreparedStatement(statement);
}
} catch (RuntimeException ex) {
- mRecentOperations.failOperation(ex);
+ mRecentOperations.failOperation(cookie, ex);
throw ex;
} finally {
- mRecentOperations.endOperation();
+ mRecentOperations.endOperation(cookie);
}
}
@@ -493,9 +501,10 @@ public final class SQLiteConnection {
throw new IllegalArgumentException("sql must not be null.");
}
- mRecentOperations.beginOperation("executeForChangedRowCount", sql, bindArgs);
+ final int cookie = mRecentOperations.beginOperation("executeForChangedRowCount",
+ sql, bindArgs);
try {
- PreparedStatement statement = acquirePreparedStatement(sql);
+ final PreparedStatement statement = acquirePreparedStatement(sql);
try {
throwIfStatementForbidden(statement);
bindArguments(statement, bindArgs);
@@ -506,10 +515,10 @@ public final class SQLiteConnection {
releasePreparedStatement(statement);
}
} catch (RuntimeException ex) {
- mRecentOperations.failOperation(ex);
+ mRecentOperations.failOperation(cookie, ex);
throw ex;
} finally {
- mRecentOperations.endOperation();
+ mRecentOperations.endOperation(cookie);
}
}
@@ -529,9 +538,10 @@ public final class SQLiteConnection {
throw new IllegalArgumentException("sql must not be null.");
}
- mRecentOperations.beginOperation("executeForLastInsertedRowId", sql, bindArgs);
+ final int cookie = mRecentOperations.beginOperation("executeForLastInsertedRowId",
+ sql, bindArgs);
try {
- PreparedStatement statement = acquirePreparedStatement(sql);
+ final PreparedStatement statement = acquirePreparedStatement(sql);
try {
throwIfStatementForbidden(statement);
bindArguments(statement, bindArgs);
@@ -542,10 +552,10 @@ public final class SQLiteConnection {
releasePreparedStatement(statement);
}
} catch (RuntimeException ex) {
- mRecentOperations.failOperation(ex);
+ mRecentOperations.failOperation(cookie, ex);
throw ex;
} finally {
- mRecentOperations.endOperation();
+ mRecentOperations.endOperation(cookie);
}
}
@@ -581,9 +591,10 @@ public final class SQLiteConnection {
int actualPos = -1;
int countedRows = -1;
int filledRows = -1;
- mRecentOperations.beginOperation("executeForCursorWindow", sql, bindArgs);
+ final int cookie = mRecentOperations.beginOperation("executeForCursorWindow",
+ sql, bindArgs);
try {
- PreparedStatement statement = acquirePreparedStatement(sql);
+ final PreparedStatement statement = acquirePreparedStatement(sql);
try {
throwIfStatementForbidden(statement);
bindArguments(statement, bindArgs);
@@ -600,11 +611,11 @@ public final class SQLiteConnection {
releasePreparedStatement(statement);
}
} catch (RuntimeException ex) {
- mRecentOperations.failOperation(ex);
+ mRecentOperations.failOperation(cookie, ex);
throw ex;
} finally {
- if (mRecentOperations.endOperationDeferLog()) {
- mRecentOperations.logOperation("window='" + window
+ if (mRecentOperations.endOperationDeferLog(cookie)) {
+ mRecentOperations.logOperation(cookie, "window='" + window
+ "', startPos=" + startPos
+ ", actualPos=" + actualPos
+ ", filledRows=" + filledRows
@@ -615,8 +626,15 @@ public final class SQLiteConnection {
private PreparedStatement acquirePreparedStatement(String sql) {
PreparedStatement statement = mPreparedStatementCache.get(sql);
+ boolean skipCache = false;
if (statement != null) {
- return statement;
+ if (!statement.mInUse) {
+ return statement;
+ }
+ // The statement is already in the cache but is in use (this statement appears
+ // to be not only re-entrant but recursive!). So prepare a new copy of the
+ // statement but do not cache it.
+ skipCache = true;
}
final int statementPtr = nativePrepareStatement(mConnectionPtr, sql);
@@ -625,7 +643,7 @@ public final class SQLiteConnection {
final int type = DatabaseUtils.getSqlStatementType(sql);
final boolean readOnly = nativeIsReadOnly(mConnectionPtr, statementPtr);
statement = obtainPreparedStatement(sql, statementPtr, numParameters, type, readOnly);
- if (isCacheable(type)) {
+ if (!skipCache && isCacheable(type)) {
mPreparedStatementCache.put(sql, statement);
statement.mInCache = true;
}
@@ -637,31 +655,38 @@ public final class SQLiteConnection {
}
throw ex;
}
+ statement.mInUse = true;
return statement;
}
private void releasePreparedStatement(PreparedStatement statement) {
+ statement.mInUse = false;
if (statement.mInCache) {
try {
nativeResetStatementAndClearBindings(mConnectionPtr, statement.mStatementPtr);
} catch (SQLiteException ex) {
- // The statement could not be reset due to an error.
- // The entryRemoved() callback for the cache will recursively call
- // releasePreparedStatement() again, but this time mInCache will be false
- // so the statement will be finalized and recycled.
- if (SQLiteDebug.DEBUG_SQL_CACHE) {
- Log.v(TAG, "Could not reset prepared statement due to an exception. "
+ // The statement could not be reset due to an error. Remove it from the cache.
+ // When remove() is called, the cache will invoke its entryRemoved() callback,
+ // which will in turn call finalizePreparedStatement() to finalize and
+ // recycle the statement.
+ if (DEBUG) {
+ Log.d(TAG, "Could not reset prepared statement due to an exception. "
+ "Removing it from the cache. SQL: "
+ trimSqlForDisplay(statement.mSql), ex);
}
+
mPreparedStatementCache.remove(statement.mSql);
}
} else {
- nativeFinalizeStatement(mConnectionPtr, statement.mStatementPtr);
- recyclePreparedStatement(statement);
+ finalizePreparedStatement(statement);
}
}
+ private void finalizePreparedStatement(PreparedStatement statement) {
+ nativeFinalizeStatement(mConnectionPtr, statement.mStatementPtr);
+ recyclePreparedStatement(statement);
+ }
+
private void bindArguments(PreparedStatement statement, Object[] bindArgs) {
final int count = bindArgs != null ? bindArgs.length : 0;
if (count != statement.mNumParameters) {
@@ -735,9 +760,10 @@ public final class SQLiteConnection {
* Dumps debugging information about this connection.
*
* @param printer The printer to receive the dump, not null.
+ * @param verbose True to dump more verbose information.
*/
- public void dump(Printer printer) {
- dumpUnsafe(printer);
+ public void dump(Printer printer, boolean verbose) {
+ dumpUnsafe(printer, verbose);
}
/**
@@ -752,15 +778,21 @@ public final class SQLiteConnection {
* it should not crash. This is ok as it is only used for diagnostic purposes.
*
* @param printer The printer to receive the dump, not null.
+ * @param verbose True to dump more verbose information.
*/
- void dumpUnsafe(Printer printer) {
+ void dumpUnsafe(Printer printer, boolean verbose) {
printer.println("Connection #" + mConnectionId + ":");
+ if (verbose) {
+ printer.println(" connectionPtr: 0x" + Integer.toHexString(mConnectionPtr));
+ }
printer.println(" isPrimaryConnection: " + mIsPrimaryConnection);
- printer.println(" connectionPtr: 0x" + Integer.toHexString(mConnectionPtr));
printer.println(" onlyAllowReadOnlyOperations: " + mOnlyAllowReadOnlyOperations);
mRecentOperations.dump(printer);
- mPreparedStatementCache.dump(printer);
+
+ if (verbose) {
+ mPreparedStatementCache.dump(printer);
+ }
}
/**
@@ -917,6 +949,12 @@ public final class SQLiteConnection {
// True if the statement is in the cache.
public boolean mInCache;
+
+ // True if the statement is in use (currently executing).
+ // We need this flag because due to the use of custom functions in triggers, it's
+ // possible for SQLite calls to be re-entrant. Consequently we need to prevent
+ // in use statements from being finalized until they are no longer in use.
+ public boolean mInUse;
}
private final class PreparedStatementCache
@@ -929,7 +967,9 @@ public final class SQLiteConnection {
protected void entryRemoved(boolean evicted, String key,
PreparedStatement oldValue, PreparedStatement newValue) {
oldValue.mInCache = false;
- releasePreparedStatement(oldValue);
+ if (!oldValue.mInUse) {
+ finalizePreparedStatement(oldValue);
+ }
}
public void dump(Printer printer) {
@@ -957,12 +997,15 @@ public final class SQLiteConnection {
}
private static final class OperationLog {
- private static final int MAX_RECENT_OPERATIONS = 10;
+ private static final int MAX_RECENT_OPERATIONS = 20;
+ private static final int COOKIE_GENERATION_SHIFT = 8;
+ private static final int COOKIE_INDEX_MASK = 0xff;
private final Operation[] mOperations = new Operation[MAX_RECENT_OPERATIONS];
private int mIndex;
+ private int mGeneration;
- public void beginOperation(String kind, String sql, Object[] bindArgs) {
+ public int beginOperation(String kind, String sql, Object[] bindArgs) {
synchronized (mOperations) {
final int index = (mIndex + 1) % MAX_RECENT_OPERATIONS;
Operation operation = mOperations[index];
@@ -995,47 +1038,54 @@ public final class SQLiteConnection {
}
}
}
+ operation.mCookie = newOperationCookieLocked(index);
mIndex = index;
+ return operation.mCookie;
}
}
- public void failOperation(Exception ex) {
+ public void failOperation(int cookie, Exception ex) {
synchronized (mOperations) {
- final Operation operation = mOperations[mIndex];
- operation.mException = ex;
+ final Operation operation = getOperationLocked(cookie);
+ if (operation != null) {
+ operation.mException = ex;
+ }
}
}
- public boolean endOperationDeferLog() {
+ public void endOperation(int cookie) {
synchronized (mOperations) {
- return endOperationDeferLogLocked();
+ if (endOperationDeferLogLocked(cookie)) {
+ logOperationLocked(cookie, null);
+ }
}
}
- private boolean endOperationDeferLogLocked() {
- final Operation operation = mOperations[mIndex];
- operation.mEndTime = System.currentTimeMillis();
- operation.mFinished = true;
- return SQLiteDebug.DEBUG_LOG_SLOW_QUERIES && SQLiteDebug.shouldLogSlowQuery(
- operation.mEndTime - operation.mStartTime);
+ public boolean endOperationDeferLog(int cookie) {
+ synchronized (mOperations) {
+ return endOperationDeferLogLocked(cookie);
+ }
}
- public void endOperation() {
+ public void logOperation(int cookie, String detail) {
synchronized (mOperations) {
- if (endOperationDeferLogLocked()) {
- logOperationLocked(null);
- }
+ logOperationLocked(cookie, detail);
}
}
- public void logOperation(String detail) {
- synchronized (mOperations) {
- logOperationLocked(detail);
+ private boolean endOperationDeferLogLocked(int cookie) {
+ final Operation operation = getOperationLocked(cookie);
+ if (operation != null) {
+ operation.mEndTime = System.currentTimeMillis();
+ operation.mFinished = true;
+ return SQLiteDebug.DEBUG_LOG_SLOW_QUERIES && SQLiteDebug.shouldLogSlowQuery(
+ operation.mEndTime - operation.mStartTime);
}
+ return false;
}
- private void logOperationLocked(String detail) {
- final Operation operation = mOperations[mIndex];
+ private void logOperationLocked(int cookie, String detail) {
+ final Operation operation = getOperationLocked(cookie);
StringBuilder msg = new StringBuilder();
operation.describe(msg);
if (detail != null) {
@@ -1044,6 +1094,17 @@ public final class SQLiteConnection {
Log.d(TAG, msg.toString());
}
+ private int newOperationCookieLocked(int index) {
+ final int generation = mGeneration++;
+ return generation << COOKIE_GENERATION_SHIFT | index;
+ }
+
+ private Operation getOperationLocked(int cookie) {
+ final int index = cookie & COOKIE_INDEX_MASK;
+ final Operation operation = mOperations[index];
+ return operation.mCookie == cookie ? operation : null;
+ }
+
public String describeCurrentOperation() {
synchronized (mOperations) {
final Operation operation = mOperations[mIndex];
@@ -1097,6 +1158,7 @@ public final class SQLiteConnection {
public ArrayList<Object> mBindArgs;
public boolean mFinished;
public Exception mException;
+ public int mCookie;
public void describe(StringBuilder msg) {
msg.append(mKind);
diff --git a/core/java/android/database/sqlite/SQLiteConnectionPool.java b/core/java/android/database/sqlite/SQLiteConnectionPool.java
index b88bfee..5469213 100644
--- a/core/java/android/database/sqlite/SQLiteConnectionPool.java
+++ b/core/java/android/database/sqlite/SQLiteConnectionPool.java
@@ -833,8 +833,9 @@ public final class SQLiteConnectionPool implements Closeable {
* Dumps debugging information about this connection pool.
*
* @param printer The printer to receive the dump, not null.
+ * @param verbose True to dump more verbose information.
*/
- public void dump(Printer printer) {
+ public void dump(Printer printer, boolean verbose) {
Printer indentedPrinter = PrefixPrinter.create(printer, " ");
synchronized (mLock) {
printer.println("Connection pool for " + mConfiguration.path + ":");
@@ -843,7 +844,7 @@ public final class SQLiteConnectionPool implements Closeable {
printer.println(" Available primary connection:");
if (mAvailablePrimaryConnection != null) {
- mAvailablePrimaryConnection.dump(indentedPrinter);
+ mAvailablePrimaryConnection.dump(indentedPrinter, verbose);
} else {
indentedPrinter.println("<none>");
}
@@ -852,7 +853,7 @@ public final class SQLiteConnectionPool implements Closeable {
if (!mAvailableNonPrimaryConnections.isEmpty()) {
final int count = mAvailableNonPrimaryConnections.size();
for (int i = 0; i < count; i++) {
- mAvailableNonPrimaryConnections.get(i).dump(indentedPrinter);
+ mAvailableNonPrimaryConnections.get(i).dump(indentedPrinter, verbose);
}
} else {
indentedPrinter.println("<none>");
@@ -863,7 +864,7 @@ public final class SQLiteConnectionPool implements Closeable {
for (Map.Entry<SQLiteConnection, Boolean> entry :
mAcquiredConnections.entrySet()) {
final SQLiteConnection connection = entry.getKey();
- connection.dumpUnsafe(indentedPrinter);
+ connection.dumpUnsafe(indentedPrinter, verbose);
indentedPrinter.println(" Pending reconfiguration: " + entry.getValue());
}
} else {
diff --git a/core/java/android/database/sqlite/SQLiteCursor.java b/core/java/android/database/sqlite/SQLiteCursor.java
index 9dcb498..946300f 100644
--- a/core/java/android/database/sqlite/SQLiteCursor.java
+++ b/core/java/android/database/sqlite/SQLiteCursor.java
@@ -269,7 +269,6 @@ public class SQLiteCursor extends AbstractWindowedCursor {
mStackTrace);
}
close();
- SQLiteDebug.notifyActiveCursorFinalized();
}
} finally {
super.finalize();
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index 377a680..9cb6480 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -1665,17 +1665,17 @@ public class SQLiteDatabase extends SQLiteClosable {
* Dump detailed information about all open databases in the current process.
* Used by bug report.
*/
- static void dumpAll(Printer printer) {
+ static void dumpAll(Printer printer, boolean verbose) {
for (SQLiteDatabase db : getActiveDatabases()) {
- db.dump(printer);
+ db.dump(printer, verbose);
}
}
- private void dump(Printer printer) {
+ private void dump(Printer printer, boolean verbose) {
synchronized (mLock) {
if (mConnectionPoolLocked != null) {
printer.println("");
- mConnectionPoolLocked.dump(printer);
+ mConnectionPoolLocked.dump(printer, verbose);
}
}
}
diff --git a/core/java/android/database/sqlite/SQLiteDebug.java b/core/java/android/database/sqlite/SQLiteDebug.java
index d87c3e4..95350ba 100644
--- a/core/java/android/database/sqlite/SQLiteDebug.java
+++ b/core/java/android/database/sqlite/SQLiteDebug.java
@@ -29,6 +29,8 @@ import android.util.Printer;
* {@hide}
*/
public final class SQLiteDebug {
+ private static native void nativeGetPagerStats(PagerStats stats);
+
/**
* Controls the printing of informational SQL log messages.
*/
@@ -49,31 +51,6 @@ public final class SQLiteDebug {
Log.isLoggable("SQLiteTime", Log.VERBOSE);
/**
- * Controls the printing of compiled-sql-statement cache stats.
- */
- public static final boolean DEBUG_SQL_CACHE =
- Log.isLoggable("SQLiteCompiledSql", Log.VERBOSE);
-
- /**
- * Controls the stack trace reporting of active cursors being
- * finalized.
- */
- public static final boolean DEBUG_ACTIVE_CURSOR_FINALIZATION =
- Log.isLoggable("SQLiteCursorClosing", Log.VERBOSE);
-
- /**
- * Controls the tracking of time spent holding the database lock.
- */
- public static final boolean DEBUG_LOCK_TIME_TRACKING =
- Log.isLoggable("SQLiteLockTime", Log.VERBOSE);
-
- /**
- * Controls the printing of stack traces when tracking the time spent holding the database lock.
- */
- public static final boolean DEBUG_LOCK_TIME_TRACKING_STACK_TRACE =
- Log.isLoggable("SQLiteLockStackTrace", Log.VERBOSE);
-
- /**
* True to enable database performance testing instrumentation.
* @hide
*/
@@ -98,30 +75,9 @@ public final class SQLiteDebug {
/**
* Contains statistics about the active pagers in the current process.
*
- * @see #getPagerStats(PagerStats)
+ * @see #nativeGetPagerStats(PagerStats)
*/
public static class PagerStats {
- /** The total number of bytes in all pagers in the current process
- * @deprecated not used any longer
- */
- @Deprecated
- public long totalBytes;
- /** The number of bytes in referenced pages in all pagers in the current process
- * @deprecated not used any longer
- * */
- @Deprecated
- public long referencedBytes;
- /** The number of bytes in all database files opened in the current process
- * @deprecated not used any longer
- */
- @Deprecated
- public long databaseBytes;
- /** The number of pagers opened in the current process
- * @deprecated not used any longer
- */
- @Deprecated
- public int numPagers;
-
/** the current amount of memory checked out by sqlite using sqlite3_malloc().
* documented at http://www.sqlite.org/c3ref/c_status_malloc_size.html
*/
@@ -134,7 +90,7 @@ public final class SQLiteDebug {
* that overflowed because no space was left in the page cache.
* documented at http://www.sqlite.org/c3ref/c_status_malloc_size.html
*/
- public int pageCacheOverflo;
+ public int pageCacheOverflow;
/** records the largest memory allocation request handed to sqlite3.
* documented at http://www.sqlite.org/c3ref/c_status_malloc_size.html
@@ -182,7 +138,8 @@ public final class SQLiteDebug {
*/
public static PagerStats getDatabaseInfo() {
PagerStats stats = new PagerStats();
- getPagerStats(stats);
+ SQLiteGlobal.initializeOnce();
+ nativeGetPagerStats(stats);
stats.dbStats = SQLiteDatabase.getDbStats();
return stats;
}
@@ -190,52 +147,16 @@ public final class SQLiteDebug {
/**
* Dumps detailed information about all databases used by the process.
* @param printer The printer for dumping database state.
+ * @param args Command-line arguments supplied to dumpsys dbinfo
*/
public static void dump(Printer printer, String[] args) {
- SQLiteDatabase.dumpAll(printer);
- }
-
- /**
- * Gathers statistics about all pagers in the current process.
- */
- public static native void getPagerStats(PagerStats stats);
-
- /**
- * Returns the size of the SQLite heap.
- * @return The size of the SQLite heap in bytes.
- */
- public static native long getHeapSize();
-
- /**
- * Returns the amount of allocated memory in the SQLite heap.
- * @return The allocated size in bytes.
- */
- public static native long getHeapAllocatedSize();
-
- /**
- * Returns the amount of free memory in the SQLite heap.
- * @return The freed size in bytes.
- */
- public static native long getHeapFreeSize();
-
- /**
- * Determines the number of dirty belonging to the SQLite
- * heap segments of this process. pages[0] returns the number of
- * shared pages, pages[1] returns the number of private pages
- */
- public static native void getHeapDirtyPages(int[] pages);
-
- private static int sNumActiveCursorsFinalized = 0;
-
- /**
- * Returns the number of active cursors that have been finalized. This depends on the GC having
- * run but is still useful for tests.
- */
- public static int getNumActiveCursorsFinalized() {
- return sNumActiveCursorsFinalized;
- }
+ boolean verbose = false;
+ for (String arg : args) {
+ if (arg.equals("-v")) {
+ verbose = true;
+ }
+ }
- static synchronized void notifyActiveCursorFinalized() {
- sNumActiveCursorsFinalized++;
+ SQLiteDatabase.dumpAll(printer, verbose);
}
}
diff --git a/core/java/android/database/sqlite/SQLiteSession.java b/core/java/android/database/sqlite/SQLiteSession.java
index 61fe45a..a933051 100644
--- a/core/java/android/database/sqlite/SQLiteSession.java
+++ b/core/java/android/database/sqlite/SQLiteSession.java
@@ -150,6 +150,12 @@ import android.os.ParcelFileDescriptor;
* A query that works well on 100 rows may struggle with 10,000.</li>
* </ul>
*
+ * <h2>Reentrance</h2>
+ * <p>
+ * This class must tolerate reentrant execution of SQLite operations because
+ * triggers may call custom SQLite functions that perform additional queries.
+ * </p>
+ *
* TODO: Support timeouts on all possibly blocking operations.
*
* @hide
@@ -159,6 +165,7 @@ public final class SQLiteSession {
private SQLiteConnection mConnection;
private int mConnectionFlags;
+ private int mConnectionUseCount;
private Transaction mTransactionPool;
private Transaction mTransactionStack;
@@ -289,7 +296,9 @@ public final class SQLiteSession {
private void beginTransactionUnchecked(int transactionMode,
SQLiteTransactionListener transactionListener, int connectionFlags) {
- acquireConnectionIfNoTransaction(null, connectionFlags); // might throw
+ if (mTransactionStack == null) {
+ acquireConnection(null, connectionFlags); // might throw
+ }
try {
// Set up the transaction such that we can back out safely
// in case we fail part way.
@@ -325,7 +334,9 @@ public final class SQLiteSession {
transaction.mParent = mTransactionStack;
mTransactionStack = transaction;
} finally {
- releaseConnectionIfNoTransaction(); // might throw
+ if (mTransactionStack == null) {
+ releaseConnection(); // might throw
+ }
}
}
@@ -408,7 +419,7 @@ public final class SQLiteSession {
mConnection.execute("ROLLBACK;", null); // might throw
}
} finally {
- releaseConnectionIfNoTransaction(); // might throw
+ releaseConnection(); // might throw
}
}
@@ -534,11 +545,11 @@ public final class SQLiteSession {
throw new IllegalArgumentException("sql must not be null.");
}
- acquireConnectionIfNoTransaction(sql, connectionFlags); // might throw
+ acquireConnection(sql, connectionFlags); // might throw
try {
mConnection.prepare(sql, outStatementInfo); // might throw
} finally {
- releaseConnectionIfNoTransaction(); // might throw
+ releaseConnection(); // might throw
}
}
@@ -562,11 +573,11 @@ public final class SQLiteSession {
return;
}
- acquireConnectionIfNoTransaction(sql, connectionFlags); // might throw
+ acquireConnection(sql, connectionFlags); // might throw
try {
mConnection.execute(sql, bindArgs); // might throw
} finally {
- releaseConnectionIfNoTransaction(); // might throw
+ releaseConnection(); // might throw
}
}
@@ -592,11 +603,11 @@ public final class SQLiteSession {
return 0;
}
- acquireConnectionIfNoTransaction(sql, connectionFlags); // might throw
+ acquireConnection(sql, connectionFlags); // might throw
try {
return mConnection.executeForLong(sql, bindArgs); // might throw
} finally {
- releaseConnectionIfNoTransaction(); // might throw
+ releaseConnection(); // might throw
}
}
@@ -622,11 +633,11 @@ public final class SQLiteSession {
return null;
}
- acquireConnectionIfNoTransaction(sql, connectionFlags); // might throw
+ acquireConnection(sql, connectionFlags); // might throw
try {
return mConnection.executeForString(sql, bindArgs); // might throw
} finally {
- releaseConnectionIfNoTransaction(); // might throw
+ releaseConnection(); // might throw
}
}
@@ -655,11 +666,11 @@ public final class SQLiteSession {
return null;
}
- acquireConnectionIfNoTransaction(sql, connectionFlags); // might throw
+ acquireConnection(sql, connectionFlags); // might throw
try {
return mConnection.executeForBlobFileDescriptor(sql, bindArgs); // might throw
} finally {
- releaseConnectionIfNoTransaction(); // might throw
+ releaseConnection(); // might throw
}
}
@@ -685,11 +696,11 @@ public final class SQLiteSession {
return 0;
}
- acquireConnectionIfNoTransaction(sql, connectionFlags); // might throw
+ acquireConnection(sql, connectionFlags); // might throw
try {
return mConnection.executeForChangedRowCount(sql, bindArgs); // might throw
} finally {
- releaseConnectionIfNoTransaction(); // might throw
+ releaseConnection(); // might throw
}
}
@@ -715,11 +726,11 @@ public final class SQLiteSession {
return 0;
}
- acquireConnectionIfNoTransaction(sql, connectionFlags); // might throw
+ acquireConnection(sql, connectionFlags); // might throw
try {
return mConnection.executeForLastInsertedRowId(sql, bindArgs); // might throw
} finally {
- releaseConnectionIfNoTransaction(); // might throw
+ releaseConnection(); // might throw
}
}
@@ -760,12 +771,12 @@ public final class SQLiteSession {
return 0;
}
- acquireConnectionIfNoTransaction(sql, connectionFlags); // might throw
+ acquireConnection(sql, connectionFlags); // might throw
try {
return mConnection.executeForCursorWindow(sql, bindArgs,
window, startPos, requiredPos, countAllRows); // might throw
} finally {
- releaseConnectionIfNoTransaction(); // might throw
+ releaseConnection(); // might throw
}
}
@@ -807,16 +818,19 @@ public final class SQLiteSession {
return false;
}
- private void acquireConnectionIfNoTransaction(String sql, int connectionFlags) {
- if (mTransactionStack == null) {
- assert mConnection == null;
+ private void acquireConnection(String sql, int connectionFlags) {
+ if (mConnection == null) {
+ assert mConnectionUseCount == 0;
mConnection = mConnectionPool.acquireConnection(sql, connectionFlags); // might throw
mConnectionFlags = connectionFlags;
}
+ mConnectionUseCount += 1;
}
- private void releaseConnectionIfNoTransaction() {
- if (mTransactionStack == null && mConnection != null) {
+ private void releaseConnection() {
+ assert mConnection != null;
+ assert mConnectionUseCount > 0;
+ if (--mConnectionUseCount == 0) {
try {
mConnectionPool.releaseConnection(mConnection); // might throw
} finally {
diff --git a/core/java/android/database/sqlite/SQLiteUnfinalizedObjectsException.java b/core/java/android/database/sqlite/SQLiteUnfinalizedObjectsException.java
deleted file mode 100644
index bcf95e2..0000000
--- a/core/java/android/database/sqlite/SQLiteUnfinalizedObjectsException.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.database.sqlite;
-
-/**
- * Thrown if the database can't be closed because of some un-closed
- * Cursor or SQLiteStatement objects. Could happen when a thread is trying to close
- * the database while another thread still hasn't closed a Cursor on that database.
- * @hide
- */
-public class SQLiteUnfinalizedObjectsException extends SQLiteException {
- public SQLiteUnfinalizedObjectsException() {}
-
- public SQLiteUnfinalizedObjectsException(String error) {
- super(error);
- }
-}
diff --git a/core/java/android/inputmethodservice/ExtractEditText.java b/core/java/android/inputmethodservice/ExtractEditText.java
index 10c1195..23ae21b 100644
--- a/core/java/android/inputmethodservice/ExtractEditText.java
+++ b/core/java/android/inputmethodservice/ExtractEditText.java
@@ -100,6 +100,9 @@ public class ExtractEditText extends EditText {
@Override public boolean onTextContextMenuItem(int id) {
if (mIME != null && mIME.onExtractTextContextMenuItem(id)) {
+ // Mode was started on Extracted, needs to be stopped here.
+ // Cut and paste will change the text, which stops selection mode.
+ if (id == android.R.id.copy) stopSelectionActionMode();
return true;
}
return super.onTextContextMenuItem(id);
diff --git a/core/java/android/nfc/LlcpPacket.aidl b/core/java/android/nfc/LlcpPacket.aidl
deleted file mode 100644
index 80f424d..0000000
--- a/core/java/android/nfc/LlcpPacket.aidl
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.nfc;
-
-/**
- * @hide
- */
-parcelable LlcpPacket; \ No newline at end of file
diff --git a/core/java/android/nfc/LlcpPacket.java b/core/java/android/nfc/LlcpPacket.java
deleted file mode 100644
index 9919dc4..0000000
--- a/core/java/android/nfc/LlcpPacket.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.nfc;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * Represents a LLCP packet received in a LLCP Connectionless communication;
- * @hide
- */
-public class LlcpPacket implements Parcelable {
-
- private final int mRemoteSap;
-
- private final byte[] mDataBuffer;
-
- /**
- * Creates a LlcpPacket to be sent to a remote Service Access Point number
- * (SAP)
- *
- * @param sap Remote Service Access Point number
- * @param data Data buffer
- */
- public LlcpPacket(int sap, byte[] data) {
- mRemoteSap = sap;
- mDataBuffer = data;
- }
-
- /**
- * Returns the remote Service Access Point number
- */
- public int getRemoteSap() {
- return mRemoteSap;
- }
-
- /**
- * Returns the data buffer
- */
- public byte[] getDataBuffer() {
- return mDataBuffer;
- }
-
- public int describeContents() {
- return 0;
- }
-
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(mRemoteSap);
- dest.writeInt(mDataBuffer.length);
- dest.writeByteArray(mDataBuffer);
- }
-
- public static final Parcelable.Creator<LlcpPacket> CREATOR = new Parcelable.Creator<LlcpPacket>() {
- public LlcpPacket createFromParcel(Parcel in) {
- // Remote SAP
- short sap = (short)in.readInt();
-
- // Data Buffer
- int dataLength = in.readInt();
- byte[] data = new byte[dataLength];
- in.readByteArray(data);
-
- return new LlcpPacket(sap, data);
- }
-
- public LlcpPacket[] newArray(int size) {
- return new LlcpPacket[size];
- }
- };
-} \ No newline at end of file
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index e1bc275..cdf235d 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -219,6 +219,36 @@ public class Process {
public static final int THREAD_PRIORITY_LESS_FAVORABLE = +1;
/**
+ * Default scheduling policy
+ * @hide
+ */
+ public static final int SCHED_OTHER = 0;
+
+ /**
+ * First-In First-Out scheduling policy
+ * @hide
+ */
+ public static final int SCHED_FIFO = 1;
+
+ /**
+ * Round-Robin scheduling policy
+ * @hide
+ */
+ public static final int SCHED_RR = 2;
+
+ /**
+ * Batch scheduling policy
+ * @hide
+ */
+ public static final int SCHED_BATCH = 3;
+
+ /**
+ * Idle scheduling policy
+ * @hide
+ */
+ public static final int SCHED_IDLE = 5;
+
+ /**
* Default thread group - gets a 'normal' share of the CPU
* @hide
*/
@@ -675,6 +705,24 @@ public class Process {
throws IllegalArgumentException;
/**
+ * Set the scheduling policy and priority of a thread, based on Linux.
+ *
+ * @param tid The identifier of the thread/process to change.
+ * @param policy A Linux scheduling policy such as SCHED_OTHER etc.
+ * @param priority A Linux priority level in a range appropriate for the given policy.
+ *
+ * @throws IllegalArgumentException Throws IllegalArgumentException if
+ * <var>tid</var> does not exist, or if <var>priority</var> is out of range for the policy.
+ * @throws SecurityException Throws SecurityException if your process does
+ * not have permission to modify the given thread, or to use the given
+ * scheduling policy or priority.
+ *
+ * {@hide}
+ */
+ public static final native void setThreadScheduler(int tid, int policy, int priority)
+ throws IllegalArgumentException;
+
+ /**
* Determine whether the current environment supports multiple processes.
*
* @return Returns true if the system can run in multiple processes, else
diff --git a/core/java/android/provider/UserDictionary.java b/core/java/android/provider/UserDictionary.java
index 5a7ef85..a9b106a 100644
--- a/core/java/android/provider/UserDictionary.java
+++ b/core/java/android/provider/UserDictionary.java
@@ -40,6 +40,9 @@ public class UserDictionary {
public static final Uri CONTENT_URI =
Uri.parse("content://" + AUTHORITY);
+ private static final int FREQUENCY_MIN = 0;
+ private static final int FREQUENCY_MAX = 255;
+
/**
* Contains the user defined words.
*/
@@ -87,12 +90,24 @@ public class UserDictionary {
*/
public static final String APP_ID = "appid";
- /** The locale type to specify that the word is common to all locales. */
+ /**
+ * An optional shortcut for this word. When the shortcut is typed, supporting IMEs should
+ * suggest the word in this row as an alternate spelling too.
+ */
+ public static final String SHORTCUT = "shortcut";
+
+ /**
+ * @deprecated Use {@link #addWord(Context, String, int, String, Locale)}.
+ */
+ @Deprecated
public static final int LOCALE_TYPE_ALL = 0;
-
- /** The locale type to specify that the word is for the current locale. */
+
+ /**
+ * @deprecated Use {@link #addWord(Context, String, int, String, Locale)}.
+ */
+ @Deprecated
public static final int LOCALE_TYPE_CURRENT = 1;
-
+
/**
* Sort by descending order of frequency.
*/
@@ -100,35 +115,65 @@ public class UserDictionary {
/** Adds a word to the dictionary, with the given frequency and the specified
* specified locale type.
+ *
+ * @deprecated Please use
+ * {@link #addWord(Context, String, int, String, Locale)} instead.
+ *
* @param context the current application context
* @param word the word to add to the dictionary. This should not be null or
* empty.
* @param localeType the locale type for this word. It should be one of
* {@link #LOCALE_TYPE_ALL} or {@link #LOCALE_TYPE_CURRENT}.
*/
- public static void addWord(Context context, String word,
+ @Deprecated
+ public static void addWord(Context context, String word,
int frequency, int localeType) {
- final ContentResolver resolver = context.getContentResolver();
- if (TextUtils.isEmpty(word) || localeType < 0 || localeType > 1) {
+ if (localeType != LOCALE_TYPE_ALL && localeType != LOCALE_TYPE_CURRENT) {
return;
}
-
- if (frequency < 0) frequency = 0;
- if (frequency > 255) frequency = 255;
- String locale = null;
+ final Locale locale;
- // TODO: Verify if this is the best way to get the current locale
if (localeType == LOCALE_TYPE_CURRENT) {
- locale = Locale.getDefault().toString();
+ locale = Locale.getDefault();
+ } else {
+ locale = null;
}
- ContentValues values = new ContentValues(4);
+
+ addWord(context, word, frequency, null, locale);
+ }
+
+ /** Adds a word to the dictionary, with the given frequency and the specified
+ * locale type.
+ *
+ * @param context the current application context
+ * @param word the word to add to the dictionary. This should not be null or
+ * empty.
+ * @param shortcut optional shortcut spelling for this word. When the shortcut
+ * is typed, the word may be suggested by applications that support it. May be null.
+ * @param locale the locale to insert the word for, or null to insert the word
+ * for all locales.
+ */
+ public static void addWord(Context context, String word,
+ int frequency, String shortcut, Locale locale) {
+ final ContentResolver resolver = context.getContentResolver();
+
+ if (TextUtils.isEmpty(word)) {
+ return;
+ }
+
+ if (frequency < FREQUENCY_MIN) frequency = FREQUENCY_MIN;
+ if (frequency > FREQUENCY_MAX) frequency = FREQUENCY_MAX;
+
+ final int COLUMN_COUNT = 5;
+ ContentValues values = new ContentValues(COLUMN_COUNT);
values.put(WORD, word);
values.put(FREQUENCY, frequency);
- values.put(LOCALE, locale);
+ values.put(LOCALE, null == locale ? null : locale.toString());
values.put(APP_ID, 0); // TODO: Get App UID
+ values.put(SHORTCUT, shortcut);
Uri result = resolver.insert(CONTENT_URI, values);
// It's ok if the insert doesn't succeed because the word
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index 8a9be85..e06d661 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -61,6 +61,7 @@ class GLES20Canvas extends HardwareCanvas {
private final float[] mLine = new float[4];
private final Rect mClipBounds = new Rect();
+ private final RectF mPathBounds = new RectF();
private DrawFilter mFilter;
@@ -406,12 +407,18 @@ class GLES20Canvas extends HardwareCanvas {
@Override
public boolean clipPath(Path path) {
- throw new UnsupportedOperationException();
+ // TODO: Implement
+ path.computeBounds(mPathBounds, true);
+ return nClipRect(mRenderer, mPathBounds.left, mPathBounds.top,
+ mPathBounds.right, mPathBounds.bottom, Region.Op.INTERSECT.nativeInt);
}
@Override
public boolean clipPath(Path path, Region.Op op) {
- throw new UnsupportedOperationException();
+ // TODO: Implement
+ path.computeBounds(mPathBounds, true);
+ return nClipRect(mRenderer, mPathBounds.left, mPathBounds.top,
+ mPathBounds.right, mPathBounds.bottom, op.nativeInt);
}
@Override
@@ -459,12 +466,18 @@ class GLES20Canvas extends HardwareCanvas {
@Override
public boolean clipRegion(Region region) {
- throw new UnsupportedOperationException();
+ // TODO: Implement
+ region.getBounds(mClipBounds);
+ return nClipRect(mRenderer, mClipBounds.left, mClipBounds.top,
+ mClipBounds.right, mClipBounds.bottom, Region.Op.INTERSECT.nativeInt);
}
@Override
public boolean clipRegion(Region region, Region.Op op) {
- throw new UnsupportedOperationException();
+ // TODO: Implement
+ region.getBounds(mClipBounds);
+ return nClipRect(mRenderer, mClipBounds.left, mClipBounds.top,
+ mClipBounds.right, mClipBounds.bottom, op.nativeInt);
}
@Override
@@ -484,12 +497,14 @@ class GLES20Canvas extends HardwareCanvas {
@Override
public boolean quickReject(Path path, EdgeType type) {
- throw new UnsupportedOperationException();
+ path.computeBounds(mPathBounds, true);
+ return nQuickReject(mRenderer, mPathBounds.left, mPathBounds.top,
+ mPathBounds.right, mPathBounds.bottom, type.nativeInt);
}
@Override
public boolean quickReject(RectF rect, EdgeType type) {
- return quickReject(rect.left, rect.top, rect.right, rect.bottom, type);
+ return nQuickReject(mRenderer, rect.left, rect.top, rect.right, rect.bottom, type.nativeInt);
}
///////////////////////////////////////////////////////////////////////////
@@ -893,17 +908,42 @@ class GLES20Canvas extends HardwareCanvas {
@Override
public void drawPicture(Picture picture) {
- throw new UnsupportedOperationException();
+ if (picture.createdFromStream) {
+ return;
+ }
+
+ picture.endRecording();
+ // TODO: Implement rendering
}
@Override
public void drawPicture(Picture picture, Rect dst) {
- throw new UnsupportedOperationException();
+ if (picture.createdFromStream) {
+ return;
+ }
+
+ save();
+ translate(dst.left, dst.top);
+ if (picture.getWidth() > 0 && picture.getHeight() > 0) {
+ scale(dst.width() / picture.getWidth(), dst.height() / picture.getHeight());
+ }
+ drawPicture(picture);
+ restore();
}
@Override
public void drawPicture(Picture picture, RectF dst) {
- throw new UnsupportedOperationException();
+ if (picture.createdFromStream) {
+ return;
+ }
+
+ save();
+ translate(dst.left, dst.top);
+ if (picture.getWidth() > 0 && picture.getHeight() > 0) {
+ scale(dst.width() / picture.getWidth(), dst.height() / picture.getHeight());
+ }
+ drawPicture(picture);
+ restore();
}
@Override
@@ -930,14 +970,38 @@ class GLES20Canvas extends HardwareCanvas {
@Override
public void drawPosText(char[] text, int index, int count, float[] pos, Paint paint) {
- // TODO: Implement
+ if (index < 0 || index + count > text.length || count * 2 > pos.length) {
+ throw new IndexOutOfBoundsException();
+ }
+
+ int modifiers = setupModifiers(paint);
+ try {
+ nDrawPosText(mRenderer, text, index, count, pos, paint.mNativePaint);
+ } finally {
+ if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
+ }
}
+ private static native void nDrawPosText(int renderer, char[] text, int index, int count,
+ float[] pos, int paint);
+
@Override
public void drawPosText(String text, float[] pos, Paint paint) {
- // TODO: Implement
+ if (text.length() * 2 > pos.length) {
+ throw new ArrayIndexOutOfBoundsException();
+ }
+
+ int modifiers = setupModifiers(paint);
+ try {
+ nDrawPosText(mRenderer, text, 0, text.length(), pos, paint.mNativePaint);
+ } finally {
+ if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
+ }
}
+ private static native void nDrawPosText(int renderer, String text, int start, int end,
+ float[] pos, int paint);
+
@Override
public void drawRect(float left, float top, float right, float bottom, Paint paint) {
int modifiers = setupModifiers(paint);
diff --git a/core/java/android/view/VelocityTracker.java b/core/java/android/view/VelocityTracker.java
index dfb2c32..1c35e31 100644
--- a/core/java/android/view/VelocityTracker.java
+++ b/core/java/android/view/VelocityTracker.java
@@ -29,7 +29,7 @@ import android.util.PoolableManager;
* 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.
+ * and {@link #getYVelocity(int)} to retrieve the velocity for each pointer id.
*/
public final class VelocityTracker implements Poolable<VelocityTracker> {
private static final Pool<VelocityTracker> sPool = Pools.synchronizedPool(
@@ -39,6 +39,7 @@ public final class VelocityTracker implements Poolable<VelocityTracker> {
}
public void onAcquired(VelocityTracker element) {
+ // Intentionally empty
}
public void onReleased(VelocityTracker element) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index e280286..a9d6cdf 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -4107,7 +4107,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
*/
void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
event.setSource(this);
- event.setClassName(getClass().getName());
+ event.setClassName(View.class.getName());
event.setPackageName(getContext().getPackageName());
event.setEnabled(isEnabled());
event.setContentDescription(mContentDescription);
@@ -4212,7 +4212,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
}
info.setPackageName(mContext.getPackageName());
- info.setClassName(getClass().getName());
+ info.setClassName(View.class.getName());
info.setContentDescription(getContentDescription());
info.setEnabled(isEnabled());
@@ -7597,15 +7597,17 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
*/
public void setAlpha(float alpha) {
ensureTransformationInfo();
- mTransformationInfo.mAlpha = alpha;
- invalidateParentCaches();
- if (onSetAlpha((int) (alpha * 255))) {
- mPrivateFlags |= ALPHA_SET;
- // subclass is handling alpha - don't optimize rendering cache invalidation
- invalidate(true);
- } else {
- mPrivateFlags &= ~ALPHA_SET;
- invalidate(false);
+ if (mTransformationInfo.mAlpha != alpha) {
+ mTransformationInfo.mAlpha = alpha;
+ invalidateParentCaches();
+ if (onSetAlpha((int) (alpha * 255))) {
+ mPrivateFlags |= ALPHA_SET;
+ // subclass is handling alpha - don't optimize rendering cache invalidation
+ invalidate(true);
+ } else {
+ mPrivateFlags &= ~ALPHA_SET;
+ invalidate(false);
+ }
}
}
@@ -7616,18 +7618,22 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
* alpha (the return value for onSetAlpha()).
*
* @param alpha The new value for the alpha property
- * @return true if the View subclass handles alpha (the return value for onSetAlpha())
+ * @return true if the View subclass handles alpha (the return value for onSetAlpha()) and
+ * the new value for the alpha property is different from the old value
*/
boolean setAlphaNoInvalidation(float alpha) {
ensureTransformationInfo();
- mTransformationInfo.mAlpha = alpha;
- boolean subclassHandlesAlpha = onSetAlpha((int) (alpha * 255));
- if (subclassHandlesAlpha) {
- mPrivateFlags |= ALPHA_SET;
- } else {
- mPrivateFlags &= ~ALPHA_SET;
+ if (mTransformationInfo.mAlpha != alpha) {
+ mTransformationInfo.mAlpha = alpha;
+ boolean subclassHandlesAlpha = onSetAlpha((int) (alpha * 255));
+ if (subclassHandlesAlpha) {
+ mPrivateFlags |= ALPHA_SET;
+ return true;
+ } else {
+ mPrivateFlags &= ~ALPHA_SET;
+ }
}
- return subclassHandlesAlpha;
+ return false;
}
/**
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 09901ff..5c63366 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -2229,6 +2229,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
@Override
void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfoInternal(info);
+ info.setClassName(ViewGroup.class.getName());
for (int i = 0, count = mChildrenCount; i < count; i++) {
View child = mChildren[i];
if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE
@@ -2238,6 +2239,12 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
}
}
+ @Override
+ void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEventInternal(event);
+ event.setClassName(ViewGroup.class.getName());
+ }
+
/**
* {@inheritDoc}
*/
diff --git a/core/java/android/view/WindowOrientationListener.java b/core/java/android/view/WindowOrientationListener.java
index c3c74a7..c28b220 100755
--- a/core/java/android/view/WindowOrientationListener.java
+++ b/core/java/android/view/WindowOrientationListener.java
@@ -21,6 +21,7 @@ import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
+import android.util.FloatMath;
import android.util.Log;
import android.util.Slog;
@@ -48,6 +49,8 @@ public abstract class WindowOrientationListener {
private static final boolean DEBUG = false;
private static final boolean localLOGV = DEBUG || false;
+ private static final boolean USE_GRAVITY_SENSOR = false;
+
private SensorManager mSensorManager;
private boolean mEnabled;
private int mRate;
@@ -79,7 +82,8 @@ public abstract class WindowOrientationListener {
private WindowOrientationListener(Context context, int rate) {
mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE);
mRate = rate;
- mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
+ mSensor = mSensorManager.getDefaultSensor(USE_GRAVITY_SENSOR
+ ? Sensor.TYPE_GRAVITY : Sensor.TYPE_ACCELEROMETER);
if (mSensor != null) {
// Create listener only if sensors do exist
mSensorEventListener = new SensorEventListenerImpl(this);
@@ -179,7 +183,7 @@ public abstract class WindowOrientationListener {
* cartesian space because the orientation calculations are sensitive to the
* absolute magnitude of the acceleration. In particular, there are singularities
* in the calculation as the magnitude approaches 0. By performing the low-pass
- * filtering early, we can eliminate high-frequency impulses systematically.
+ * filtering early, we can eliminate most spurious high-frequency impulses due to noise.
*
* - Convert the acceleromter vector from cartesian to spherical coordinates.
* Since we're dealing with rotation of the device, this is the sensible coordinate
@@ -204,11 +208,17 @@ public abstract class WindowOrientationListener {
* new orientation proposal.
*
* Details are explained inline.
+ *
+ * See http://en.wikipedia.org/wiki/Low-pass_filter#Discrete-time_realization for
+ * signal processing background.
*/
static final class SensorEventListenerImpl implements SensorEventListener {
// We work with all angles in degrees in this class.
private static final float RADIANS_TO_DEGREES = (float) (180 / Math.PI);
+ // Number of nanoseconds per millisecond.
+ private static final long NANOS_PER_MS = 1000000;
+
// Indices into SensorEvent.values for the accelerometer sensor.
private static final int ACCELEROMETER_DATA_X = 0;
private static final int ACCELEROMETER_DATA_Y = 1;
@@ -216,38 +226,41 @@ public abstract class WindowOrientationListener {
private final WindowOrientationListener mOrientationListener;
- /* State for first order low-pass filtering of accelerometer data.
- * See http://en.wikipedia.org/wiki/Low-pass_filter#Discrete-time_realization for
- * signal processing background.
- */
-
- private long mLastTimestamp = Long.MAX_VALUE; // in nanoseconds
- private float mLastFilteredX, mLastFilteredY, mLastFilteredZ;
-
- // The current proposal. We wait for the proposal to be stable for a
- // certain amount of time before accepting it.
- //
- // The basic idea is to ignore intermediate poses of the device while the
- // user is picking up, putting down or turning the device.
- private int mProposalRotation;
- private long mProposalAgeMS;
-
- // A historical trace of tilt and orientation angles. Used to determine whether
- // the device posture has settled down.
- private static final int HISTORY_SIZE = 20;
- private int mHistoryIndex; // index of most recent sample
- private int mHistoryLength; // length of historical trace
- private final long[] mHistoryTimestampMS = new long[HISTORY_SIZE];
- private final float[] mHistoryMagnitudes = new float[HISTORY_SIZE];
- private final int[] mHistoryTiltAngles = new int[HISTORY_SIZE];
- private final int[] mHistoryOrientationAngles = new int[HISTORY_SIZE];
+ // The minimum amount of time that a predicted rotation must be stable before it
+ // is accepted as a valid rotation proposal. This value can be quite small because
+ // the low-pass filter already suppresses most of the noise so we're really just
+ // looking for quick confirmation that the last few samples are in agreement as to
+ // the desired orientation.
+ private static final long PROPOSAL_SETTLE_TIME_NANOS = 40 * NANOS_PER_MS;
+
+ // The minimum amount of time that must have elapsed since the device last exited
+ // the flat state (time since it was picked up) before the proposed rotation
+ // can change.
+ private static final long PROPOSAL_MIN_TIME_SINCE_FLAT_ENDED_NANOS = 500 * NANOS_PER_MS;
+
+ // The mininum amount of time that must have elapsed since the device stopped
+ // swinging (time since device appeared to be in the process of being put down
+ // or put away into a pocket) before the proposed rotation can change.
+ private static final long PROPOSAL_MIN_TIME_SINCE_SWING_ENDED_NANOS = 300 * NANOS_PER_MS;
+
+ // If the tilt angle remains greater than the specified angle for a minimum of
+ // the specified time, then the device is deemed to be lying flat
+ // (just chillin' on a table).
+ private static final float FLAT_ANGLE = 75;
+ private static final long FLAT_TIME_NANOS = 1000 * NANOS_PER_MS;
+
+ // If the tilt angle has increased by at least delta degrees within the specified amount
+ // of time, then the device is deemed to be swinging away from the user
+ // down towards flat (tilt = 90).
+ private static final float SWING_AWAY_ANGLE_DELTA = 20;
+ private static final long SWING_TIME_NANOS = 300 * NANOS_PER_MS;
// The maximum sample inter-arrival time in milliseconds.
// If the acceleration samples are further apart than this amount in time, we reset the
// state of the low-pass filter and orientation properties. This helps to handle
// boundary conditions when the device is turned on, wakes from suspend or there is
// a significant gap in samples.
- private static final float MAX_FILTER_DELTA_TIME_MS = 1000;
+ private static final long MAX_FILTER_DELTA_TIME_NANOS = 1000 * NANOS_PER_MS;
// The acceleration filter time constant.
//
@@ -267,8 +280,10 @@ public abstract class WindowOrientationListener {
//
// Filtering adds latency proportional the time constant (inversely proportional
// to the cutoff frequency) so we don't want to make the time constant too
- // large or we can lose responsiveness.
- private static final float FILTER_TIME_CONSTANT_MS = 100.0f;
+ // large or we can lose responsiveness. Likewise we don't want to make it too
+ // small or we do a poor job suppressing acceleration spikes.
+ // Empirically, 100ms seems to be too small and 500ms is too large.
+ private static final float FILTER_TIME_CONSTANT_MS = 200.0f;
/* State for orientation detection. */
@@ -286,9 +301,9 @@ public abstract class WindowOrientationListener {
//
// In both cases, we postpone choosing an orientation.
private static final float MIN_ACCELERATION_MAGNITUDE =
- SensorManager.STANDARD_GRAVITY * 0.5f;
+ SensorManager.STANDARD_GRAVITY * 0.3f;
private static final float MAX_ACCELERATION_MAGNITUDE =
- SensorManager.STANDARD_GRAVITY * 1.5f;
+ SensorManager.STANDARD_GRAVITY * 1.25f;
// Maximum absolute tilt angle at which to consider orientation data. Beyond this (i.e.
// when screen is facing the sky or ground), we completely ignore orientation data.
@@ -306,10 +321,10 @@ public abstract class WindowOrientationListener {
// The ideal tilt angle is 0 (when the device is vertical) so the limits establish
// how close to vertical the device must be in order to change orientation.
private static final int[][] TILT_TOLERANCE = new int[][] {
- /* ROTATION_0 */ { -20, 70 },
- /* ROTATION_90 */ { -20, 60 },
- /* ROTATION_180 */ { -20, 50 },
- /* ROTATION_270 */ { -20, 60 }
+ /* ROTATION_0 */ { -25, 70 },
+ /* ROTATION_90 */ { -25, 65 },
+ /* ROTATION_180 */ { -25, 60 },
+ /* ROTATION_270 */ { -25, 65 }
};
// The gap angle in degrees between adjacent orientation angles for hysteresis.
@@ -319,29 +334,38 @@ public abstract class WindowOrientationListener {
// orientation.
private static final int ADJACENT_ORIENTATION_ANGLE_GAP = 45;
- // The number of milliseconds for which the device posture must be stable
- // before we perform an orientation change. If the device appears to be rotating
- // (being picked up, put down) then we keep waiting until it settles.
- private static final int SETTLE_TIME_MS = 200;
+ // Timestamp and value of the last accelerometer sample.
+ private long mLastFilteredTimestampNanos;
+ private float mLastFilteredX, mLastFilteredY, mLastFilteredZ;
+
+ // The last proposed rotation, -1 if unknown.
+ private int mProposedRotation;
+
+ // Value of the current predicted rotation, -1 if unknown.
+ private int mPredictedRotation;
+
+ // Timestamp of when the predicted rotation most recently changed.
+ private long mPredictedRotationTimestampNanos;
- // The maximum change in magnitude that can occur during the settle time.
- // Tuning this constant particularly helps to filter out situations where the
- // device is being picked up or put down by the user.
- private static final float SETTLE_MAGNITUDE_MAX_DELTA =
- SensorManager.STANDARD_GRAVITY * 0.2f;
+ // Timestamp when the device last appeared to be flat for sure (the flat delay elapsed).
+ private long mFlatTimestampNanos;
- // The maximum change in tilt angle that can occur during the settle time.
- private static final int SETTLE_TILT_ANGLE_MAX_DELTA = 5;
+ // Timestamp when the device last appeared to be swinging.
+ private long mSwingTimestampNanos;
- // The maximum change in orientation angle that can occur during the settle time.
- private static final int SETTLE_ORIENTATION_ANGLE_MAX_DELTA = 5;
+ // History of observed tilt angles.
+ private static final int TILT_HISTORY_SIZE = 40;
+ private float[] mTiltHistory = new float[TILT_HISTORY_SIZE];
+ private long[] mTiltHistoryTimestampNanos = new long[TILT_HISTORY_SIZE];
+ private int mTiltHistoryIndex;
public SensorEventListenerImpl(WindowOrientationListener orientationListener) {
mOrientationListener = orientationListener;
+ reset();
}
public int getProposedRotation() {
- return mProposalAgeMS >= SETTLE_TIME_MS ? mProposalRotation : -1;
+ return mProposedRotation;
}
@Override
@@ -359,8 +383,9 @@ public abstract class WindowOrientationListener {
float z = event.values[ACCELEROMETER_DATA_Z];
if (log) {
- Slog.v(TAG, "Raw acceleration vector: " +
- "x=" + x + ", y=" + y + ", z=" + z);
+ Slog.v(TAG, "Raw acceleration vector: "
+ + "x=" + x + ", y=" + y + ", z=" + z
+ + ", magnitude=" + FloatMath.sqrt(x * x + y * y + z * z));
}
// Apply a low-pass filter to the acceleration up vector in cartesian space.
@@ -368,14 +393,16 @@ public abstract class WindowOrientationListener {
// or when we see values of (0, 0, 0) which indicates that we polled the
// accelerometer too soon after turning it on and we don't have any data yet.
final long now = event.timestamp;
- final float timeDeltaMS = (now - mLastTimestamp) * 0.000001f;
- boolean skipSample;
- if (timeDeltaMS <= 0 || timeDeltaMS > MAX_FILTER_DELTA_TIME_MS
+ final long then = mLastFilteredTimestampNanos;
+ final float timeDeltaMS = (now - then) * 0.000001f;
+ final boolean skipSample;
+ if (now < then
+ || now > then + MAX_FILTER_DELTA_TIME_NANOS
|| (x == 0 && y == 0 && z == 0)) {
if (log) {
Slog.v(TAG, "Resetting orientation listener.");
}
- clearProposal();
+ reset();
skipSample = true;
} else {
final float alpha = timeDeltaMS / (FILTER_TIME_CONSTANT_MS + timeDeltaMS);
@@ -383,27 +410,28 @@ public abstract class WindowOrientationListener {
y = alpha * (y - mLastFilteredY) + mLastFilteredY;
z = alpha * (z - mLastFilteredZ) + mLastFilteredZ;
if (log) {
- Slog.v(TAG, "Filtered acceleration vector: " +
- "x=" + x + ", y=" + y + ", z=" + z);
+ Slog.v(TAG, "Filtered acceleration vector: "
+ + "x=" + x + ", y=" + y + ", z=" + z
+ + ", magnitude=" + FloatMath.sqrt(x * x + y * y + z * z));
}
skipSample = false;
}
- mLastTimestamp = now;
+ mLastFilteredTimestampNanos = now;
mLastFilteredX = x;
mLastFilteredY = y;
mLastFilteredZ = z;
- final int oldProposedRotation = getProposedRotation();
+ boolean isFlat = false;
+ boolean isSwinging = false;
if (!skipSample) {
// Calculate the magnitude of the acceleration vector.
- final float magnitude = (float) Math.sqrt(x * x + y * y + z * z);
+ final float magnitude = FloatMath.sqrt(x * x + y * y + z * z);
if (magnitude < MIN_ACCELERATION_MAGNITUDE
|| magnitude > MAX_ACCELERATION_MAGNITUDE) {
if (log) {
- Slog.v(TAG, "Ignoring sensor data, magnitude out of range: "
- + "magnitude=" + magnitude);
+ Slog.v(TAG, "Ignoring sensor data, magnitude out of range.");
}
- clearProposal();
+ clearPredictedRotation();
} else {
// Calculate the tilt angle.
// This is the angle between the up vector and the x-y plane (the plane of
@@ -414,14 +442,25 @@ public abstract class WindowOrientationListener {
final int tiltAngle = (int) Math.round(
Math.asin(z / magnitude) * RADIANS_TO_DEGREES);
+ // Determine whether the device appears to be flat or swinging.
+ if (isFlat(now)) {
+ isFlat = true;
+ mFlatTimestampNanos = now;
+ }
+ if (isSwinging(now, tiltAngle)) {
+ isSwinging = true;
+ mSwingTimestampNanos = now;
+ }
+ addTiltHistoryEntry(now, tiltAngle);
+
// If the tilt angle is too close to horizontal then we cannot determine
// the orientation angle of the screen.
if (Math.abs(tiltAngle) > MAX_TILT) {
if (log) {
Slog.v(TAG, "Ignoring sensor data, tilt angle too high: "
- + "magnitude=" + magnitude + ", tiltAngle=" + tiltAngle);
+ + "tiltAngle=" + tiltAngle);
}
- clearProposal();
+ clearPredictedRotation();
} else {
// Calculate the orientation angle.
// This is the angle between the x-y projection of the up vector onto
@@ -439,89 +478,93 @@ public abstract class WindowOrientationListener {
nearestRotation = 0;
}
- // Determine the proposed orientation.
- // The confidence of the proposal is 1.0 when it is ideal and it
- // decays exponentially as the proposal moves further from the ideal
- // angle, tilt and magnitude of the proposed orientation.
- if (!isTiltAngleAcceptable(nearestRotation, tiltAngle)
- || !isOrientationAngleAcceptable(nearestRotation,
+ // Determine the predicted orientation.
+ if (isTiltAngleAcceptable(nearestRotation, tiltAngle)
+ && isOrientationAngleAcceptable(nearestRotation,
orientationAngle)) {
+ updatePredictedRotation(now, nearestRotation);
if (log) {
- Slog.v(TAG, "Ignoring sensor data, no proposal: "
- + "magnitude=" + magnitude + ", tiltAngle=" + tiltAngle
- + ", orientationAngle=" + orientationAngle);
+ Slog.v(TAG, "Predicted: "
+ + "tiltAngle=" + tiltAngle
+ + ", orientationAngle=" + orientationAngle
+ + ", predictedRotation=" + mPredictedRotation
+ + ", predictedRotationAgeMS="
+ + ((now - mPredictedRotationTimestampNanos)
+ * 0.000001f));
}
- clearProposal();
} else {
if (log) {
- Slog.v(TAG, "Proposal: "
- + "magnitude=" + magnitude
- + ", tiltAngle=" + tiltAngle
- + ", orientationAngle=" + orientationAngle
- + ", proposalRotation=" + mProposalRotation);
+ Slog.v(TAG, "Ignoring sensor data, no predicted rotation: "
+ + "tiltAngle=" + tiltAngle
+ + ", orientationAngle=" + orientationAngle);
}
- updateProposal(nearestRotation, now / 1000000L,
- magnitude, tiltAngle, orientationAngle);
+ clearPredictedRotation();
}
}
}
}
+ // Determine new proposed rotation.
+ final int oldProposedRotation = mProposedRotation;
+ if (mPredictedRotation < 0 || isPredictedRotationAcceptable(now)) {
+ mProposedRotation = mPredictedRotation;
+ }
+
// Write final statistics about where we are in the orientation detection process.
- final int proposedRotation = getProposedRotation();
if (log) {
- final float proposalConfidence = Math.min(
- mProposalAgeMS * 1.0f / SETTLE_TIME_MS, 1.0f);
Slog.v(TAG, "Result: currentRotation=" + mOrientationListener.mCurrentRotation
- + ", proposedRotation=" + proposedRotation
+ + ", proposedRotation=" + mProposedRotation
+ + ", predictedRotation=" + mPredictedRotation
+ ", timeDeltaMS=" + timeDeltaMS
- + ", proposalRotation=" + mProposalRotation
- + ", proposalAgeMS=" + mProposalAgeMS
- + ", proposalConfidence=" + proposalConfidence);
+ + ", isFlat=" + isFlat
+ + ", isSwinging=" + isSwinging
+ + ", timeUntilSettledMS=" + remainingMS(now,
+ mPredictedRotationTimestampNanos + PROPOSAL_SETTLE_TIME_NANOS)
+ + ", timeUntilFlatDelayExpiredMS=" + remainingMS(now,
+ mFlatTimestampNanos + PROPOSAL_MIN_TIME_SINCE_FLAT_ENDED_NANOS)
+ + ", timeUntilSwingDelayExpiredMS=" + remainingMS(now,
+ mSwingTimestampNanos + PROPOSAL_MIN_TIME_SINCE_SWING_ENDED_NANOS));
}
// Tell the listener.
- if (proposedRotation != oldProposedRotation && proposedRotation >= 0) {
+ if (mProposedRotation != oldProposedRotation && mProposedRotation >= 0) {
if (log) {
- Slog.v(TAG, "Proposed rotation changed! proposedRotation=" + proposedRotation
+ Slog.v(TAG, "Proposed rotation changed! proposedRotation=" + mProposedRotation
+ ", oldProposedRotation=" + oldProposedRotation);
}
- mOrientationListener.onProposedRotationChanged(proposedRotation);
+ mOrientationListener.onProposedRotationChanged(mProposedRotation);
}
}
/**
- * Returns true if the tilt angle is acceptable for a proposed
- * orientation transition.
+ * Returns true if the tilt angle is acceptable for a given predicted rotation.
*/
- private boolean isTiltAngleAcceptable(int proposedRotation,
- int tiltAngle) {
- return tiltAngle >= TILT_TOLERANCE[proposedRotation][0]
- && tiltAngle <= TILT_TOLERANCE[proposedRotation][1];
+ private boolean isTiltAngleAcceptable(int rotation, int tiltAngle) {
+ return tiltAngle >= TILT_TOLERANCE[rotation][0]
+ && tiltAngle <= TILT_TOLERANCE[rotation][1];
}
/**
- * Returns true if the orientation angle is acceptable for a proposed
- * orientation transition.
+ * Returns true if the orientation angle is acceptable for a given predicted rotation.
*
* This function takes into account the gap between adjacent orientations
* for hysteresis.
*/
- private boolean isOrientationAngleAcceptable(int proposedRotation, int orientationAngle) {
+ private boolean isOrientationAngleAcceptable(int rotation, int orientationAngle) {
// If there is no current rotation, then there is no gap.
// The gap is used only to introduce hysteresis among advertised orientation
// changes to avoid flapping.
final int currentRotation = mOrientationListener.mCurrentRotation;
if (currentRotation >= 0) {
- // If the proposed rotation is the same or is counter-clockwise adjacent,
- // then we set a lower bound on the orientation angle.
+ // If the specified rotation is the same or is counter-clockwise adjacent
+ // to the current rotation, then we set a lower bound on the orientation angle.
// For example, if currentRotation is ROTATION_0 and proposed is ROTATION_90,
// then we want to check orientationAngle > 45 + GAP / 2.
- if (proposedRotation == currentRotation
- || proposedRotation == (currentRotation + 1) % 4) {
- int lowerBound = proposedRotation * 90 - 45
+ if (rotation == currentRotation
+ || rotation == (currentRotation + 1) % 4) {
+ int lowerBound = rotation * 90 - 45
+ ADJACENT_ORIENTATION_ANGLE_GAP / 2;
- if (proposedRotation == 0) {
+ if (rotation == 0) {
if (orientationAngle >= 315 && orientationAngle < lowerBound + 360) {
return false;
}
@@ -532,15 +575,15 @@ public abstract class WindowOrientationListener {
}
}
- // If the proposed rotation is the same or is clockwise adjacent,
+ // If the specified rotation is the same or is clockwise adjacent,
// then we set an upper bound on the orientation angle.
- // For example, if currentRotation is ROTATION_0 and proposed is ROTATION_270,
+ // For example, if currentRotation is ROTATION_0 and rotation is ROTATION_270,
// then we want to check orientationAngle < 315 - GAP / 2.
- if (proposedRotation == currentRotation
- || proposedRotation == (currentRotation + 3) % 4) {
- int upperBound = proposedRotation * 90 + 45
+ if (rotation == currentRotation
+ || rotation == (currentRotation + 3) % 4) {
+ int upperBound = rotation * 90 + 45
- ADJACENT_ORIENTATION_ANGLE_GAP / 2;
- if (proposedRotation == 0) {
+ if (rotation == 0) {
if (orientationAngle <= 45 && orientationAngle > upperBound) {
return false;
}
@@ -554,58 +597,97 @@ public abstract class WindowOrientationListener {
return true;
}
- private void clearProposal() {
- mProposalRotation = -1;
- mProposalAgeMS = 0;
- }
+ /**
+ * Returns true if the predicted rotation is ready to be advertised as a
+ * proposed rotation.
+ */
+ private boolean isPredictedRotationAcceptable(long now) {
+ // The predicted rotation must have settled long enough.
+ if (now < mPredictedRotationTimestampNanos + PROPOSAL_SETTLE_TIME_NANOS) {
+ return false;
+ }
+
+ // The last flat state (time since picked up) must have been sufficiently long ago.
+ if (now < mFlatTimestampNanos + PROPOSAL_MIN_TIME_SINCE_FLAT_ENDED_NANOS) {
+ return false;
+ }
- private void updateProposal(int rotation, long timestampMS,
- float magnitude, int tiltAngle, int orientationAngle) {
- if (mProposalRotation != rotation) {
- mProposalRotation = rotation;
- mHistoryIndex = 0;
- mHistoryLength = 0;
+ // The last swing state (time since last movement to put down) must have been
+ // sufficiently long ago.
+ if (now < mSwingTimestampNanos + PROPOSAL_MIN_TIME_SINCE_SWING_ENDED_NANOS) {
+ return false;
}
- final int index = mHistoryIndex;
- mHistoryTimestampMS[index] = timestampMS;
- mHistoryMagnitudes[index] = magnitude;
- mHistoryTiltAngles[index] = tiltAngle;
- mHistoryOrientationAngles[index] = orientationAngle;
- mHistoryIndex = (index + 1) % HISTORY_SIZE;
- if (mHistoryLength < HISTORY_SIZE) {
- mHistoryLength += 1;
+ // Looks good!
+ return true;
+ }
+
+ private void reset() {
+ mLastFilteredTimestampNanos = Long.MIN_VALUE;
+ mProposedRotation = -1;
+ mFlatTimestampNanos = Long.MIN_VALUE;
+ mSwingTimestampNanos = Long.MIN_VALUE;
+ clearPredictedRotation();
+ clearTiltHistory();
+ }
+
+ private void clearPredictedRotation() {
+ mPredictedRotation = -1;
+ mPredictedRotationTimestampNanos = Long.MIN_VALUE;
+ }
+
+ private void updatePredictedRotation(long now, int rotation) {
+ if (mPredictedRotation != rotation) {
+ mPredictedRotation = rotation;
+ mPredictedRotationTimestampNanos = now;
}
+ }
- long age = 0;
- for (int i = 1; i < mHistoryLength; i++) {
- final int olderIndex = (index + HISTORY_SIZE - i) % HISTORY_SIZE;
- if (Math.abs(mHistoryMagnitudes[olderIndex] - magnitude)
- > SETTLE_MAGNITUDE_MAX_DELTA) {
+ private void clearTiltHistory() {
+ mTiltHistoryTimestampNanos[0] = Long.MIN_VALUE;
+ mTiltHistoryIndex = 1;
+ }
+
+ private void addTiltHistoryEntry(long now, float tilt) {
+ mTiltHistory[mTiltHistoryIndex] = tilt;
+ mTiltHistoryTimestampNanos[mTiltHistoryIndex] = now;
+ mTiltHistoryIndex = (mTiltHistoryIndex + 1) % TILT_HISTORY_SIZE;
+ mTiltHistoryTimestampNanos[mTiltHistoryIndex] = Long.MIN_VALUE;
+ }
+
+ private boolean isFlat(long now) {
+ for (int i = mTiltHistoryIndex; (i = nextTiltHistoryIndex(i)) >= 0; ) {
+ if (mTiltHistory[i] < FLAT_ANGLE) {
break;
}
- if (angleAbsoluteDelta(mHistoryTiltAngles[olderIndex],
- tiltAngle) > SETTLE_TILT_ANGLE_MAX_DELTA) {
- break;
+ if (mTiltHistoryTimestampNanos[i] + FLAT_TIME_NANOS <= now) {
+ // Tilt has remained greater than FLAT_TILT_ANGLE for FLAT_TIME_NANOS.
+ return true;
}
- if (angleAbsoluteDelta(mHistoryOrientationAngles[olderIndex],
- orientationAngle) > SETTLE_ORIENTATION_ANGLE_MAX_DELTA) {
+ }
+ return false;
+ }
+
+ private boolean isSwinging(long now, float tilt) {
+ for (int i = mTiltHistoryIndex; (i = nextTiltHistoryIndex(i)) >= 0; ) {
+ if (mTiltHistoryTimestampNanos[i] + SWING_TIME_NANOS < now) {
break;
}
- age = timestampMS - mHistoryTimestampMS[olderIndex];
- if (age >= SETTLE_TIME_MS) {
- break;
+ if (mTiltHistory[i] + SWING_AWAY_ANGLE_DELTA <= tilt) {
+ // Tilted away by SWING_AWAY_ANGLE_DELTA within SWING_TIME_NANOS.
+ return true;
}
}
- mProposalAgeMS = age;
+ return false;
}
- private static int angleAbsoluteDelta(int a, int b) {
- int delta = Math.abs(a - b);
- if (delta > 180) {
- delta = 360 - delta;
- }
- return delta;
+ private int nextTiltHistoryIndex(int index) {
+ index = (index == 0 ? TILT_HISTORY_SIZE : index) - 1;
+ return mTiltHistoryTimestampNanos[index] != Long.MIN_VALUE ? index : -1;
+ }
+
+ private static float remainingMS(long now, long until) {
+ return now >= until ? 0 : (until - now) * 0.000001f;
}
}
}
diff --git a/core/java/android/webkit/HTML5VideoFullScreen.java b/core/java/android/webkit/HTML5VideoFullScreen.java
index 21364c1..bc0557e 100644
--- a/core/java/android/webkit/HTML5VideoFullScreen.java
+++ b/core/java/android/webkit/HTML5VideoFullScreen.java
@@ -198,6 +198,10 @@ public class HTML5VideoFullScreen extends HTML5VideoView
// Call into the native to ask for the state, if still in play mode,
// this will trigger the video to play.
mProxy.dispatchOnRestoreState();
+
+ if (getStartWhenPrepared()) {
+ mPlayer.start();
+ }
}
public boolean fullScreenExited() {
diff --git a/core/java/android/webkit/HTML5VideoView.java b/core/java/android/webkit/HTML5VideoView.java
index 1d8bda7..73166cb 100644
--- a/core/java/android/webkit/HTML5VideoView.java
+++ b/core/java/android/webkit/HTML5VideoView.java
@@ -194,20 +194,9 @@ public class HTML5VideoView implements MediaPlayer.OnPreparedListener {
mPlayer.setOnInfoListener(proxy);
}
- // Normally called immediately after setVideoURI. But for full screen,
- // this should be after surface holder created
- public void prepareDataAndDisplayMode(HTML5VideoViewProxy proxy) {
- // SurfaceTexture will be created lazily here for inline mode
- decideDisplayMode();
-
- setOnCompletionListener(proxy);
- setOnPreparedListener(proxy);
- setOnErrorListener(proxy);
- setOnInfoListener(proxy);
- // When there is exception, we could just bail out silently.
- // No Video will be played though. Write the stack for debug
+ public void prepareDataCommon(HTML5VideoViewProxy proxy) {
try {
- mPlayer.setDataSource(mProxy.getContext(), mUri, mHeaders);
+ mPlayer.setDataSource(proxy.getContext(), mUri, mHeaders);
mPlayer.prepareAsync();
} catch (IllegalArgumentException e) {
e.printStackTrace();
@@ -219,6 +208,25 @@ public class HTML5VideoView implements MediaPlayer.OnPreparedListener {
mCurrentState = STATE_NOTPREPARED;
}
+ public void reprepareData(HTML5VideoViewProxy proxy) {
+ mPlayer.reset();
+ prepareDataCommon(proxy);
+ }
+
+ // Normally called immediately after setVideoURI. But for full screen,
+ // this should be after surface holder created
+ public void prepareDataAndDisplayMode(HTML5VideoViewProxy proxy) {
+ // SurfaceTexture will be created lazily here for inline mode
+ decideDisplayMode();
+
+ setOnCompletionListener(proxy);
+ setOnPreparedListener(proxy);
+ setOnErrorListener(proxy);
+ setOnInfoListener(proxy);
+
+ prepareDataCommon(proxy);
+ }
+
// Common code
public int getVideoLayerId() {
@@ -324,4 +332,14 @@ public class HTML5VideoView implements MediaPlayer.OnPreparedListener {
return false;
}
+ private boolean m_startWhenPrepared = false;
+
+ public void setStartWhenPrepared(boolean willPlay) {
+ m_startWhenPrepared = willPlay;
+ }
+
+ public boolean getStartWhenPrepared() {
+ return m_startWhenPrepared;
+ }
+
}
diff --git a/core/java/android/webkit/HTML5VideoViewProxy.java b/core/java/android/webkit/HTML5VideoViewProxy.java
index 7c2dc38..d306c86 100644
--- a/core/java/android/webkit/HTML5VideoViewProxy.java
+++ b/core/java/android/webkit/HTML5VideoViewProxy.java
@@ -162,6 +162,16 @@ class HTML5VideoViewProxy extends Handler
mHTML5VideoView.enterFullScreenVideoState(layerId, proxy, webView);
}
+ public static void exitFullScreenVideo(HTML5VideoViewProxy proxy,
+ WebView webView) {
+ if (!mHTML5VideoView.fullScreenExited() && mHTML5VideoView.isFullScreenMode()) {
+ WebChromeClient client = webView.getWebChromeClient();
+ if (client != null) {
+ client.onHideCustomView();
+ }
+ }
+ }
+
// This is on the UI thread.
// When native tell Java to play, we need to check whether or not it is
// still the same video by using videoLayerId and treat it differently.
@@ -172,6 +182,21 @@ class HTML5VideoViewProxy extends Handler
if (mHTML5VideoView != null) {
currentVideoLayerId = mHTML5VideoView.getVideoLayerId();
backFromFullScreenMode = mHTML5VideoView.fullScreenExited();
+
+ // When playing video back to back in full screen mode,
+ // javascript will switch the src and call play.
+ // In this case, we can just reuse the same full screen view,
+ // and play the video after prepared.
+ if (mHTML5VideoView.isFullScreenMode()
+ && !backFromFullScreenMode
+ && currentVideoLayerId != videoLayerId
+ && mCurrentProxy != proxy) {
+ mCurrentProxy = proxy;
+ mHTML5VideoView.setStartWhenPrepared(true);
+ mHTML5VideoView.setVideoURI(url, proxy);
+ mHTML5VideoView.reprepareData(proxy);
+ return;
+ }
}
if (backFromFullScreenMode
@@ -682,6 +707,10 @@ class HTML5VideoViewProxy extends Handler
VideoPlayer.enterFullScreenVideo(layerId, url, this, mWebView);
}
+ public void exitFullScreenVideo() {
+ VideoPlayer.exitFullScreenVideo(this, mWebView);
+ }
+
/**
* The factory for HTML5VideoViewProxy instances.
* @param webViewCore is the WebViewCore that is requesting the proxy.
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index ab4665a..69c15a6 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -327,6 +327,15 @@ import static javax.microedition.khronos.egl.EGL10.EGL_DEFAULT_DISPLAY;
* property to {@code device-dpi}. This stops Android from performing scaling in your web page and
* allows you to make the necessary adjustments for each density via CSS and JavaScript.</p>
*
+ * <h3>HTML5 Video support</h3>
+ *
+ * <p>In order to support inline HTML5 video in your application, you need to have hardware
+ * acceleration turned on, and set a {@link android.webkit.WebChromeClient}. For full screen support,
+ * implementations of {@link WebChromeClient#onShowCustomView(View, WebChromeClient.CustomViewCallback)}
+ * and {@link WebChromeClient#onHideCustomView()} are required,
+ * {@link WebChromeClient#getVideoLoadingProgressView()} is optional.
+ * </p>
+ *
*
*/
@Widget
@@ -413,7 +422,7 @@ public class WebView extends AbsoluteLayout
private final Rect mViewRectViewport = new Rect();
private final RectF mVisibleContentRect = new RectF();
private boolean mGLViewportEmpty = false;
- WebViewInputConnection mInputConnection = new WebViewInputConnection();
+ WebViewInputConnection mInputConnection = null;
/**
@@ -611,6 +620,7 @@ public class WebView extends AbsoluteLayout
private boolean mIsPaused;
private HitTestResult mInitialHitTestResult;
+ private WebKitHitTest mFocusedNode;
/**
* Customizable constant
@@ -702,13 +712,11 @@ public class WebView extends AbsoluteLayout
static boolean sDisableNavcache = false;
// the color used to highlight the touch rectangles
- private static final int HIGHLIGHT_COLOR = 0x6633b5e5;
- // the round corner for the highlight path
- private static final float TOUCH_HIGHLIGHT_ARC = 5.0f;
+ static final int HIGHLIGHT_COLOR = 0x6633b5e5;
// the region indicating where the user touched on the screen
private Region mTouchHighlightRegion = new Region();
// the paint for the touch highlight
- private Paint mTouchHightlightPaint;
+ private Paint mTouchHightlightPaint = new Paint();
// debug only
private static final boolean DEBUG_TOUCH_HIGHLIGHT = true;
private static final int TOUCH_HIGHLIGHT_ELAPSE_TIME = 2000;
@@ -787,6 +795,7 @@ public class WebView extends AbsoluteLayout
static final int ENTER_FULLSCREEN_VIDEO = 137;
static final int UPDATE_SELECTION = 138;
static final int UPDATE_ZOOM_DENSITY = 139;
+ static final int EXIT_FULLSCREEN_VIDEO = 140;
private static final int FIRST_PACKAGE_MSG_ID = SCROLL_TO_MSG_ID;
private static final int LAST_PACKAGE_MSG_ID = HIT_TEST_RESULT;
@@ -1073,6 +1082,15 @@ public class WebView extends AbsoluteLayout
}
/**
+ * Refer to {@link WebView#requestFocusNodeHref(Message)} for more information
+ */
+ static class FocusNodeHref {
+ static final String TITLE = "title";
+ static final String URL = "url";
+ static final String SRC = "src";
+ }
+
+ /**
* Construct a new WebView with a Context object.
* @param context A Context object used to access application assets.
*/
@@ -2704,6 +2722,14 @@ public class WebView extends AbsoluteLayout
}
int contentX = viewToContentX(mLastTouchX + mScrollX);
int contentY = viewToContentY(mLastTouchY + mScrollY);
+ if (mFocusedNode != null && mFocusedNode.mHitTestX == contentX
+ && mFocusedNode.mHitTestY == contentY) {
+ hrefMsg.getData().putString(FocusNodeHref.URL, mFocusedNode.mLinkUrl);
+ hrefMsg.getData().putString(FocusNodeHref.TITLE, mFocusedNode.mAnchorText);
+ hrefMsg.getData().putString(FocusNodeHref.SRC, mFocusedNode.mImageUrl);
+ hrefMsg.sendToTarget();
+ return;
+ }
if (nativeHasCursorNode()) {
Rect cursorBounds = nativeGetCursorRingBounds();
if (!cursorBounds.contains(contentX, contentY)) {
@@ -4402,10 +4428,6 @@ public class WebView extends AbsoluteLayout
Rect r = mTouchHighlightRegion.getBounds();
postInvalidateDelayed(delay, r.left, r.top, r.right, r.bottom);
} else {
- if (mTouchHightlightPaint == null) {
- mTouchHightlightPaint = new Paint();
- mTouchHightlightPaint.setColor(HIGHLIGHT_COLOR);
- }
RegionIterator iter = new RegionIterator(mTouchHighlightRegion);
Rect r = new Rect();
while (iter.next(r)) {
@@ -4920,15 +4942,13 @@ public class WebView extends AbsoluteLayout
}
@Override
- public boolean onCheckIsTextEditor() {
- return true;
- }
-
- @Override
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_FULLSCREEN
| EditorInfo.TYPE_CLASS_TEXT
| EditorInfo.TYPE_TEXT_VARIATION_NORMAL;
+ if (mInputConnection == null) {
+ mInputConnection = new WebViewInputConnection();
+ }
return mInputConnection;
}
@@ -8745,6 +8765,12 @@ public class WebView extends AbsoluteLayout
}
break;
+ case EXIT_FULLSCREEN_VIDEO:
+ if (mHTML5VideoViewProxy != null) {
+ mHTML5VideoViewProxy.exitFullScreenVideo();
+ }
+ break;
+
case SHOW_FULLSCREEN: {
View view = (View) msg.obj;
int orientation = msg.arg1;
@@ -8839,13 +8865,25 @@ public class WebView extends AbsoluteLayout
case HIT_TEST_RESULT:
WebKitHitTest hit = (WebKitHitTest) msg.obj;
- setTouchHighlightRects(hit != null ? hit.mTouchRects : null);
+ mFocusedNode = hit;
+ setTouchHighlightRects(hit);
if (hit == null) {
mInitialHitTestResult = null;
} else {
mInitialHitTestResult = new HitTestResult();
- mInitialHitTestResult.mType = hit.mType;
- mInitialHitTestResult.mExtra = hit.mExtra;
+ if (hit.mLinkUrl != null) {
+ mInitialHitTestResult.mType = HitTestResult.SRC_ANCHOR_TYPE;
+ mInitialHitTestResult.mExtra = hit.mLinkUrl;
+ if (hit.mImageUrl != null) {
+ mInitialHitTestResult.mType = HitTestResult.SRC_IMAGE_ANCHOR_TYPE;
+ mInitialHitTestResult.mExtra = hit.mImageUrl;
+ }
+ } else if (hit.mImageUrl != null) {
+ mInitialHitTestResult.mType = HitTestResult.IMAGE_TYPE;
+ mInitialHitTestResult.mExtra = hit.mImageUrl;
+ } else if (hit.mEditable) {
+ mInitialHitTestResult.mType = HitTestResult.EDIT_TEXT_TYPE;
+ }
}
break;
@@ -8883,10 +8921,14 @@ public class WebView extends AbsoluteLayout
}
}
- private void setTouchHighlightRects(Rect[] rects) {
- invalidate(mTouchHighlightRegion.getBounds());
- mTouchHighlightRegion.setEmpty();
+ private void setTouchHighlightRects(WebKitHitTest hit) {
+ Rect[] rects = hit != null ? hit.mTouchRects : null;
+ if (!mTouchHighlightRegion.isEmpty()) {
+ invalidate(mTouchHighlightRegion.getBounds());
+ mTouchHighlightRegion.setEmpty();
+ }
if (rects != null) {
+ mTouchHightlightPaint.setColor(hit.mTapHighlightColor);
for (Rect rect : rects) {
Rect viewRect = contentToViewRect(rect);
// some sites, like stories in nytimes.com, set
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index 962a8f1..c4981e1 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -26,6 +26,7 @@ import android.graphics.Region;
import android.media.MediaFile;
import android.net.ProxyProperties;
import android.net.Uri;
+import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -37,6 +38,7 @@ import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.SurfaceView;
import android.view.View;
+import android.webkit.WebView.FocusNodeHref;
import junit.framework.Assert;
@@ -502,6 +504,17 @@ public final class WebViewCore {
}
/**
+ * Notify the webview that we want to exit the video fullscreen.
+ * This is called through JNI by webcore.
+ */
+ protected void exitFullscreenVideo() {
+ if (mWebView == null) return;
+ Message message = Message.obtain(mWebView.mPrivateHandler,
+ WebView.EXIT_FULLSCREEN_VIDEO);
+ message.sendToTarget();
+ }
+
+ /**
* Clear the picture set. To be called only on the WebCore thread.
*/
/* package */ void clearContent() {
@@ -861,9 +874,20 @@ public final class WebViewCore {
}
static class WebKitHitTest {
- int mType;
- String mExtra;
+ String mLinkUrl;
+ String mAnchorText;
+ String mImageUrl;
+ String mAltDisplayString;
+ String mTitle;
Rect[] mTouchRects;
+ boolean mEditable;
+ int mTapHighlightColor = WebView.HIGHLIGHT_COLOR;
+
+ // These are the input values that produced this hit test
+ int mHitTestX;
+ int mHitTestY;
+ int mHitTestSlop;
+ boolean mHitTestMovedMouse;
}
static class AutoFillData {
@@ -1514,13 +1538,12 @@ public final class WebViewCore {
break;
case REQUEST_CURSOR_HREF: {
+ WebKitHitTest hit = performHitTest(msg.arg1, msg.arg2, 1, false);
Message hrefMsg = (Message) msg.obj;
- hrefMsg.getData().putString("url",
- nativeRetrieveHref(mNativeClass, msg.arg1, msg.arg2));
- hrefMsg.getData().putString("title",
- nativeRetrieveAnchorText(mNativeClass, msg.arg1, msg.arg2));
- hrefMsg.getData().putString("src",
- nativeRetrieveImageSource(mNativeClass, msg.arg1, msg.arg2));
+ Bundle data = hrefMsg.getData();
+ data.putString(FocusNodeHref.URL,hit.mLinkUrl);
+ data.putString(FocusNodeHref.TITLE, hit.mAnchorText);
+ data.putString(FocusNodeHref.SRC, hit.mImageUrl);
hrefMsg.sendToTarget();
break;
}
@@ -1685,8 +1708,7 @@ public final class WebViewCore {
nativeScrollLayer(mNativeClass,
d.mNativeLayer, d.mNativeLayerRect);
}
- WebKitHitTest hit = nativeHitTest(mNativeClass,
- d.mX, d.mY, d.mSlop);
+ WebKitHitTest hit = performHitTest(d.mX, d.mY, d.mSlop, true);
mWebView.mPrivateHandler.obtainMessage(
WebView.HIT_TEST_RESULT, hit)
.sendToTarget();
@@ -1890,6 +1912,15 @@ public final class WebViewCore {
// WebViewCore private methods
//-------------------------------------------------------------------------
+ private WebKitHitTest performHitTest(int x, int y, int slop, boolean moveMouse) {
+ WebKitHitTest hit = nativeHitTest(mNativeClass, x, y, slop, moveMouse);
+ hit.mHitTestX = x;
+ hit.mHitTestY = y;
+ hit.mHitTestSlop = slop;
+ hit.mHitTestMovedMouse = moveMouse;
+ return hit;
+ }
+
private void clearCache(boolean includeDiskFiles) {
mBrowserFrame.clearCache();
if (includeDiskFiles) {
@@ -2940,7 +2971,8 @@ public final class WebViewCore {
private native boolean nativeValidNodeAndBounds(int nativeClass, int frame,
int node, Rect bounds);
- private native WebKitHitTest nativeHitTest(int nativeClass, int x, int y, int slop);
+ private native WebKitHitTest nativeHitTest(int nativeClass, int x, int y,
+ int slop, boolean moveMouse);
private native void nativeAutoFillForm(int nativeClass, int queryId);
private native void nativeScrollLayer(int nativeClass, int layer, Rect rect);
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 38bb2e1..e94b1cb 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -20,7 +20,6 @@ import com.android.internal.R;
import android.content.Context;
import android.content.Intent;
-import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
@@ -1297,6 +1296,18 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
super.sendAccessibilityEvent(eventType);
}
+ @Override
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(event);
+ event.setClassName(AbsListView.class.getName());
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+ info.setClassName(AbsListView.class.getName());
+ }
+
/**
* Indicates whether the children's drawing cache is used during a scroll.
* By default, the drawing cache is enabled but this will consume more memory.
@@ -5572,6 +5583,16 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
}
/**
+ * Hints the RemoteViewsAdapter, if it exists, about which views are currently
+ * being displayed by the AbsListView.
+ */
+ void setVisibleRangeHint(int start, int end) {
+ if (mRemoteAdapter != null) {
+ mRemoteAdapter.setVisibleRangeHint(start, end);
+ }
+ }
+
+ /**
* Sets the recycler listener to be notified whenever a View is set aside in
* the recycler for later reuse. This listener can be used to free resources
* associated to the View.
diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java
index bdaf89e..e36afa3 100644
--- a/core/java/android/widget/AbsSeekBar.java
+++ b/core/java/android/widget/AbsSeekBar.java
@@ -25,6 +25,8 @@ import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
public abstract class AbsSeekBar extends ProgressBar {
private Drawable mThumb;
@@ -464,4 +466,15 @@ public abstract class AbsSeekBar extends ProgressBar {
return super.onKeyDown(keyCode, event);
}
+ @Override
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(event);
+ event.setClassName(AbsSeekBar.class.getName());
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+ info.setClassName(AbsSeekBar.class.getName());
+ }
}
diff --git a/core/java/android/widget/AbsSpinner.java b/core/java/android/widget/AbsSpinner.java
index 3d79205..efdfae3 100644
--- a/core/java/android/widget/AbsSpinner.java
+++ b/core/java/android/widget/AbsSpinner.java
@@ -28,6 +28,8 @@ import android.util.AttributeSet;
import android.util.SparseArray;
import android.view.View;
import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
/**
* An abstract base class for spinner widgets. SDK users will probably not
@@ -40,7 +42,6 @@ public abstract class AbsSpinner extends AdapterView<SpinnerAdapter> {
int mHeightMeasureSpec;
int mWidthMeasureSpec;
- boolean mBlockLayoutRequests;
int mSelectionLeftPadding = 0;
int mSelectionTopPadding = 0;
@@ -463,4 +464,16 @@ public abstract class AbsSpinner extends AdapterView<SpinnerAdapter> {
scrapHeap.clear();
}
}
+
+ @Override
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(event);
+ event.setClassName(AbsSpinner.class.getName());
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+ info.setClassName(AbsSpinner.class.getName());
+ }
}
diff --git a/core/java/android/widget/AdapterView.java b/core/java/android/widget/AdapterView.java
index 40df168..97a864c 100644
--- a/core/java/android/widget/AdapterView.java
+++ b/core/java/android/widget/AdapterView.java
@@ -913,6 +913,7 @@ public abstract class AdapterView<T extends Adapter> extends ViewGroup {
@Override
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(info);
+ info.setClassName(AdapterView.class.getName());
info.setScrollable(isScrollableForAccessibility());
View selectedView = getSelectedView();
if (selectedView != null) {
@@ -923,6 +924,7 @@ public abstract class AdapterView<T extends Adapter> extends ViewGroup {
@Override
public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
super.onInitializeAccessibilityEvent(event);
+ event.setClassName(AdapterView.class.getName());
event.setScrollable(isScrollableForAccessibility());
View selectedView = getSelectedView();
if (selectedView != null) {
diff --git a/core/java/android/widget/AdapterViewAnimator.java b/core/java/android/widget/AdapterViewAnimator.java
index c83c780..e226d37 100644
--- a/core/java/android/widget/AdapterViewAnimator.java
+++ b/core/java/android/widget/AdapterViewAnimator.java
@@ -29,6 +29,8 @@ import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
import java.util.ArrayList;
import java.util.HashMap;
@@ -555,6 +557,9 @@ public abstract class AdapterViewAnimator extends AdapterView<Adapter>
mCurrentWindowStart = newWindowStart;
mCurrentWindowEnd = newWindowEnd;
mCurrentWindowStartUnbounded = newWindowStartUnbounded;
+ if (mRemoteViewsAdapter != null) {
+ mRemoteViewsAdapter.setVisibleRangeHint(mCurrentWindowStart, mCurrentWindowEnd);
+ }
}
requestLayout();
invalidate();
@@ -1045,4 +1050,16 @@ public abstract class AdapterViewAnimator extends AdapterView<Adapter>
*/
public void fyiWillBeAdvancedByHostKThx() {
}
+
+ @Override
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(event);
+ event.setClassName(AdapterViewAnimator.class.getName());
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+ info.setClassName(AdapterViewAnimator.class.getName());
+ }
}
diff --git a/core/java/android/widget/AdapterViewFlipper.java b/core/java/android/widget/AdapterViewFlipper.java
index 4419886..5096227 100644
--- a/core/java/android/widget/AdapterViewFlipper.java
+++ b/core/java/android/widget/AdapterViewFlipper.java
@@ -16,7 +16,6 @@
package android.widget;
-import android.animation.ObjectAnimator;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -27,7 +26,8 @@ import android.os.Message;
import android.util.AttributeSet;
import android.util.Log;
import android.view.RemotableViewMethod;
-import android.view.animation.AlphaAnimation;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.RemoteViews.RemoteView;
/**
@@ -268,4 +268,16 @@ public class AdapterViewFlipper extends AdapterViewAnimator {
mAdvancedByHost = true;
updateRunning(false);
}
+
+ @Override
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(event);
+ event.setClassName(AdapterViewFlipper.class.getName());
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+ info.setClassName(AdapterViewFlipper.class.getName());
+ }
}
diff --git a/core/java/android/widget/Button.java b/core/java/android/widget/Button.java
index 8d58a6d..99f4cae 100644
--- a/core/java/android/widget/Button.java
+++ b/core/java/android/widget/Button.java
@@ -18,9 +18,8 @@ package android.widget;
import android.content.Context;
import android.util.AttributeSet;
-import android.util.Log;
-import android.view.MotionEvent;
-import android.view.KeyEvent;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.RemoteViews.RemoteView;
@@ -107,4 +106,16 @@ public class Button extends TextView {
public Button(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
+
+ @Override
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(event);
+ event.setClassName(Button.class.getName());
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+ info.setClassName(Button.class.getName());
+ }
}
diff --git a/core/java/android/widget/CalendarView.java b/core/java/android/widget/CalendarView.java
index e0403ff..85252af 100644
--- a/core/java/android/widget/CalendarView.java
+++ b/core/java/android/widget/CalendarView.java
@@ -39,6 +39,8 @@ import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.AbsListView.OnScrollListener;
import com.android.internal.R;
@@ -431,6 +433,18 @@ public class CalendarView extends FrameLayout {
setCurrentLocale(newConfig.locale);
}
+ @Override
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(event);
+ event.setClassName(CalendarView.class.getName());
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+ info.setClassName(CalendarView.class.getName());
+ }
+
/**
* Gets the minimal date supported by this {@link CalendarView} in milliseconds
* since January 1, 1970 00:00:00 in {@link TimeZone#getDefault()} time
diff --git a/core/java/android/widget/CheckBox.java b/core/java/android/widget/CheckBox.java
index 2788846..0685eea 100644
--- a/core/java/android/widget/CheckBox.java
+++ b/core/java/android/widget/CheckBox.java
@@ -19,6 +19,7 @@ package android.widget;
import android.content.Context;
import android.util.AttributeSet;
import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
import com.android.internal.R;
@@ -78,4 +79,16 @@ public class CheckBox extends CompoundButton {
event.getText().add(mContext.getString(R.string.checkbox_not_checked));
}
}
+
+ @Override
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(event);
+ event.setClassName(CheckBox.class.getName());
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+ info.setClassName(CheckBox.class.getName());
+ }
}
diff --git a/core/java/android/widget/CheckedTextView.java b/core/java/android/widget/CheckedTextView.java
index 0a54743..5c7e5a3 100644
--- a/core/java/android/widget/CheckedTextView.java
+++ b/core/java/android/widget/CheckedTextView.java
@@ -220,6 +220,7 @@ public class CheckedTextView extends TextView implements Checkable {
@Override
public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
super.onInitializeAccessibilityEvent(event);
+ event.setClassName(CheckedTextView.class.getName());
event.setChecked(mChecked);
}
@@ -236,6 +237,7 @@ public class CheckedTextView extends TextView implements Checkable {
@Override
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(info);
+ info.setClassName(CheckedTextView.class.getName());
info.setChecked(mChecked);
}
}
diff --git a/core/java/android/widget/Chronometer.java b/core/java/android/widget/Chronometer.java
index 7e66722..0370049 100644
--- a/core/java/android/widget/Chronometer.java
+++ b/core/java/android/widget/Chronometer.java
@@ -25,6 +25,8 @@ import android.os.SystemClock;
import android.text.format.DateUtils;
import android.util.AttributeSet;
import android.util.Log;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.RemoteViews.RemoteView;
import java.util.Formatter;
@@ -276,4 +278,16 @@ public class Chronometer extends TextView {
mOnChronometerTickListener.onChronometerTick(this);
}
}
+
+ @Override
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(event);
+ event.setClassName(Chronometer.class.getName());
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+ info.setClassName(Chronometer.class.getName());
+ }
}
diff --git a/core/java/android/widget/CompoundButton.java b/core/java/android/widget/CompoundButton.java
index d3cdad8..02c4c4f 100644
--- a/core/java/android/widget/CompoundButton.java
+++ b/core/java/android/widget/CompoundButton.java
@@ -211,12 +211,14 @@ public abstract class CompoundButton extends Button implements Checkable {
@Override
public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
super.onInitializeAccessibilityEvent(event);
+ event.setClassName(CompoundButton.class.getName());
event.setChecked(mChecked);
}
@Override
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(info);
+ info.setClassName(CompoundButton.class.getName());
info.setCheckable(true);
info.setChecked(mChecked);
}
diff --git a/core/java/android/widget/DatePicker.java b/core/java/android/widget/DatePicker.java
index 0f462ff..110c8f3 100644
--- a/core/java/android/widget/DatePicker.java
+++ b/core/java/android/widget/DatePicker.java
@@ -31,6 +31,7 @@ import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityNodeInfo;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.NumberPicker.OnValueChangeListener;
@@ -391,6 +392,18 @@ public class DatePicker extends FrameLayout {
}
@Override
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(event);
+ event.setClassName(DatePicker.class.getName());
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+ info.setClassName(DatePicker.class.getName());
+ }
+
+ @Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
setCurrentLocale(newConfig.locale);
diff --git a/core/java/android/widget/DigitalClock.java b/core/java/android/widget/DigitalClock.java
index 379883a..add9d9b 100644
--- a/core/java/android/widget/DigitalClock.java
+++ b/core/java/android/widget/DigitalClock.java
@@ -24,6 +24,8 @@ import android.os.SystemClock;
import android.provider.Settings;
import android.text.format.DateFormat;
import android.util.AttributeSet;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
import java.util.Calendar;
@@ -126,4 +128,16 @@ public class DigitalClock extends TextView {
setFormat();
}
}
+
+ @Override
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(event);
+ event.setClassName(DigitalClock.class.getName());
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+ info.setClassName(DigitalClock.class.getName());
+ }
}
diff --git a/core/java/android/widget/EditText.java b/core/java/android/widget/EditText.java
index 0da68a4..2fd8768 100644
--- a/core/java/android/widget/EditText.java
+++ b/core/java/android/widget/EditText.java
@@ -24,6 +24,8 @@ import android.text.TextUtils;
import android.text.method.ArrowKeyMovementMethod;
import android.text.method.MovementMethod;
import android.util.AttributeSet;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
/*
@@ -114,4 +116,16 @@ public class EditText extends TextView {
}
super.setEllipsize(ellipsis);
}
+
+ @Override
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(event);
+ event.setClassName(EditText.class.getName());
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+ info.setClassName(EditText.class.getName());
+ }
}
diff --git a/core/java/android/widget/ExpandableListView.java b/core/java/android/widget/ExpandableListView.java
index ead9b4f..badfaa7 100644
--- a/core/java/android/widget/ExpandableListView.java
+++ b/core/java/android/widget/ExpandableListView.java
@@ -30,6 +30,8 @@ import android.view.ContextMenu;
import android.view.SoundEffectConstants;
import android.view.View;
import android.view.ContextMenu.ContextMenuInfo;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.ExpandableListConnector.PositionMetadata;
import java.util.ArrayList;
@@ -1167,4 +1169,15 @@ public class ExpandableListView extends ListView {
}
}
+ @Override
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(event);
+ event.setClassName(ExpandableListView.class.getName());
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+ info.setClassName(ExpandableListView.class.getName());
+ }
}
diff --git a/core/java/android/widget/FrameLayout.java b/core/java/android/widget/FrameLayout.java
index 74a57b0..da98884 100644
--- a/core/java/android/widget/FrameLayout.java
+++ b/core/java/android/widget/FrameLayout.java
@@ -29,6 +29,8 @@ import android.view.Gravity;
import android.view.View;
import android.view.ViewDebug;
import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.RemoteViews.RemoteView;
@@ -555,6 +557,19 @@ public class FrameLayout extends ViewGroup {
return new LayoutParams(p);
}
+
+ @Override
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(event);
+ event.setClassName(FrameLayout.class.getName());
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+ info.setClassName(FrameLayout.class.getName());
+ }
+
/**
* Per-child layout information for layouts that support margins.
* See {@link android.R.styleable#FrameLayout_Layout FrameLayout Layout Attributes}
diff --git a/core/java/android/widget/Gallery.java b/core/java/android/widget/Gallery.java
index 5e37fa8..03fdc39 100644
--- a/core/java/android/widget/Gallery.java
+++ b/core/java/android/widget/Gallery.java
@@ -32,6 +32,8 @@ import android.view.SoundEffectConstants;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
import android.view.animation.Transformation;
import com.android.internal.R;
@@ -1355,6 +1357,18 @@ public class Gallery extends AbsSpinner implements GestureDetector.OnGestureList
}
+ @Override
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(event);
+ event.setClassName(Gallery.class.getName());
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+ info.setClassName(Gallery.class.getName());
+ }
+
/**
* Responsible for fling behavior. Use {@link #startUsingVelocity(int)} to
* initiate a fling. Each frame of the fling is handled in {@link #run()}.
diff --git a/core/java/android/widget/GridLayout.java b/core/java/android/widget/GridLayout.java
index 7cf5168..7d58011 100644
--- a/core/java/android/widget/GridLayout.java
+++ b/core/java/android/widget/GridLayout.java
@@ -27,6 +27,9 @@ import android.util.Pair;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
+
import com.android.internal.R;
import java.lang.reflect.Array;
@@ -1041,6 +1044,18 @@ public class GridLayout extends ViewGroup {
}
}
+ @Override
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(event);
+ event.setClassName(GridLayout.class.getName());
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+ info.setClassName(GridLayout.class.getName());
+ }
+
// Inner classes
/*
diff --git a/core/java/android/widget/GridView.java b/core/java/android/widget/GridView.java
index 5d406de..be2df8e 100644
--- a/core/java/android/widget/GridView.java
+++ b/core/java/android/widget/GridView.java
@@ -27,6 +27,8 @@ import android.view.SoundEffectConstants;
import android.view.View;
import android.view.ViewDebug;
import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
import android.view.animation.GridLayoutAnimationController;
import android.widget.RemoteViews.RemoteView;
@@ -290,6 +292,7 @@ public class GridView extends AbsListView {
pos += mNumColumns;
}
+ setVisibleRangeHint(mFirstPosition, mFirstPosition + getChildCount() - 1);
return selectedView;
}
@@ -382,6 +385,7 @@ public class GridView extends AbsListView {
mFirstPosition = Math.max(0, pos + 1);
}
+ setVisibleRangeHint(mFirstPosition, mFirstPosition + getChildCount() - 1);
return selectedView;
}
@@ -2116,5 +2120,16 @@ public class GridView extends AbsListView {
}
return result;
}
-}
+ @Override
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(event);
+ event.setClassName(GridView.class.getName());
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+ info.setClassName(GridView.class.getName());
+ }
+}
diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java
index 1683d20..0b4ebf4 100644
--- a/core/java/android/widget/HorizontalScrollView.java
+++ b/core/java/android/widget/HorizontalScrollView.java
@@ -721,12 +721,14 @@ public class HorizontalScrollView extends FrameLayout {
@Override
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(info);
+ info.setClassName(HorizontalScrollView.class.getName());
info.setScrollable(getScrollRange() > 0);
}
@Override
public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
super.onInitializeAccessibilityEvent(event);
+ event.setClassName(HorizontalScrollView.class.getName());
event.setScrollable(getScrollRange() > 0);
event.setScrollX(mScrollX);
event.setScrollY(mScrollY);
diff --git a/core/java/android/widget/ImageButton.java b/core/java/android/widget/ImageButton.java
index d680fad..59a8f28 100644
--- a/core/java/android/widget/ImageButton.java
+++ b/core/java/android/widget/ImageButton.java
@@ -21,6 +21,8 @@ import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.view.MotionEvent;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.RemoteViews.RemoteView;
import java.util.Map;
@@ -90,4 +92,16 @@ public class ImageButton extends ImageView {
protected boolean onSetAlpha(int alpha) {
return false;
}
+
+ @Override
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(event);
+ event.setClassName(ImageButton.class.getName());
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+ info.setClassName(ImageButton.class.getName());
+ }
}
diff --git a/core/java/android/widget/ImageSwitcher.java b/core/java/android/widget/ImageSwitcher.java
index bcb750a..c048970 100644
--- a/core/java/android/widget/ImageSwitcher.java
+++ b/core/java/android/widget/ImageSwitcher.java
@@ -16,12 +16,12 @@
package android.widget;
-import java.util.Map;
-
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.util.AttributeSet;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
public class ImageSwitcher extends ViewSwitcher
@@ -55,5 +55,16 @@ public class ImageSwitcher extends ViewSwitcher
image.setImageDrawable(drawable);
showNext();
}
-}
+ @Override
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(event);
+ event.setClassName(ImageSwitcher.class.getName());
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+ info.setClassName(ImageSwitcher.class.getName());
+ }
+}
diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java
index 73e1273..07ae93b 100644
--- a/core/java/android/widget/ImageView.java
+++ b/core/java/android/widget/ImageView.java
@@ -37,6 +37,7 @@ import android.view.RemotableViewMethod;
import android.view.View;
import android.view.ViewDebug;
import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.RemoteViews.RemoteView;
/**
@@ -1060,4 +1061,16 @@ public class ImageView extends View {
mDrawable.setVisible(false, false);
}
}
+
+ @Override
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(event);
+ event.setClassName(ImageView.class.getName());
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+ info.setClassName(ImageView.class.getName());
+ }
}
diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java
index 427fd3e..b5deec7 100644
--- a/core/java/android/widget/LinearLayout.java
+++ b/core/java/android/widget/LinearLayout.java
@@ -27,6 +27,8 @@ import android.view.Gravity;
import android.view.View;
import android.view.ViewDebug;
import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.RemoteViews.RemoteView;
@@ -1729,7 +1731,19 @@ public class LinearLayout extends ViewGroup {
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
return p instanceof LinearLayout.LayoutParams;
}
-
+
+ @Override
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(event);
+ event.setClassName(LinearLayout.class.getName());
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+ info.setClassName(LinearLayout.class.getName());
+ }
+
/**
* Per-child layout information associated with ViewLinearLayout.
*
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index 7f7a3a7..e20d12a 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -32,13 +32,13 @@ import android.util.AttributeSet;
import android.util.SparseBooleanArray;
import android.view.FocusFinder;
import android.view.KeyEvent;
-import android.view.MotionEvent;
import android.view.SoundEffectConstants;
import android.view.View;
import android.view.ViewDebug;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.RemoteViews.RemoteView;
import java.util.ArrayList;
@@ -678,6 +678,7 @@ public class ListView extends AbsListView {
pos++;
}
+ setVisibleRangeHint(mFirstPosition, mFirstPosition + getChildCount() - 1);
return selectedView;
}
@@ -711,7 +712,7 @@ public class ListView extends AbsListView {
}
mFirstPosition = pos + 1;
-
+ setVisibleRangeHint(mFirstPosition, mFirstPosition + getChildCount() - 1);
return selectedView;
}
@@ -3609,4 +3610,16 @@ public class ListView extends AbsListView {
}
return new long[0];
}
+
+ @Override
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(event);
+ event.setClassName(ListView.class.getName());
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+ info.setClassName(ListView.class.getName());
+ }
}
diff --git a/core/java/android/widget/MediaController.java b/core/java/android/widget/MediaController.java
index f2ea3fc..fc35f05 100644
--- a/core/java/android/widget/MediaController.java
+++ b/core/java/android/widget/MediaController.java
@@ -31,6 +31,8 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.SeekBar.OnSeekBarChangeListener;
import com.android.internal.policy.PolicyManager;
@@ -592,6 +594,18 @@ public class MediaController extends FrameLayout {
super.setEnabled(enabled);
}
+ @Override
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(event);
+ event.setClassName(MediaController.class.getName());
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+ info.setClassName(MediaController.class.getName());
+ }
+
private View.OnClickListener mRewListener = new View.OnClickListener() {
public void onClick(View v) {
int pos = mPlayer.getCurrentPosition();
diff --git a/core/java/android/widget/MultiAutoCompleteTextView.java b/core/java/android/widget/MultiAutoCompleteTextView.java
index 134e4c4..0b30c84 100644
--- a/core/java/android/widget/MultiAutoCompleteTextView.java
+++ b/core/java/android/widget/MultiAutoCompleteTextView.java
@@ -23,7 +23,8 @@ import android.text.Spanned;
import android.text.TextUtils;
import android.text.method.QwertyKeyListener;
import android.util.AttributeSet;
-import android.widget.MultiAutoCompleteTextView.Tokenizer;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
/**
* An editable text view, extending {@link AutoCompleteTextView}, that
@@ -196,6 +197,18 @@ public class MultiAutoCompleteTextView extends AutoCompleteTextView {
editable.replace(start, end, mTokenizer.terminateToken(text));
}
+ @Override
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(event);
+ event.setClassName(MultiAutoCompleteTextView.class.getName());
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+ info.setClassName(MultiAutoCompleteTextView.class.getName());
+ }
+
public static interface Tokenizer {
/**
* Returns the start of the token that ends at offset
diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java
index 7d0f98e..a210f0b 100644
--- a/core/java/android/widget/NumberPicker.java
+++ b/core/java/android/widget/NumberPicker.java
@@ -47,6 +47,7 @@ import android.view.View;
import android.view.ViewConfiguration;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityNodeInfo;
import android.view.animation.DecelerateInterpolator;
import android.view.inputmethod.InputMethodManager;
@@ -1382,6 +1383,18 @@ public class NumberPicker extends LinearLayout {
// perceive this widget as several controls rather as a whole.
}
+ @Override
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(event);
+ event.setClassName(NumberPicker.class.getName());
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+ info.setClassName(NumberPicker.class.getName());
+ }
+
/**
* Makes a measure spec that tries greedily to use the max value.
*
diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index df88fec..ace3f60 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -45,6 +45,7 @@ import android.view.View;
import android.view.ViewDebug;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityNodeInfo;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
@@ -1124,10 +1125,17 @@ public class ProgressBar extends View {
@Override
public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
super.onInitializeAccessibilityEvent(event);
+ event.setClassName(ProgressBar.class.getName());
event.setItemCount(mMax);
event.setCurrentItemIndex(mProgress);
}
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+ info.setClassName(ProgressBar.class.getName());
+ }
+
/**
* Schedule a command for sending an accessibility event.
* </br>
diff --git a/core/java/android/widget/QuickContactBadge.java b/core/java/android/widget/QuickContactBadge.java
index adc0fb0..786afe2 100644
--- a/core/java/android/widget/QuickContactBadge.java
+++ b/core/java/android/widget/QuickContactBadge.java
@@ -36,6 +36,8 @@ import android.provider.ContactsContract.RawContacts;
import android.util.AttributeSet;
import android.view.View;
import android.view.View.OnClickListener;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
/**
* Widget used to show an image with the standard QuickContact badge
@@ -228,6 +230,18 @@ public class QuickContactBadge extends ImageView implements OnClickListener {
}
}
+ @Override
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(event);
+ event.setClassName(QuickContactBadge.class.getName());
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+ info.setClassName(QuickContactBadge.class.getName());
+ }
+
/**
* Set a list of specific MIME-types to exclude and not display. For
* example, this can be used to hide the {@link Contacts#CONTENT_ITEM_TYPE}
diff --git a/core/java/android/widget/RadioButton.java b/core/java/android/widget/RadioButton.java
index 9fa649f..b6dac3e 100644
--- a/core/java/android/widget/RadioButton.java
+++ b/core/java/android/widget/RadioButton.java
@@ -19,6 +19,7 @@ package android.widget;
import android.content.Context;
import android.util.AttributeSet;
import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
import com.android.internal.R;
@@ -85,4 +86,16 @@ public class RadioButton extends CompoundButton {
event.getText().add(mContext.getString(R.string.radiobutton_not_selected));
}
}
+
+ @Override
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(event);
+ event.setClassName(RadioButton.class.getName());
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+ info.setClassName(RadioButton.class.getName());
+ }
}
diff --git a/core/java/android/widget/RadioGroup.java b/core/java/android/widget/RadioGroup.java
index 393346a..7f53ffd 100644
--- a/core/java/android/widget/RadioGroup.java
+++ b/core/java/android/widget/RadioGroup.java
@@ -23,6 +23,8 @@ import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
/**
@@ -236,6 +238,18 @@ public class RadioGroup extends LinearLayout {
return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
}
+ @Override
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(event);
+ event.setClassName(RadioGroup.class.getName());
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+ info.setClassName(RadioGroup.class.getName());
+ }
+
/**
* <p>This set of layout parameters defaults the width and the height of
* the children to {@link #WRAP_CONTENT} when they are not specified in the
diff --git a/core/java/android/widget/RatingBar.java b/core/java/android/widget/RatingBar.java
index 9e6ff4b..e69577b 100644
--- a/core/java/android/widget/RatingBar.java
+++ b/core/java/android/widget/RatingBar.java
@@ -21,6 +21,8 @@ import android.content.res.TypedArray;
import android.graphics.drawable.shapes.RectShape;
import android.graphics.drawable.shapes.Shape;
import android.util.AttributeSet;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
import com.android.internal.R;
@@ -324,4 +326,15 @@ public class RatingBar extends AbsSeekBar {
super.setMax(max);
}
+ @Override
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(event);
+ event.setClassName(RatingBar.class.getName());
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+ info.setClassName(RatingBar.class.getName());
+ }
}
diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java
index a452fec..e4b8f34 100644
--- a/core/java/android/widget/RelativeLayout.java
+++ b/core/java/android/widget/RelativeLayout.java
@@ -40,6 +40,7 @@ import android.view.View;
import android.view.ViewDebug;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.RemoteViews.RemoteView;
import static android.util.Log.d;
@@ -985,6 +986,18 @@ public class RelativeLayout extends ViewGroup {
return false;
}
+ @Override
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(event);
+ event.setClassName(RelativeLayout.class.getName());
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+ info.setClassName(RelativeLayout.class.getName());
+ }
+
/**
* Compares two views in left-to-right and top-to-bottom fashion.
*/
diff --git a/core/java/android/widget/RemoteViewsAdapter.java b/core/java/android/widget/RemoteViewsAdapter.java
index 7b43032..586fdf4 100644
--- a/core/java/android/widget/RemoteViewsAdapter.java
+++ b/core/java/android/widget/RemoteViewsAdapter.java
@@ -68,6 +68,8 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback
private RemoteViewsAdapterServiceConnection mServiceConnection;
private WeakReference<RemoteAdapterConnectionCallback> mCallback;
private FixedSizeRemoteViewsCache mCache;
+ private int mVisibleWindowLowerBound;
+ private int mVisibleWindowUpperBound;
// A flag to determine whether we should notify data set changed after we connect
private boolean mNotifyDataSetChangedAfterOnServiceConnected = false;
@@ -765,7 +767,7 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback
}
if (position > -1) {
// Load the item, and notify any existing RemoteViewsFrameLayouts
- updateRemoteViews(position, isRequested);
+ updateRemoteViews(position, isRequested, true);
// Queue up for the next one to load
loadNextIndexInBackground();
@@ -827,8 +829,8 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback
}
}
- private void updateRemoteViews(final int position, boolean isRequested) {
- if (!mServiceConnection.isConnected()) return;
+ private void updateRemoteViews(final int position, boolean isRequested, boolean
+ notifyWhenLoaded) {
IRemoteViewsFactory factory = mServiceConnection.getRemoteViewsFactory();
// Load the item information from the remote service
@@ -864,12 +866,14 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback
// there is new data for it.
final RemoteViews rv = remoteViews;
final int typeId = mCache.getMetaDataAt(position).typeId;
- mMainQueue.post(new Runnable() {
- @Override
- public void run() {
- mRequestedViews.notifyOnRemoteViewsLoaded(position, rv, typeId);
- }
- });
+ if (notifyWhenLoaded) {
+ mMainQueue.post(new Runnable() {
+ @Override
+ public void run() {
+ mRequestedViews.notifyOnRemoteViewsLoaded(position, rv, typeId);
+ }
+ });
+ }
}
}
@@ -929,6 +933,16 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback
return typeId;
}
+ /**
+ * This method allows an AdapterView using this Adapter to provide information about which
+ * views are currently being displayed. This allows for certain optimizations and preloading
+ * which wouldn't otherwise be possible.
+ */
+ public void setVisibleRangeHint(int lowerBound, int upperBound) {
+ mVisibleWindowLowerBound = lowerBound;
+ mVisibleWindowUpperBound = upperBound;
+ }
+
public View getView(int position, View convertView, ViewGroup parent) {
// "Request" an index so that we can queue it for loading, initiate subsequent
// preloading, etc.
@@ -1059,6 +1073,13 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback
// Re-request the new metadata (only after the notification to the factory)
updateTemporaryMetaData();
+ // Pre-load (our best guess of) the views which are currently visible in the AdapterView.
+ // This mitigates flashing and flickering of loading views when a widget notifies that
+ // its data has changed.
+ for (int i = mVisibleWindowLowerBound; i <= mVisibleWindowUpperBound; i++) {
+ updateRemoteViews(i, false, false);
+ }
+
// Propagate the notification back to the base adapter
mMainQueue.post(new Runnable() {
@Override
diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java
index 767eaee..3ffc0fe 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -721,12 +721,14 @@ public class ScrollView extends FrameLayout {
@Override
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(info);
+ info.setClassName(ScrollView.class.getName());
info.setScrollable(getScrollRange() > 0);
}
@Override
public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
super.onInitializeAccessibilityEvent(event);
+ event.setClassName(ScrollView.class.getName());
final boolean scrollable = getScrollRange() > 0;
event.setScrollable(scrollable);
event.setScrollX(mScrollX);
diff --git a/core/java/android/widget/SearchView.java b/core/java/android/widget/SearchView.java
index 9d2ff2e..99cd0b8 100644
--- a/core/java/android/widget/SearchView.java
+++ b/core/java/android/widget/SearchView.java
@@ -35,7 +35,6 @@ import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
-import android.os.Handler;
import android.speech.RecognizerIntent;
import android.text.Editable;
import android.text.InputType;
@@ -51,6 +50,8 @@ import android.view.CollapsibleActionView;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView.OnItemClickListener;
@@ -1206,6 +1207,18 @@ public class SearchView extends LinearLayout implements CollapsibleActionView {
setIconified(false);
}
+ @Override
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(event);
+ event.setClassName(SearchView.class.getName());
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+ info.setClassName(SearchView.class.getName());
+ }
+
private void adjustDropDownSizeAndPosition() {
if (mDropDownAnchor.getWidth() > 1) {
Resources res = getContext().getResources();
diff --git a/core/java/android/widget/SeekBar.java b/core/java/android/widget/SeekBar.java
index c76728f..2737f94 100644
--- a/core/java/android/widget/SeekBar.java
+++ b/core/java/android/widget/SeekBar.java
@@ -18,6 +18,8 @@ package android.widget;
import android.content.Context;
import android.util.AttributeSet;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
@@ -117,5 +119,16 @@ public class SeekBar extends AbsSeekBar {
mOnSeekBarChangeListener.onStopTrackingTouch(this);
}
}
-
+
+ @Override
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(event);
+ event.setClassName(SeekBar.class.getName());
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+ info.setClassName(SeekBar.class.getName());
+ }
}
diff --git a/core/java/android/widget/SlidingDrawer.java b/core/java/android/widget/SlidingDrawer.java
index bdeb5c2..14edd10 100644
--- a/core/java/android/widget/SlidingDrawer.java
+++ b/core/java/android/widget/SlidingDrawer.java
@@ -32,6 +32,7 @@ import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
/**
* SlidingDrawer hides content out of the screen and allows the user to drag a handle
@@ -810,6 +811,18 @@ public class SlidingDrawer extends ViewGroup {
}
}
+ @Override
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(event);
+ event.setClassName(SlidingDrawer.class.getName());
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+ info.setClassName(SlidingDrawer.class.getName());
+ }
+
private void closeDrawer() {
moveHandle(COLLAPSED_FULL_CLOSED);
mContent.setVisibility(View.GONE);
diff --git a/core/java/android/widget/Spinner.java b/core/java/android/widget/Spinner.java
index ec3790e..ecf19b3 100644
--- a/core/java/android/widget/Spinner.java
+++ b/core/java/android/widget/Spinner.java
@@ -29,6 +29,8 @@ import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
/**
@@ -456,12 +458,24 @@ public class Spinner extends AbsSpinner implements OnClickListener {
return handled;
}
-
+
public void onClick(DialogInterface dialog, int which) {
setSelection(which);
dialog.dismiss();
}
+ @Override
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(event);
+ event.setClassName(Spinner.class.getName());
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+ info.setClassName(Spinner.class.getName());
+ }
+
/**
* Sets the prompt to display when the dialog is shown.
* @param prompt the prompt to set
diff --git a/core/java/android/widget/StackView.java b/core/java/android/widget/StackView.java
index 03e6e99..22df3bc 100644
--- a/core/java/android/widget/StackView.java
+++ b/core/java/android/widget/StackView.java
@@ -40,6 +40,8 @@ import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
import android.view.animation.LinearInterpolator;
import android.widget.RemoteViews.RemoteView;
@@ -1216,6 +1218,18 @@ public class StackView extends AdapterViewAnimator {
measureChildren();
}
+ @Override
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(event);
+ event.setClassName(StackView.class.getName());
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+ info.setClassName(StackView.class.getName());
+ }
+
class LayoutParams extends ViewGroup.LayoutParams {
int horizontalOffset;
int verticalOffset;
diff --git a/core/java/android/widget/Switch.java b/core/java/android/widget/Switch.java
index 02c9d03..334b9c4 100644
--- a/core/java/android/widget/Switch.java
+++ b/core/java/android/widget/Switch.java
@@ -35,6 +35,7 @@ import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.ViewConfiguration;
import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
import com.android.internal.R;
@@ -651,4 +652,16 @@ public class Switch extends CompoundButton {
mThumbDrawable.jumpToCurrentState();
mTrackDrawable.jumpToCurrentState();
}
+
+ @Override
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(event);
+ event.setClassName(Switch.class.getName());
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+ info.setClassName(Switch.class.getName());
+ }
}
diff --git a/core/java/android/widget/TabHost.java b/core/java/android/widget/TabHost.java
index 88d7230..9b292be 100644
--- a/core/java/android/widget/TabHost.java
+++ b/core/java/android/widget/TabHost.java
@@ -33,6 +33,8 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.Window;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
import java.util.ArrayList;
import java.util.List;
@@ -321,6 +323,18 @@ mTabHost.addTab(TAB_TAG_1, "Hello, world!", "Tab 1");
}
}
+ @Override
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(event);
+ event.setClassName(TabHost.class.getName());
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+ info.setClassName(TabHost.class.getName());
+ }
+
public void setCurrentTab(int index) {
if (index < 0 || index >= mTabSpecs.size()) {
return;
diff --git a/core/java/android/widget/TabWidget.java b/core/java/android/widget/TabWidget.java
index 80bfe99..8901037 100644
--- a/core/java/android/widget/TabWidget.java
+++ b/core/java/android/widget/TabWidget.java
@@ -29,6 +29,7 @@ import android.view.View;
import android.view.View.OnFocusChangeListener;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
/**
*
@@ -416,10 +417,28 @@ public class TabWidget extends LinearLayout implements OnFocusChangeListener {
@Override
public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
super.onInitializeAccessibilityEvent(event);
+ event.setClassName(TabWidget.class.getName());
event.setItemCount(getTabCount());
event.setCurrentItemIndex(mSelectedTab);
}
+
+ @Override
+ public void sendAccessibilityEventUnchecked(AccessibilityEvent event) {
+ // this class fires events only when tabs are focused or selected
+ if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_FOCUSED && isFocused()) {
+ event.recycle();
+ return;
+ }
+ super.sendAccessibilityEventUnchecked(event);
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+ info.setClassName(TabWidget.class.getName());
+ }
+
/**
* Sets the current tab and focuses the UI on it.
* This method makes sure that the focused tab matches the selected
@@ -485,16 +504,6 @@ public class TabWidget extends LinearLayout implements OnFocusChangeListener {
mSelectedTab = -1;
}
- @Override
- public void sendAccessibilityEventUnchecked(AccessibilityEvent event) {
- // this class fires events only when tabs are focused or selected
- if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_FOCUSED && isFocused()) {
- event.recycle();
- return;
- }
- super.sendAccessibilityEventUnchecked(event);
- }
-
/**
* Provides a way for {@link TabHost} to be notified that the user clicked on a tab indicator.
*/
diff --git a/core/java/android/widget/TableLayout.java b/core/java/android/widget/TableLayout.java
index 842b087..f5d3746 100644
--- a/core/java/android/widget/TableLayout.java
+++ b/core/java/android/widget/TableLayout.java
@@ -24,6 +24,8 @@ import android.util.AttributeSet;
import android.util.SparseBooleanArray;
import android.view.View;
import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
import java.util.regex.Pattern;
@@ -658,6 +660,18 @@ public class TableLayout extends LinearLayout {
return new LayoutParams(p);
}
+ @Override
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(event);
+ event.setClassName(TableLayout.class.getName());
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+ info.setClassName(TableLayout.class.getName());
+ }
+
/**
* <p>This set of layout parameters enforces the width of each child to be
* {@link #MATCH_PARENT} and the height of each child to be
diff --git a/core/java/android/widget/TableRow.java b/core/java/android/widget/TableRow.java
index 3fd4631..01c4c2c 100644
--- a/core/java/android/widget/TableRow.java
+++ b/core/java/android/widget/TableRow.java
@@ -24,6 +24,8 @@ import android.view.Gravity;
import android.view.View;
import android.view.ViewDebug;
import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
/**
@@ -377,6 +379,18 @@ public class TableRow extends LinearLayout {
return new LayoutParams(p);
}
+ @Override
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(event);
+ event.setClassName(TableRow.class.getName());
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+ info.setClassName(TableRow.class.getName());
+ }
+
/**
* <p>Set of layout parameters used in table rows.</p>
*
diff --git a/core/java/android/widget/TextSwitcher.java b/core/java/android/widget/TextSwitcher.java
index a8794a3..1aefd2b 100644
--- a/core/java/android/widget/TextSwitcher.java
+++ b/core/java/android/widget/TextSwitcher.java
@@ -21,6 +21,8 @@ import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
/**
* Specialized {@link android.widget.ViewSwitcher} that contains
@@ -88,4 +90,16 @@ public class TextSwitcher extends ViewSwitcher {
public void setCurrentText(CharSequence text) {
((TextView)getCurrentView()).setText(text);
}
+
+ @Override
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(event);
+ event.setClassName(TextSwitcher.class.getName());
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+ info.setClassName(TextSwitcher.class.getName());
+ }
}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index acea1a1..e508e9a 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -357,7 +357,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
private float mLastDownPositionX, mLastDownPositionY;
private Callback mCustomSelectionActionModeCallback;
- private final int mSquaredTouchSlopDistance;
// Set when this TextView gained focus with some text selected. Will start selection mode.
private boolean mCreatedWithASelection = false;
@@ -443,15 +442,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
this(context, null);
}
- public TextView(Context context,
- AttributeSet attrs) {
+ public TextView(Context context, AttributeSet attrs) {
this(context, attrs, com.android.internal.R.attr.textViewStyle);
}
@SuppressWarnings("deprecation")
- public TextView(Context context,
- AttributeSet attrs,
- int defStyle) {
+ public TextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mText = "";
@@ -1134,10 +1130,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
setLongClickable(longClickable);
prepareCursorControllers();
-
- final ViewConfiguration viewConfiguration = ViewConfiguration.get(context);
- final int touchSlop = viewConfiguration.getScaledTouchSlop();
- mSquaredTouchSlopDistance = touchSlop * touchSlop;
}
private void setTypefaceByIndex(int typefaceIndex, int styleIndex) {
@@ -3202,8 +3194,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
int n = mFilters.length;
for (int i = 0; i < n; i++) {
- CharSequence out = mFilters[i].filter(text, 0, text.length(),
- EMPTY_SPANNED, 0, 0);
+ CharSequence out = mFilters[i].filter(text, 0, text.length(), EMPTY_SPANNED, 0, 0);
if (out != null) {
text = out;
}
@@ -5273,10 +5264,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
state.handleUpEvent(event);
}
if (event.isTracking() && !event.isCanceled()) {
- if (isInSelectionMode) {
- stopSelectionActionMode();
- return true;
- }
+ stopSelectionActionMode();
+ return true;
}
}
}
@@ -5621,11 +5610,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
return super.onKeyUp(keyCode, event);
}
- @Override public boolean onCheckIsTextEditor() {
+ @Override
+ public boolean onCheckIsTextEditor() {
return mInputType != EditorInfo.TYPE_NULL;
}
- @Override public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
+ @Override
+ public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
if (onCheckIsTextEditor() && isEnabled()) {
if (mInputMethodState == null) {
mInputMethodState = new InputMethodState();
@@ -9029,6 +9020,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
super.onInitializeAccessibilityEvent(event);
+ event.setClassName(TextView.class.getName());
final boolean isPassword = hasPasswordTransformationMethod();
event.setPassword(isPassword);
@@ -9043,11 +9035,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(info);
+ info.setClassName(TextView.class.getName());
final boolean isPassword = hasPasswordTransformationMethod();
+ info.setPassword(isPassword);
+
if (!isPassword) {
info.setText(getTextForAccessibility());
}
- info.setPassword(isPassword);
}
@Override
@@ -9245,7 +9239,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
boolean vibrate = true;
if (super.performLongClick()) {
- mDiscardNextActionUp = true;
handled = true;
}
@@ -10181,7 +10174,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
return false;
}
- private void stopSelectionActionMode() {
+ /**
+ * @hide
+ */
+ protected void stopSelectionActionMode() {
if (mSelectionActionMode != null) {
// This will hide the mSelectionModifierCursorController
mSelectionActionMode.finish();
@@ -10795,7 +10791,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
final float deltaX = mDownPositionX - ev.getRawX();
final float deltaY = mDownPositionY - ev.getRawY();
final float distanceSquared = deltaX * deltaX + deltaY * deltaY;
- if (distanceSquared < mSquaredTouchSlopDistance) {
+
+ final ViewConfiguration viewConfiguration = ViewConfiguration.get(
+ TextView.this.getContext());
+ final int touchSlop = viewConfiguration.getScaledTouchSlop();
+
+ if (distanceSquared < touchSlop * touchSlop) {
if (mActionPopupWindow != null && mActionPopupWindow.isShowing()) {
// Tapping on the handle dismisses the displayed action popup
mActionPopupWindow.hide();
@@ -11009,7 +11010,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
// Double tap detection
private long mPreviousTapUpTime = 0;
- private float mPreviousTapPositionX, mPreviousTapPositionY;
+ private float mDownPositionX, mDownPositionY;
+ private boolean mGestureStayedInTapRegion;
SelectionModifierCursorController() {
resetTouchOffsets();
@@ -11072,20 +11074,28 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
mMinTouchOffset = mMaxTouchOffset = getOffsetForPosition(x, y);
// Double tap detection
- long duration = SystemClock.uptimeMillis() - mPreviousTapUpTime;
- if (duration <= ViewConfiguration.getDoubleTapTimeout() &&
- isPositionOnText(x, y)) {
- final float deltaX = x - mPreviousTapPositionX;
- final float deltaY = y - mPreviousTapPositionY;
- final float distanceSquared = deltaX * deltaX + deltaY * deltaY;
- if (distanceSquared < mSquaredTouchSlopDistance) {
- startSelectionActionMode();
- mDiscardNextActionUp = true;
+ if (mGestureStayedInTapRegion) {
+ long duration = SystemClock.uptimeMillis() - mPreviousTapUpTime;
+ if (duration <= ViewConfiguration.getDoubleTapTimeout()) {
+ final float deltaX = x - mDownPositionX;
+ final float deltaY = y - mDownPositionY;
+ final float distanceSquared = deltaX * deltaX + deltaY * deltaY;
+
+ ViewConfiguration viewConfiguration = ViewConfiguration.get(
+ TextView.this.getContext());
+ int doubleTapSlop = viewConfiguration.getScaledDoubleTapSlop();
+ boolean stayedInArea = distanceSquared < doubleTapSlop * doubleTapSlop;
+
+ if (stayedInArea && isPositionOnText(x, y)) {
+ startSelectionActionMode();
+ mDiscardNextActionUp = true;
+ }
}
}
- mPreviousTapPositionX = x;
- mPreviousTapPositionY = y;
+ mDownPositionX = x;
+ mDownPositionY = y;
+ mGestureStayedInTapRegion = true;
break;
case MotionEvent.ACTION_POINTER_DOWN:
@@ -11098,6 +11108,22 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
break;
+ case MotionEvent.ACTION_MOVE:
+ if (mGestureStayedInTapRegion) {
+ final float deltaX = event.getX() - mDownPositionX;
+ final float deltaY = event.getY() - mDownPositionY;
+ final float distanceSquared = deltaX * deltaX + deltaY * deltaY;
+
+ final ViewConfiguration viewConfiguration = ViewConfiguration.get(
+ TextView.this.getContext());
+ int doubleTapTouchSlop = viewConfiguration.getScaledDoubleTapTouchSlop();
+
+ if (distanceSquared > doubleTapTouchSlop * doubleTapTouchSlop) {
+ mGestureStayedInTapRegion = false;
+ }
+ }
+ break;
+
case MotionEvent.ACTION_UP:
mPreviousTapUpTime = SystemClock.uptimeMillis();
break;
diff --git a/core/java/android/widget/TimePicker.java b/core/java/android/widget/TimePicker.java
index afca2db..8f10fff 100644
--- a/core/java/android/widget/TimePicker.java
+++ b/core/java/android/widget/TimePicker.java
@@ -27,6 +27,7 @@ import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.NumberPicker.OnValueChangeListener;
@@ -476,6 +477,18 @@ public class TimePicker extends FrameLayout {
event.getText().add(selectedDateUtterance);
}
+ @Override
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(event);
+ event.setClassName(TimePicker.class.getName());
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+ info.setClassName(TimePicker.class.getName());
+ }
+
private void updateHourControl() {
if (is24HourView()) {
mHourSpinner.setMinValue(0);
diff --git a/core/java/android/widget/ToggleButton.java b/core/java/android/widget/ToggleButton.java
index a754268..a0edafe 100644
--- a/core/java/android/widget/ToggleButton.java
+++ b/core/java/android/widget/ToggleButton.java
@@ -23,6 +23,7 @@ import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.util.AttributeSet;
import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
import com.android.internal.R;
@@ -161,4 +162,16 @@ public class ToggleButton extends CompoundButton {
event.getText().add(mContext.getString(R.string.togglebutton_not_pressed));
}
}
+
+ @Override
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(event);
+ event.setClassName(ToggleButton.class.getName());
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+ info.setClassName(ToggleButton.class.getName());
+ }
}
diff --git a/core/java/android/widget/TwoLineListItem.java b/core/java/android/widget/TwoLineListItem.java
index eab6f2d..e707ea3 100644
--- a/core/java/android/widget/TwoLineListItem.java
+++ b/core/java/android/widget/TwoLineListItem.java
@@ -16,14 +16,12 @@
package android.widget;
-import com.android.internal.R;
-
-
import android.annotation.Widget;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
-import android.view.View;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.RelativeLayout;
/**
@@ -86,4 +84,16 @@ public class TwoLineListItem extends RelativeLayout {
public TextView getText2() {
return mText2;
}
+
+ @Override
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(event);
+ event.setClassName(TwoLineListItem.class.getName());
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+ info.setClassName(TwoLineListItem.class.getName());
+ }
}
diff --git a/core/java/android/widget/VideoView.java b/core/java/android/widget/VideoView.java
index 64fdf34..0fba498 100644
--- a/core/java/android/widget/VideoView.java
+++ b/core/java/android/widget/VideoView.java
@@ -34,6 +34,8 @@ import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.MediaController.MediaPlayerControl;
import java.io.IOException;
@@ -124,6 +126,18 @@ public class VideoView extends SurfaceView implements MediaPlayerControl {
setMeasuredDimension(width, height);
}
+ @Override
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(event);
+ event.setClassName(VideoView.class.getName());
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+ info.setClassName(VideoView.class.getName());
+ }
+
public int resolveAdjustedSize(int desiredSize, int measureSpec) {
int result = desiredSize;
int specMode = MeasureSpec.getMode(measureSpec);
diff --git a/core/java/android/widget/ViewAnimator.java b/core/java/android/widget/ViewAnimator.java
index 71ff66b..6a68240 100644
--- a/core/java/android/widget/ViewAnimator.java
+++ b/core/java/android/widget/ViewAnimator.java
@@ -22,6 +22,8 @@ import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
@@ -341,4 +343,16 @@ public class ViewAnimator extends FrameLayout {
public int getBaseline() {
return (getCurrentView() != null) ? getCurrentView().getBaseline() : super.getBaseline();
}
+
+ @Override
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(event);
+ event.setClassName(ViewAnimator.class.getName());
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+ info.setClassName(ViewAnimator.class.getName());
+ }
}
diff --git a/core/java/android/widget/ViewFlipper.java b/core/java/android/widget/ViewFlipper.java
index c6f6e81..061bb00 100644
--- a/core/java/android/widget/ViewFlipper.java
+++ b/core/java/android/widget/ViewFlipper.java
@@ -25,6 +25,8 @@ import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.util.Log;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.RemoteViews.RemoteView;
/**
@@ -139,6 +141,18 @@ public class ViewFlipper extends ViewAnimator {
updateRunning();
}
+ @Override
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(event);
+ event.setClassName(ViewFlipper.class.getName());
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+ info.setClassName(ViewFlipper.class.getName());
+ }
+
/**
* Internal method to start or stop dispatching flip {@link Message} based
* on {@link #mRunning} and {@link #mVisible} state.
diff --git a/core/java/android/widget/ViewSwitcher.java b/core/java/android/widget/ViewSwitcher.java
index 71ae624..0376918 100644
--- a/core/java/android/widget/ViewSwitcher.java
+++ b/core/java/android/widget/ViewSwitcher.java
@@ -20,6 +20,8 @@ import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
/**
* {@link ViewAnimator} that switches between two views, and has a factory
@@ -66,6 +68,18 @@ public class ViewSwitcher extends ViewAnimator {
super.addView(child, index, params);
}
+ @Override
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(event);
+ event.setClassName(ViewSwitcher.class.getName());
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+ info.setClassName(ViewSwitcher.class.getName());
+ }
+
/**
* Returns the next view to be displayed.
*
diff --git a/core/java/android/widget/ZoomButton.java b/core/java/android/widget/ZoomButton.java
index eb372ca..af17c94 100644
--- a/core/java/android/widget/ZoomButton.java
+++ b/core/java/android/widget/ZoomButton.java
@@ -23,6 +23,8 @@ import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnLongClickListener;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
public class ZoomButton extends ImageButton implements OnLongClickListener {
@@ -96,4 +98,16 @@ public class ZoomButton extends ImageButton implements OnLongClickListener {
clearFocus();
return super.dispatchUnhandledMove(focused, direction);
}
+
+ @Override
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(event);
+ event.setClassName(ZoomButton.class.getName());
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+ info.setClassName(ZoomButton.class.getName());
+ }
}
diff --git a/core/java/android/widget/ZoomControls.java b/core/java/android/widget/ZoomControls.java
index a12aee5..8897875 100644
--- a/core/java/android/widget/ZoomControls.java
+++ b/core/java/android/widget/ZoomControls.java
@@ -22,6 +22,8 @@ import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
import android.view.animation.AlphaAnimation;
import com.android.internal.R;
@@ -106,4 +108,16 @@ public class ZoomControls extends LinearLayout {
public boolean hasFocus() {
return mZoomIn.hasFocus() || mZoomOut.hasFocus();
}
+
+ @Override
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(event);
+ event.setClassName(ZoomControls.class.getName());
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+ info.setClassName(ZoomControls.class.getName());
+ }
}