summaryrefslogtreecommitdiffstats
path: root/core/java/android
diff options
context:
space:
mode:
Diffstat (limited to 'core/java/android')
-rw-r--r--core/java/android/accessibilityservice/AccessibilityService.java9
-rw-r--r--core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl2
-rw-r--r--core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl21
-rw-r--r--core/java/android/accounts/AccountManager.java2
-rw-r--r--core/java/android/bluetooth/BluetoothTetheringDataTracker.java1
-rw-r--r--core/java/android/content/ContentResolver.java19
-rw-r--r--core/java/android/content/ContentService.java6
-rw-r--r--core/java/android/content/SyncManager.java299
-rw-r--r--core/java/android/content/SyncOperation.java60
-rw-r--r--core/java/android/content/SyncQueue.java16
-rw-r--r--core/java/android/content/SyncStorageEngine.java39
-rw-r--r--core/java/android/content/res/StringBlock.java87
-rw-r--r--core/java/android/hardware/SerialPort.java8
-rw-r--r--core/java/android/inputmethodservice/InputMethodService.java2
-rw-r--r--core/java/android/net/ConnectivityManager.java12
-rw-r--r--core/java/android/net/DhcpInfo.java5
-rw-r--r--core/java/android/net/DhcpInfoInternal.java166
-rw-r--r--core/java/android/net/DhcpResults.aidl (renamed from core/java/android/util/Pool.java)16
-rw-r--r--core/java/android/net/DhcpResults.java244
-rw-r--r--core/java/android/net/DhcpStateMachine.java19
-rw-r--r--core/java/android/net/EthernetDataTracker.java7
-rw-r--r--core/java/android/net/LinkProperties.java41
-rw-r--r--core/java/android/net/NetworkUtils.java13
-rw-r--r--core/java/android/net/RouteInfo.java4
-rw-r--r--core/java/android/net/http/AndroidHttpClient.java36
-rw-r--r--core/java/android/os/INetworkManagementService.aidl2
-rw-r--r--core/java/android/os/Trace.java5
-rw-r--r--core/java/android/service/wallpaper/WallpaperService.java21
-rw-r--r--core/java/android/speech/tts/ITextToSpeechService.aidl7
-rw-r--r--core/java/android/speech/tts/SynthesisCallback.java5
-rw-r--r--core/java/android/speech/tts/TextToSpeech.java188
-rw-r--r--core/java/android/speech/tts/TextToSpeechService.java134
-rw-r--r--core/java/android/text/Html.java58
-rw-r--r--core/java/android/text/method/DigitsKeyListener.java39
-rw-r--r--core/java/android/text/style/SuggestionSpan.java49
-rw-r--r--core/java/android/util/FinitePool.java94
-rw-r--r--core/java/android/util/Poolable.java27
-rw-r--r--core/java/android/util/PoolableManager.java27
-rw-r--r--core/java/android/util/Pools.java166
-rwxr-xr-xcore/java/android/util/PropertyValueModel.java143
-rw-r--r--core/java/android/util/SparseLongArray.java2
-rw-r--r--core/java/android/util/SynchronizedPool.java48
-rwxr-xr-xcore/java/android/util/ValueModel.java74
-rw-r--r--core/java/android/view/AccessibilityInteractionController.java120
-rw-r--r--core/java/android/view/DisplayList.java24
-rw-r--r--core/java/android/view/GLES20RecordingCanvas.java47
-rw-r--r--core/java/android/view/HardwareRenderer.java163
-rw-r--r--core/java/android/view/IDisplayMagnificationController.aidl (renamed from core/java/android/view/IDisplayContentChangeListener.aidl)16
-rw-r--r--core/java/android/view/IDisplayMagnificationMediator.aidl31
-rw-r--r--core/java/android/view/IWindowManager.aidl34
-rw-r--r--core/java/android/view/MagnificationSpec.aidl (renamed from core/java/android/view/WindowInfo.aidl)2
-rw-r--r--core/java/android/view/MagnificationSpec.java120
-rw-r--r--core/java/android/view/Surface.java45
-rw-r--r--core/java/android/view/VelocityTracker.java58
-rw-r--r--core/java/android/view/View.java162
-rw-r--r--core/java/android/view/ViewGroup.java282
-rw-r--r--core/java/android/view/ViewRootImpl.java213
-rw-r--r--core/java/android/view/VolumePanel.java90
-rw-r--r--core/java/android/view/WindowInfo.java175
-rw-r--r--core/java/android/view/WindowManagerPolicy.java8
-rw-r--r--core/java/android/view/accessibility/AccessibilityEvent.java249
-rw-r--r--core/java/android/view/accessibility/AccessibilityInteractionClient.java73
-rw-r--r--core/java/android/view/accessibility/AccessibilityNodeInfo.java60
-rw-r--r--core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl11
-rw-r--r--core/java/android/webkit/BrowserFrame.java2
-rw-r--r--core/java/android/webkit/CacheManager.java126
-rw-r--r--core/java/android/webkit/EventLogTags.logtags1
-rw-r--r--core/java/android/webkit/HttpAuthHandler.java2
-rw-r--r--core/java/android/webkit/WebSettings.java37
-rw-r--r--core/java/android/webkit/WebSettingsClassic.java4
-rw-r--r--core/java/android/webkit/WebView.java10
-rw-r--r--core/java/android/webkit/WebViewClassic.java5
-rw-r--r--core/java/android/webkit/WebViewClient.java9
-rw-r--r--core/java/android/webkit/WebViewCore.java3
-rw-r--r--core/java/android/webkit/WebViewDatabase.java20
-rw-r--r--core/java/android/webkit/WebViewFactory.java2
-rw-r--r--core/java/android/webkit/WebViewProvider.java2
-rw-r--r--core/java/android/widget/AbsSpinner.java2
-rw-r--r--core/java/android/widget/CheckBox.java23
-rw-r--r--core/java/android/widget/EditText.java23
-rw-r--r--core/java/android/widget/Editor.java13
-rw-r--r--core/java/android/widget/GridLayout.java77
-rw-r--r--core/java/android/widget/ImageView.java50
-rw-r--r--core/java/android/widget/LinearLayout.java28
-rw-r--r--core/java/android/widget/MediaController.java12
-rw-r--r--core/java/android/widget/ProgressBar.java56
-rw-r--r--core/java/android/widget/RelativeLayout.java87
-rw-r--r--core/java/android/widget/SearchView.java1
-rw-r--r--core/java/android/widget/SeekBar.java20
-rw-r--r--core/java/android/widget/SpellChecker.java15
-rw-r--r--core/java/android/widget/Spinner.java89
-rw-r--r--core/java/android/widget/TableLayout.java2
-rw-r--r--core/java/android/widget/TableRow.java2
-rw-r--r--core/java/android/widget/TextView.java76
-rwxr-xr-xcore/java/android/widget/ValueEditor.java53
-rw-r--r--core/java/android/widget/VideoView.java90
96 files changed, 3147 insertions, 2001 deletions
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 9d6ee80..7efe189 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -548,6 +548,7 @@ public abstract class AccessibilityService extends Service {
private static final int DO_ON_INTERRUPT = 20;
private static final int DO_ON_ACCESSIBILITY_EVENT = 30;
private static final int DO_ON_GESTURE = 40;
+ private static final int DO_CLEAR_ACCESSIBILITY_NODE_INFO_CACHE = 50;
private final HandlerCaller mCaller;
@@ -580,6 +581,11 @@ public abstract class AccessibilityService extends Service {
mCaller.sendMessage(message);
}
+ public void clearAccessibilityNodeInfoCache() {
+ Message message = mCaller.obtainMessage(DO_CLEAR_ACCESSIBILITY_NODE_INFO_CACHE);
+ mCaller.sendMessage(message);
+ }
+
public void executeMessage(Message message) {
switch (message.what) {
case DO_ON_ACCESSIBILITY_EVENT :
@@ -611,6 +617,9 @@ public abstract class AccessibilityService extends Service {
final int gestureId = message.arg1;
mCallback.onGesture(gestureId);
return;
+ case DO_CLEAR_ACCESSIBILITY_NODE_INFO_CACHE:
+ AccessibilityInteractionClient.getInstance().clearCache();
+ return;
default :
Log.w(LOG_TAG, "Unknown message type " + message.what);
}
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
index d459fd5..5d684e3 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
@@ -33,4 +33,6 @@ import android.view.accessibility.AccessibilityEvent;
void onInterrupt();
void onGesture(int gesture);
+
+ void clearAccessibilityNodeInfoCache();
}
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
index dd50f3c..f33f503 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
@@ -18,6 +18,7 @@ package android.accessibilityservice;
import android.os.Bundle;
import android.accessibilityservice.AccessibilityServiceInfo;
+import android.view.MagnificationSpec;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
@@ -44,9 +45,9 @@ interface IAccessibilityServiceConnection {
* @param callback Callback which to receive the result.
* @param flags Additional flags.
* @param threadId The id of the calling thread.
- * @return The current window scale, where zero means a failure.
+ * @return Whether the call succeeded.
*/
- float findAccessibilityNodeInfoByAccessibilityId(int accessibilityWindowId,
+ boolean findAccessibilityNodeInfoByAccessibilityId(int accessibilityWindowId,
long accessibilityNodeId, int interactionId,
IAccessibilityInteractionConnectionCallback callback, int flags, long threadId);
@@ -66,9 +67,9 @@ interface IAccessibilityServiceConnection {
* @param interactionId The id of the interaction for matching with the callback result.
* @param callback Callback which to receive the result.
* @param threadId The id of the calling thread.
- * @return The current window scale, where zero means a failure.
+ * @return Whether the call succeeded.
*/
- float findAccessibilityNodeInfosByText(int accessibilityWindowId, long accessibilityNodeId,
+ boolean findAccessibilityNodeInfosByText(int accessibilityWindowId, long accessibilityNodeId,
String text, int interactionId, IAccessibilityInteractionConnectionCallback callback,
long threadId);
@@ -88,9 +89,9 @@ interface IAccessibilityServiceConnection {
* @param interactionId The id of the interaction for matching with the callback result.
* @param callback Callback which to receive the result.
* @param threadId The id of the calling thread.
- * @return The current window scale, where zero means a failure.
+ * @return Whether the call succeeded.
*/
- float findAccessibilityNodeInfoByViewId(int accessibilityWindowId, long accessibilityNodeId,
+ boolean findAccessibilityNodeInfoByViewId(int accessibilityWindowId, long accessibilityNodeId,
int viewId, int interactionId, IAccessibilityInteractionConnectionCallback callback,
long threadId);
@@ -110,9 +111,9 @@ interface IAccessibilityServiceConnection {
* @param interactionId The id of the interaction for matching with the callback result.
* @param callback Callback which to receive the result.
* @param threadId The id of the calling thread.
- * @return The current window scale, where zero means a failure.
+ * @return Whether the call succeeded.
*/
- float findFocus(int accessibilityWindowId, long accessibilityNodeId, int focusType,
+ boolean findFocus(int accessibilityWindowId, long accessibilityNodeId, int focusType,
int interactionId, IAccessibilityInteractionConnectionCallback callback, long threadId);
/**
@@ -131,9 +132,9 @@ interface IAccessibilityServiceConnection {
* @param interactionId The id of the interaction for matching with the callback result.
* @param callback Callback which to receive the result.
* @param threadId The id of the calling thread.
- * @return The current window scale, where zero means a failure.
+ * @return Whether the call succeeded.
*/
- float focusSearch(int accessibilityWindowId, long accessibilityNodeId, int direction,
+ boolean focusSearch(int accessibilityWindowId, long accessibilityNodeId, int direction,
int interactionId, IAccessibilityInteractionConnectionCallback callback, long threadId);
/**
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index bcb35d5..6d9bb1d 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -1846,7 +1846,7 @@ public class AccountManager {
* Returns an intent to an {@link Activity} that prompts the user to choose from a list of
* accounts.
* The caller will then typically start the activity by calling
- * <code>startActivityWithResult(intent, ...);</code>.
+ * <code>startActivityForResult(intent, ...);</code>.
* <p>
* On success the activity returns a Bundle with the account name and type specified using
* keys {@link #KEY_ACCOUNT_NAME} and {@link #KEY_ACCOUNT_TYPE}.
diff --git a/core/java/android/bluetooth/BluetoothTetheringDataTracker.java b/core/java/android/bluetooth/BluetoothTetheringDataTracker.java
index 063e5a8..3ba4f26 100644
--- a/core/java/android/bluetooth/BluetoothTetheringDataTracker.java
+++ b/core/java/android/bluetooth/BluetoothTetheringDataTracker.java
@@ -21,7 +21,6 @@ import android.os.ServiceManager;
import android.os.INetworkManagementService;
import android.content.Context;
import android.net.ConnectivityManager;
-import android.net.DhcpInfoInternal;
import android.net.LinkCapabilities;
import android.net.LinkProperties;
import android.net.NetworkInfo;
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 9e406d4..b20cf88 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -169,6 +169,25 @@ public abstract class ContentResolver {
/** @hide */
public static final int SYNC_ERROR_INTERNAL = 8;
+ private static final String[] SYNC_ERROR_NAMES = new String[] {
+ "already-in-progress",
+ "authentication-error",
+ "io-error",
+ "parse-error",
+ "conflict",
+ "too-many-deletions",
+ "too-many-retries",
+ "internal-error",
+ };
+
+ /** @hide */
+ static String syncErrorToString(int error) {
+ if (error < 1 || error > SYNC_ERROR_NAMES.length) {
+ return String.valueOf(error);
+ }
+ return SYNC_ERROR_NAMES[error - 1];
+ }
+
public static final int SYNC_OBSERVER_TYPE_SETTINGS = 1<<0;
public static final int SYNC_OBSERVER_TYPE_PENDING = 1<<1;
public static final int SYNC_OBSERVER_TYPE_ACTIVE = 1<<2;
diff --git a/core/java/android/content/ContentService.java b/core/java/android/content/ContentService.java
index 4512e82..8bac888 100644
--- a/core/java/android/content/ContentService.java
+++ b/core/java/android/content/ContentService.java
@@ -226,6 +226,7 @@ public final class ContentService extends IContentService.Stub {
}
}
+ final int uid = Binder.getCallingUid();
// This makes it so that future permission checks will be in the context of this
// process rather than the caller's process. We will restore this before returning.
long identityToken = clearCallingIdentity();
@@ -264,7 +265,7 @@ public final class ContentService extends IContentService.Stub {
if (syncToNetwork) {
SyncManager syncManager = getSyncManager();
if (syncManager != null) {
- syncManager.scheduleLocalSync(null /* all accounts */, callingUserHandle,
+ syncManager.scheduleLocalSync(null /* all accounts */, callingUserHandle, uid,
uri.getAuthority());
}
}
@@ -300,6 +301,7 @@ public final class ContentService extends IContentService.Stub {
public void requestSync(Account account, String authority, Bundle extras) {
ContentResolver.validateSyncExtrasBundle(extras);
int userId = UserHandle.getCallingUserId();
+ int uId = Binder.getCallingUid();
// This makes it so that future permission checks will be in the context of this
// process rather than the caller's process. We will restore this before returning.
@@ -307,7 +309,7 @@ public final class ContentService extends IContentService.Stub {
try {
SyncManager syncManager = getSyncManager();
if (syncManager != null) {
- syncManager.scheduleSync(account, userId, authority, extras, 0 /* no delay */,
+ syncManager.scheduleSync(account, userId, uId, authority, extras, 0 /* no delay */,
false /* onlyThoseWithUnkownSyncableState */);
}
} finally {
diff --git a/core/java/android/content/SyncManager.java b/core/java/android/content/SyncManager.java
index e4b4b97..e428968 100644
--- a/core/java/android/content/SyncManager.java
+++ b/core/java/android/content/SyncManager.java
@@ -65,6 +65,7 @@ import com.google.android.collect.Maps;
import com.google.android.collect.Sets;
import java.io.FileDescriptor;
+import java.io.PrintStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
@@ -201,7 +202,9 @@ public class SyncManager {
private BroadcastReceiver mBackgroundDataSettingChanged = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
if (getConnectivityManager().getBackgroundDataSetting()) {
- scheduleSync(null /* account */, UserHandle.USER_ALL, null /* authority */,
+ scheduleSync(null /* account */, UserHandle.USER_ALL,
+ SyncOperation.REASON_BACKGROUND_DATA_SETTINGS_CHANGED,
+ null /* authority */,
new Bundle(), 0 /* delay */,
false /* onlyThoseWithUnknownSyncableState */);
}
@@ -213,7 +216,8 @@ public class SyncManager {
updateRunningAccounts();
// Kick off sync for everyone, since this was a radical account change
- scheduleSync(null, UserHandle.USER_ALL, null, null, 0 /* no delay */, false);
+ scheduleSync(null, UserHandle.USER_ALL, SyncOperation.REASON_ACCOUNTS_UPDATED, null,
+ null, 0 /* no delay */, false);
}
};
@@ -351,14 +355,14 @@ public class SyncManager {
SyncStorageEngine.init(context);
mSyncStorageEngine = SyncStorageEngine.getSingleton();
mSyncStorageEngine.setOnSyncRequestListener(new OnSyncRequestListener() {
- public void onSyncRequest(Account account, int userId, String authority,
+ public void onSyncRequest(Account account, int userId, int reason, String authority,
Bundle extras) {
- scheduleSync(account, userId, authority, extras, 0, false);
+ scheduleSync(account, userId, reason, authority, extras, 0, false);
}
});
mSyncAdapters = new SyncAdaptersCache(mContext);
- mSyncQueue = new SyncQueue(mSyncStorageEngine, mSyncAdapters);
+ mSyncQueue = new SyncQueue(mContext.getPackageManager(), mSyncStorageEngine, mSyncAdapters);
HandlerThread syncThread = new HandlerThread("SyncHandlerThread",
Process.THREAD_PRIORITY_BACKGROUND);
@@ -369,7 +373,9 @@ public class SyncManager {
@Override
public void onServiceChanged(SyncAdapterType type, int userId, boolean removed) {
if (!removed) {
- scheduleSync(null, UserHandle.USER_ALL, type.authority, null, 0 /* no delay */,
+ scheduleSync(null, UserHandle.USER_ALL,
+ SyncOperation.REASON_SERVICE_CHANGED,
+ type.authority, null, 0 /* no delay */,
false /* onlyThoseWithUnkownSyncableState */);
}
}
@@ -498,6 +504,17 @@ public class SyncManager {
* @param requestedAccount the account to sync, may be null to signify all accounts
* @param userId the id of the user whose accounts are to be synced. If userId is USER_ALL,
* then all users' accounts are considered.
+ * @param reason for sync request. If this is a positive integer, it is the Linux uid
+ * assigned to the process that requested the sync. If it's negative, the sync was requested by
+ * the SyncManager itself and could be one of the following:
+ * {@link SyncOperation#REASON_BACKGROUND_DATA_SETTINGS_CHANGED}
+ * {@link SyncOperation#REASON_ACCOUNTS_UPDATED}
+ * {@link SyncOperation#REASON_SERVICE_CHANGED}
+ * {@link SyncOperation#REASON_PERIODIC}
+ * {@link SyncOperation#REASON_IS_SYNCABLE}
+ * {@link SyncOperation#REASON_SYNC_AUTO}
+ * {@link SyncOperation#REASON_MASTER_SYNC_AUTO}
+ * {@link SyncOperation#REASON_USER_START}
* @param requestedAuthority the authority to sync, may be null to indicate all authorities
* @param extras a Map of SyncAdapter-specific information to control
* syncs of a specific provider. Can be null. Is ignored
@@ -505,8 +522,9 @@ public class SyncManager {
* @param delay how many milliseconds in the future to wait before performing this
* @param onlyThoseWithUnkownSyncableState
*/
- public void scheduleSync(Account requestedAccount, int userId, String requestedAuthority,
- Bundle extras, long delay, boolean onlyThoseWithUnkownSyncableState) {
+ public void scheduleSync(Account requestedAccount, int userId, int reason,
+ String requestedAuthority, Bundle extras, long delay,
+ boolean onlyThoseWithUnkownSyncableState) {
boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
final boolean backgroundDataUsageAllowed = !mBootCompleted ||
@@ -632,8 +650,9 @@ public class SyncManager {
+ ", extras " + newExtras);
}
scheduleSyncOperation(
- new SyncOperation(account.account, account.userId, source, authority,
- newExtras, 0, backoffTime, delayUntil, allowParallelSyncs));
+ new SyncOperation(account.account, account.userId, reason, source,
+ authority, newExtras, 0, backoffTime, delayUntil,
+ allowParallelSyncs));
}
if (!onlyThoseWithUnkownSyncableState) {
if (isLoggable) {
@@ -645,17 +664,18 @@ public class SyncManager {
+ ", extras " + extras);
}
scheduleSyncOperation(
- new SyncOperation(account.account, account.userId, source, authority,
- extras, delay, backoffTime, delayUntil, allowParallelSyncs));
+ new SyncOperation(account.account, account.userId, reason, source,
+ authority, extras, delay, backoffTime, delayUntil,
+ allowParallelSyncs));
}
}
}
}
- public void scheduleLocalSync(Account account, int userId, String authority) {
+ public void scheduleLocalSync(Account account, int userId, int reason, String authority) {
final Bundle extras = new Bundle();
extras.putBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, true);
- scheduleSync(account, userId, authority, extras, LOCAL_SYNC_DELAY,
+ scheduleSync(account, userId, reason, authority, extras, LOCAL_SYNC_DELAY,
false /* onlyThoseWithUnkownSyncableState */);
}
@@ -881,6 +901,7 @@ public class SyncManager {
+ "sync in progress: " + operation);
}
scheduleSyncOperation(new SyncOperation(operation.account, operation.userId,
+ operation.reason,
operation.syncSource,
operation.authority, operation.extras,
DELAY_RETRY_SYNC_IN_PROGRESS_IN_SECONDS * 1000,
@@ -912,8 +933,8 @@ public class SyncManager {
// Schedule sync for any accounts under started user
final Account[] accounts = AccountManagerService.getSingleton().getAccounts(userId);
for (Account account : accounts) {
- scheduleSync(account, userId, null, null, 0 /* no delay */,
- true /* onlyThoseWithUnknownSyncableState */);
+ scheduleSync(account, userId, SyncOperation.REASON_USER_START, null, null,
+ 0 /* no delay */, true /* onlyThoseWithUnknownSyncableState */);
}
sendCheckAlarmsMessage();
@@ -1127,12 +1148,13 @@ public class SyncManager {
pw.println();
pw.println("Active Syncs: " + mActiveSyncContexts.size());
+ final PackageManager pm = mContext.getPackageManager();
for (SyncManager.ActiveSyncContext activeSyncContext : mActiveSyncContexts) {
final long durationInSeconds = (now - activeSyncContext.mStartTime) / 1000;
pw.print(" ");
pw.print(DateUtils.formatElapsedTime(durationInSeconds));
pw.print(" - ");
- pw.print(activeSyncContext.mSyncOperation.dump(false));
+ pw.print(activeSyncContext.mSyncOperation.dump(pm, false));
pw.println();
}
@@ -1147,78 +1169,96 @@ public class SyncManager {
pw.println();
pw.println("Sync Status");
for (AccountAndUser account : accounts) {
- pw.print(" Account "); pw.print(account.account.name);
- pw.print(" u"); pw.print(account.userId);
- pw.print(" "); pw.print(account.account.type);
- pw.println(":");
- for (RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterType :
- mSyncAdapters.getAllServices(account.userId)) {
+ pw.printf("Account %s u%d %s\n",
+ account.account.name, account.userId, account.account.type);
+
+ pw.println("=======================================================================");
+ final PrintTable table = new PrintTable(13);
+ table.set(0, 0,
+ "Authority", // 0
+ "Syncable", // 1
+ "Enabled", // 2
+ "Delay", // 3
+ "Loc", // 4
+ "Poll", // 5
+ "Per", // 6
+ "Serv", // 7
+ "User", // 8
+ "Tot", // 9
+ "Time", // 10
+ "Last Sync", // 11
+ "Periodic" // 12
+ );
+
+ final List<RegisteredServicesCache.ServiceInfo<SyncAdapterType>> sorted =
+ Lists.newArrayList();
+ sorted.addAll(mSyncAdapters.getAllServices(account.userId));
+ Collections.sort(sorted,
+ new Comparator<RegisteredServicesCache.ServiceInfo<SyncAdapterType>>() {
+ @Override
+ public int compare(RegisteredServicesCache.ServiceInfo<SyncAdapterType> lhs,
+ RegisteredServicesCache.ServiceInfo<SyncAdapterType> rhs) {
+ return lhs.type.authority.compareTo(rhs.type.authority);
+ }
+ });
+ for (RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterType : sorted) {
if (!syncAdapterType.type.accountType.equals(account.account.type)) {
continue;
}
-
+ int row = table.getNumRows();
SyncStorageEngine.AuthorityInfo settings =
mSyncStorageEngine.getOrCreateAuthority(
account.account, account.userId, syncAdapterType.type.authority);
SyncStatusInfo status = mSyncStorageEngine.getOrCreateSyncStatus(settings);
- pw.print(" "); pw.print(settings.authority);
- pw.println(":");
- pw.print(" settings:");
- pw.print(" " + (settings.syncable > 0
- ? "syncable"
- : (settings.syncable == 0 ? "not syncable" : "not initialized")));
- pw.print(", " + (settings.enabled ? "enabled" : "disabled"));
- if (settings.delayUntil > now) {
- pw.print(", delay for "
- + ((settings.delayUntil - now) / 1000) + " sec");
+
+ String authority = settings.authority;
+ if (authority.length() > 50) {
+ authority = authority.substring(authority.length() - 50);
}
- if (settings.backoffTime > now) {
- pw.print(", backoff for "
- + ((settings.backoffTime - now) / 1000) + " sec");
+ table.set(row, 0, authority, settings.syncable, settings.enabled);
+ table.set(row, 4,
+ status.numSourceLocal,
+ status.numSourcePoll,
+ status.numSourcePeriodic,
+ status.numSourceServer,
+ status.numSourceUser,
+ status.numSyncs,
+ DateUtils.formatElapsedTime(status.totalElapsedTime / 1000));
+
+
+ for (int i = 0; i < settings.periodicSyncs.size(); i++) {
+ final Pair<Bundle, Long> pair = settings.periodicSyncs.get(0);
+ final String period = String.valueOf(pair.second);
+ final String extras = pair.first.size() > 0 ? pair.first.toString() : "";
+ final String next = formatTime(status.getPeriodicSyncTime(0)
+ + pair.second * 1000);
+ table.set(row + i * 2, 12, period + extras);
+ table.set(row + i * 2 + 1, 12, next);
}
- if (settings.backoffDelay > 0) {
- pw.print(", the backoff increment is " + settings.backoffDelay / 1000
- + " sec");
+
+ int row1 = row;
+ if (settings.delayUntil > now) {
+ table.set(row1++, 12, "D: " + (settings.delayUntil - now) / 1000);
+ if (settings.backoffTime > now) {
+ table.set(row1++, 12, "B: " + (settings.backoffTime - now) / 1000);
+ table.set(row1++, 12, settings.backoffDelay / 1000);
+ }
}
- pw.println();
- for (int periodicIndex = 0;
- periodicIndex < settings.periodicSyncs.size();
- periodicIndex++) {
- Pair<Bundle, Long> info = settings.periodicSyncs.get(periodicIndex);
- long lastPeriodicTime = status.getPeriodicSyncTime(periodicIndex);
- long nextPeriodicTime = lastPeriodicTime + info.second * 1000;
- pw.println(" periodic period=" + info.second
- + ", extras=" + info.first
- + ", next=" + formatTime(nextPeriodicTime));
- }
- pw.print(" count: local="); pw.print(status.numSourceLocal);
- pw.print(" poll="); pw.print(status.numSourcePoll);
- pw.print(" periodic="); pw.print(status.numSourcePeriodic);
- pw.print(" server="); pw.print(status.numSourceServer);
- pw.print(" user="); pw.print(status.numSourceUser);
- pw.print(" total="); pw.print(status.numSyncs);
- pw.println();
- pw.print(" total duration: ");
- pw.println(DateUtils.formatElapsedTime(status.totalElapsedTime/1000));
+
if (status.lastSuccessTime != 0) {
- pw.print(" SUCCESS: source=");
- pw.print(SyncStorageEngine.SOURCES[status.lastSuccessSource]);
- pw.print(" time=");
- pw.println(formatTime(status.lastSuccessTime));
+ table.set(row1++, 11, SyncStorageEngine.SOURCES[status.lastSuccessSource]
+ + " " + "SUCCESS");
+ table.set(row1++, 11, formatTime(status.lastSuccessTime));
}
if (status.lastFailureTime != 0) {
- pw.print(" FAILURE: source=");
- pw.print(SyncStorageEngine.SOURCES[
- status.lastFailureSource]);
- pw.print(" initialTime=");
- pw.print(formatTime(status.initialFailureTime));
- pw.print(" lastTime=");
- pw.println(formatTime(status.lastFailureTime));
- int errCode = status.getLastFailureMesgAsInt(0);
- pw.print(" message: "); pw.println(
- getLastFailureMessage(errCode) + " (" + errCode + ")");
+ table.set(row1++, 11, SyncStorageEngine.SOURCES[status.lastFailureSource]
+ + " " + "FAILURE");
+ table.set(row1++, 11, formatTime(status.lastFailureTime));
+ //noinspection UnusedAssignment
+ table.set(row1++, 11, status.lastFailureMesg);
}
}
+ table.writeTo(pw);
}
}
@@ -1412,9 +1452,9 @@ public class SyncManager {
pw.println();
pw.println("Recent Sync History");
- final String format = " %-" + maxAccount + "s %s\n";
+ final String format = " %-" + maxAccount + "s %-" + maxAuthority + "s %s\n";
final Map<String, Long> lastTimeMap = Maps.newHashMap();
-
+ final PackageManager pm = mContext.getPackageManager();
for (int i = 0; i < N; i++) {
SyncStorageEngine.SyncHistoryItem item = items.get(i);
SyncStorageEngine.AuthorityInfo authority
@@ -1459,7 +1499,8 @@ public class SyncManager {
SyncStorageEngine.SOURCES[item.source],
((float) elapsedTime) / 1000,
diffString);
- pw.printf(format, accountKey, authorityName);
+ pw.printf(format, accountKey, authorityName,
+ SyncOperation.reasonToString(pm, item.reason));
if (item.event != SyncStorageEngine.EVENT_STOP
|| item.upstreamActivity != 0
@@ -1474,6 +1515,37 @@ public class SyncManager {
pw.printf(" mesg=%s\n", item.mesg);
}
}
+ pw.println();
+ pw.println("Recent Sync History Extras");
+ for (int i = 0; i < N; i++) {
+ final SyncStorageEngine.SyncHistoryItem item = items.get(i);
+ final Bundle extras = item.extras;
+ if (extras == null || extras.size() == 0) {
+ continue;
+ }
+ final SyncStorageEngine.AuthorityInfo authority
+ = mSyncStorageEngine.getAuthority(item.authorityId);
+ final String authorityName;
+ final String accountKey;
+ if (authority != null) {
+ authorityName = authority.authority;
+ accountKey = authority.account.name + "/" + authority.account.type
+ + " u" + authority.userId;
+ } else {
+ authorityName = "Unknown";
+ accountKey = "Unknown";
+ }
+ final Time time = new Time();
+ final long eventTime = item.eventTime;
+ time.set(eventTime);
+
+ pw.printf(" #%-3d: %s %8s ",
+ i + 1,
+ formatTime(eventTime),
+ SyncStorageEngine.SOURCES[item.source]);
+
+ pw.printf(format, accountKey, authorityName, extras);
+ }
}
}
@@ -1888,6 +1960,7 @@ public class SyncManager {
}
scheduleSyncOperation(
new SyncOperation(info.account, info.userId,
+ SyncOperation.REASON_PERIODIC,
SyncStorageEngine.SOURCE_PERIODIC,
info.authority, extras, 0 /* delay */,
backoff != null ? backoff.first : 0,
@@ -2289,7 +2362,8 @@ public class SyncManager {
}
// reschedule the sync if so indicated by the syncResult
maybeRescheduleSync(syncResult, syncOperation);
- historyMessage = Integer.toString(syncResultToErrorNumber(syncResult));
+ historyMessage = ContentResolver.syncErrorToString(
+ syncResultToErrorNumber(syncResult));
// TODO: set these correctly when the SyncResult is extended to include it
downstreamActivity = 0;
upstreamActivity = 0;
@@ -2327,6 +2401,7 @@ public class SyncManager {
if (syncResult != null && syncResult.fullSyncRequested) {
scheduleSyncOperation(new SyncOperation(syncOperation.account, syncOperation.userId,
+ syncOperation.reason,
syncOperation.syncSource, syncOperation.authority, new Bundle(), 0,
syncOperation.backoff, syncOperation.delayUntil,
syncOperation.allowParallelSyncs));
@@ -2606,8 +2681,10 @@ public class SyncManager {
syncOperation.account.name.hashCode());
return mSyncStorageEngine.insertStartSyncEvent(
- syncOperation.account, syncOperation.userId, syncOperation.authority,
- now, source, syncOperation.isInitialization());
+ syncOperation.account, syncOperation.userId, syncOperation.reason,
+ syncOperation.authority,
+ now, source, syncOperation.isInitialization(), syncOperation.extras
+ );
}
public void stopSyncEvent(long rowId, SyncOperation syncOperation, String resultMessage,
@@ -2629,4 +2706,66 @@ public class SyncManager {
}
return false;
}
+
+ static class PrintTable {
+ private ArrayList<Object[]> mTable = Lists.newArrayList();
+ private final int mCols;
+
+ PrintTable(int cols) {
+ mCols = cols;
+ }
+
+ void set(int row, int col, Object... values) {
+ if (col + values.length > mCols) {
+ throw new IndexOutOfBoundsException("Table only has " + mCols +
+ " columns. can't set " + values.length + " at column " + col);
+ }
+ for (int i = mTable.size(); i <= row; i++) {
+ final Object[] list = new Object[mCols];
+ mTable.add(list);
+ for (int j = 0; j < mCols; j++) {
+ list[j] = "";
+ }
+ }
+ System.arraycopy(values, 0, mTable.get(row), col, values.length);
+ }
+
+ void writeTo(PrintWriter out) {
+ final String[] formats = new String[mCols];
+ int totalLength = 0;
+ for (int col = 0; col < mCols; ++col) {
+ int maxLength = 0;
+ for (Object[] row : mTable) {
+ final int length = row[col].toString().length();
+ if (length > maxLength) {
+ maxLength = length;
+ }
+ }
+ totalLength += maxLength;
+ formats[col] = String.format("%%-%ds", maxLength);
+ }
+ printRow(out, formats, mTable.get(0));
+ totalLength += (mCols - 1) * 2;
+ for (int i = 0; i < totalLength; ++i) {
+ out.print("-");
+ }
+ out.println();
+ for (int i = 1, mTableSize = mTable.size(); i < mTableSize; i++) {
+ Object[] row = mTable.get(i);
+ printRow(out, formats, row);
+ }
+ }
+
+ private void printRow(PrintWriter out, String[] formats, Object[] row) {
+ for (int j = 0, rowLength = row.length; j < rowLength; j++) {
+ out.printf(String.format(formats[j], row[j].toString()));
+ out.print(" ");
+ }
+ out.println();
+ }
+
+ public int getNumRows() {
+ return mTable.size();
+ }
+ }
}
diff --git a/core/java/android/content/SyncOperation.java b/core/java/android/content/SyncOperation.java
index 6611fcd..a4c2cff 100644
--- a/core/java/android/content/SyncOperation.java
+++ b/core/java/android/content/SyncOperation.java
@@ -17,6 +17,7 @@
package android.content;
import android.accounts.Account;
+import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.SystemClock;
@@ -25,8 +26,29 @@ import android.os.SystemClock;
* @hide
*/
public class SyncOperation implements Comparable {
+ public static final int REASON_BACKGROUND_DATA_SETTINGS_CHANGED = -1;
+ public static final int REASON_ACCOUNTS_UPDATED = -2;
+ public static final int REASON_SERVICE_CHANGED = -3;
+ public static final int REASON_PERIODIC = -4;
+ public static final int REASON_IS_SYNCABLE = -5;
+ public static final int REASON_SYNC_AUTO = -6;
+ public static final int REASON_MASTER_SYNC_AUTO = -7;
+ public static final int REASON_USER_START = -8;
+
+ private static String[] REASON_NAMES = new String[] {
+ "DataSettingsChanged",
+ "AccountsUpdated",
+ "ServiceChanged",
+ "Periodic",
+ "IsSyncable",
+ "AutoSync",
+ "MasterSyncAuto",
+ "UserStart",
+ };
+
public final Account account;
public final int userId;
+ public final int reason;
public int syncSource;
public String authority;
public final boolean allowParallelSyncs;
@@ -39,10 +61,12 @@ public class SyncOperation implements Comparable {
public long delayUntil;
public long effectiveRunTime;
- public SyncOperation(Account account, int userId, int source, String authority, Bundle extras,
- long delayInMs, long backoff, long delayUntil, boolean allowParallelSyncs) {
+ public SyncOperation(Account account, int userId, int reason, int source, String authority,
+ Bundle extras, long delayInMs, long backoff, long delayUntil,
+ boolean allowParallelSyncs) {
this.account = account;
this.userId = userId;
+ this.reason = reason;
this.syncSource = source;
this.authority = authority;
this.allowParallelSyncs = allowParallelSyncs;
@@ -78,6 +102,7 @@ public class SyncOperation implements Comparable {
SyncOperation(SyncOperation other) {
this.account = other.account;
this.userId = other.userId;
+ this.reason = other.reason;
this.syncSource = other.syncSource;
this.authority = other.authority;
this.extras = new Bundle(other.extras);
@@ -91,10 +116,10 @@ public class SyncOperation implements Comparable {
}
public String toString() {
- return dump(true);
+ return dump(null, true);
}
- public String dump(boolean useOneLine) {
+ public String dump(PackageManager pm, boolean useOneLine) {
StringBuilder sb = new StringBuilder()
.append(account.name)
.append(" u")
@@ -110,6 +135,8 @@ public class SyncOperation implements Comparable {
if (expedited) {
sb.append(", EXPEDITED");
}
+ sb.append(", reason: ");
+ sb.append(reasonToString(pm, reason));
if (!useOneLine && !extras.keySet().isEmpty()) {
sb.append("\n ");
extrasToStringBuilder(extras, sb);
@@ -117,6 +144,31 @@ public class SyncOperation implements Comparable {
return sb.toString();
}
+ public static String reasonToString(PackageManager pm, int reason) {
+ if (reason >= 0) {
+ if (pm != null) {
+ final String[] packages = pm.getPackagesForUid(reason);
+ if (packages != null && packages.length == 1) {
+ return packages[0];
+ }
+ final String name = pm.getNameForUid(reason);
+ if (name != null) {
+ return name;
+ }
+ return String.valueOf(reason);
+ } else {
+ return String.valueOf(reason);
+ }
+ } else {
+ final int index = -reason - 1;
+ if (index >= REASON_NAMES.length) {
+ return String.valueOf(reason);
+ } else {
+ return REASON_NAMES[index];
+ }
+ }
+ }
+
public boolean isInitialization() {
return extras.getBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, false);
}
diff --git a/core/java/android/content/SyncQueue.java b/core/java/android/content/SyncQueue.java
index c9a325e..c09703c 100644
--- a/core/java/android/content/SyncQueue.java
+++ b/core/java/android/content/SyncQueue.java
@@ -17,6 +17,8 @@
package android.content;
import android.accounts.Account;
+import android.content.pm.PackageManager;
+import android.content.pm.RegisteredServicesCache;
import android.content.pm.RegisteredServicesCache.ServiceInfo;
import android.os.SystemClock;
import android.os.UserHandle;
@@ -40,15 +42,17 @@ import java.util.Map;
*/
public class SyncQueue {
private static final String TAG = "SyncManager";
-
private final SyncStorageEngine mSyncStorageEngine;
private final SyncAdaptersCache mSyncAdapters;
+ private final PackageManager mPackageManager;
// A Map of SyncOperations operationKey -> SyncOperation that is designed for
// quick lookup of an enqueued SyncOperation.
private final HashMap<String, SyncOperation> mOperationsMap = Maps.newHashMap();
- public SyncQueue(SyncStorageEngine syncStorageEngine, final SyncAdaptersCache syncAdapters) {
+ public SyncQueue(PackageManager packageManager, SyncStorageEngine syncStorageEngine,
+ final SyncAdaptersCache syncAdapters) {
+ mPackageManager = packageManager;
mSyncStorageEngine = syncStorageEngine;
mSyncAdapters = syncAdapters;
}
@@ -67,8 +71,8 @@ public class SyncQueue {
continue;
}
SyncOperation syncOperation = new SyncOperation(
- op.account, op.userId, op.syncSource, op.authority, op.extras, 0 /* delay */,
- backoff != null ? backoff.first : 0,
+ op.account, op.userId, op.reason, op.syncSource, op.authority, op.extras,
+ 0 /* delay */, backoff != null ? backoff.first : 0,
mSyncStorageEngine.getDelayUntilTime(op.account, op.userId, op.authority),
syncAdapterInfo.type.allowParallelSyncs());
syncOperation.expedited = op.expedited;
@@ -112,7 +116,7 @@ public class SyncQueue {
operation.pendingOperation = pop;
if (operation.pendingOperation == null) {
pop = new SyncStorageEngine.PendingOperation(
- operation.account, operation.userId, operation.syncSource,
+ operation.account, operation.userId, operation.reason, operation.syncSource,
operation.authority, operation.extras, operation.expedited);
pop = mSyncStorageEngine.insertIntoPending(pop);
if (pop == null) {
@@ -214,7 +218,7 @@ public class SyncQueue {
sb.append(DateUtils.formatElapsedTime((operation.effectiveRunTime - now) / 1000));
}
sb.append(" - ");
- sb.append(operation.dump(false)).append("\n");
+ sb.append(operation.dump(mPackageManager, false)).append("\n");
}
}
}
diff --git a/core/java/android/content/SyncStorageEngine.java b/core/java/android/content/SyncStorageEngine.java
index 1ecab09..8d9b8e0 100644
--- a/core/java/android/content/SyncStorageEngine.java
+++ b/core/java/android/content/SyncStorageEngine.java
@@ -26,7 +26,6 @@ import org.xmlpull.v1.XmlSerializer;
import android.accounts.Account;
import android.accounts.AccountAndUser;
-import android.content.res.Resources;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
@@ -147,6 +146,7 @@ public class SyncStorageEngine extends Handler {
public static class PendingOperation {
final Account account;
final int userId;
+ final int reason;
final int syncSource;
final String authority;
final Bundle extras; // note: read-only.
@@ -155,11 +155,12 @@ public class SyncStorageEngine extends Handler {
int authorityId;
byte[] flatExtras;
- PendingOperation(Account account, int userId, int source,
+ PendingOperation(Account account, int userId, int reason,int source,
String authority, Bundle extras, boolean expedited) {
this.account = account;
this.userId = userId;
this.syncSource = source;
+ this.reason = reason;
this.authority = authority;
this.extras = extras != null ? new Bundle(extras) : extras;
this.expedited = expedited;
@@ -169,6 +170,7 @@ public class SyncStorageEngine extends Handler {
PendingOperation(PendingOperation other) {
this.account = other.account;
this.userId = other.userId;
+ this.reason = other.reason;
this.syncSource = other.syncSource;
this.authority = other.authority;
this.extras = other.extras;
@@ -247,6 +249,8 @@ public class SyncStorageEngine extends Handler {
long downstreamActivity;
String mesg;
boolean initialization;
+ Bundle extras;
+ int reason;
}
public static class DayStats {
@@ -266,10 +270,12 @@ public class SyncStorageEngine extends Handler {
* Called when a sync is needed on an account(s) due to some change in state.
* @param account
* @param userId
+ * @param reason
* @param authority
* @param extras
*/
- public void onSyncRequest(Account account, int userId, String authority, Bundle extras);
+ public void onSyncRequest(Account account, int userId, int reason, String authority,
+ Bundle extras);
}
// Primary list of all syncable authorities. Also our global lock.
@@ -503,7 +509,8 @@ public class SyncStorageEngine extends Handler {
}
if (sync) {
- requestSync(account, userId, providerName, new Bundle());
+ requestSync(account, userId, SyncOperation.REASON_SYNC_AUTO, providerName,
+ new Bundle());
}
reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
}
@@ -555,7 +562,8 @@ public class SyncStorageEngine extends Handler {
}
if (syncable > 0) {
- requestSync(account, userId, providerName, new Bundle());
+ requestSync(account, userId, SyncOperation.REASON_IS_SYNCABLE, providerName,
+ new Bundle());
}
reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
}
@@ -788,7 +796,8 @@ public class SyncStorageEngine extends Handler {
writeAccountInfoLocked();
}
if (flag) {
- requestSync(null, userId, null, new Bundle());
+ requestSync(null, userId, SyncOperation.REASON_MASTER_SYNC_AUTO, null,
+ new Bundle());
}
reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
mContext.sendBroadcast(SYNC_CONNECTION_SETTING_CHANGED_INTENT);
@@ -1051,8 +1060,8 @@ public class SyncStorageEngine extends Handler {
/**
* Note that sync has started for the given account and authority.
*/
- public long insertStartSyncEvent(Account accountName, int userId, String authorityName,
- long now, int source, boolean initialization) {
+ public long insertStartSyncEvent(Account accountName, int userId, int reason,
+ String authorityName, long now, int source, boolean initialization, Bundle extras) {
long id;
synchronized (mAuthorities) {
if (DEBUG) {
@@ -1071,6 +1080,8 @@ public class SyncStorageEngine extends Handler {
if (mNextHistoryId < 0) mNextHistoryId = 0;
item.eventTime = now;
item.source = source;
+ item.reason = reason;
+ item.extras = extras;
item.event = EVENT_START;
mSyncHistory.add(0, item);
while (mSyncHistory.size() > MAX_HISTORY) {
@@ -2046,7 +2057,7 @@ public class SyncStorageEngine extends Handler {
}
}
- public static final int PENDING_OPERATION_VERSION = 2;
+ public static final int PENDING_OPERATION_VERSION = 3;
/**
* Read all pending operations back in to the initial engine state.
@@ -2075,6 +2086,7 @@ public class SyncStorageEngine extends Handler {
} else {
expedited = false;
}
+ int reason = in.readInt();
AuthorityInfo authority = mAuthorities.get(authorityId);
if (authority != null) {
Bundle extras;
@@ -2086,13 +2098,14 @@ public class SyncStorageEngine extends Handler {
extras = new Bundle();
}
PendingOperation op = new PendingOperation(
- authority.account, authority.userId, syncSource,
+ authority.account, authority.userId, reason, syncSource,
authority.authority, extras, expedited);
op.authorityId = authorityId;
op.flatExtras = flatExtras;
if (DEBUG_FILE) Log.v(TAG, "Adding pending op: account=" + op.account
+ " auth=" + op.authority
+ " src=" + op.syncSource
+ + " reason=" + op.reason
+ " expedited=" + op.expedited
+ " extras=" + op.extras);
mPendingOperations.add(op);
@@ -2112,6 +2125,7 @@ public class SyncStorageEngine extends Handler {
}
out.writeByteArray(op.flatExtras);
out.writeInt(op.expedited ? 1 : 0);
+ out.writeInt(op.reason);
}
/**
@@ -2206,14 +2220,15 @@ public class SyncStorageEngine extends Handler {
return bundle;
}
- private void requestSync(Account account, int userId, String authority, Bundle extras) {
+ private void requestSync(Account account, int userId, int reason, String authority,
+ Bundle extras) {
// If this is happening in the system process, then call the syncrequest listener
// to make a request back to the SyncManager directly.
// If this is probably a test instance, then call back through the ContentResolver
// which will know which userId to apply based on the Binder id.
if (android.os.Process.myUid() == android.os.Process.SYSTEM_UID
&& mSyncRequestListener != null) {
- mSyncRequestListener.onSyncRequest(account, userId, authority, extras);
+ mSyncRequestListener.onSyncRequest(account, userId, reason, authority, extras);
} else {
ContentResolver.requestSync(account, authority, extras);
}
diff --git a/core/java/android/content/res/StringBlock.java b/core/java/android/content/res/StringBlock.java
index 63e33ce..78180b1 100644
--- a/core/java/android/content/res/StringBlock.java
+++ b/core/java/android/content/res/StringBlock.java
@@ -16,6 +16,7 @@
package android.content.res;
+import android.graphics.Color;
import android.text.*;
import android.text.style.*;
import android.util.Log;
@@ -24,7 +25,7 @@ import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Typeface;
-import com.android.internal.util.XmlUtils;
+import java.util.Arrays;
/**
* Conveniences for retrieving data out of a compiled string resource.
@@ -33,7 +34,7 @@ import com.android.internal.util.XmlUtils;
*/
final class StringBlock {
private static final String TAG = "AssetManager";
- private static final boolean localLOGV = false || false;
+ private static final boolean localLOGV = false;
private final int mNative;
private final boolean mUseSparse;
@@ -82,7 +83,7 @@ final class StringBlock {
CharSequence res = str;
int[] style = nativeGetStyle(mNative, idx);
if (localLOGV) Log.v(TAG, "Got string: " + str);
- if (localLOGV) Log.v(TAG, "Got styles: " + style);
+ if (localLOGV) Log.v(TAG, "Got styles: " + Arrays.toString(style));
if (style != null) {
if (mStyleIDs == null) {
mStyleIDs = new StyleIDs();
@@ -139,8 +140,12 @@ final class StringBlock {
}
protected void finalize() throws Throwable {
- if (mOwnsNative) {
- nativeDestroy(mNative);
+ try {
+ super.finalize();
+ } finally {
+ if (mOwnsNative) {
+ nativeDestroy(mNative);
+ }
}
}
@@ -236,19 +241,31 @@ final class StringBlock {
sub = subtag(tag, ";fgcolor=");
if (sub != null) {
- int color = XmlUtils.convertValueToUnsignedInt(sub, -1);
- buffer.setSpan(new ForegroundColorSpan(color),
+ buffer.setSpan(getColor(sub, true),
style[i+1], style[i+2]+1,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
+ sub = subtag(tag, ";color=");
+ if (sub != null) {
+ buffer.setSpan(getColor(sub, true),
+ style[i+1], style[i+2]+1,
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+
sub = subtag(tag, ";bgcolor=");
if (sub != null) {
- int color = XmlUtils.convertValueToUnsignedInt(sub, -1);
- buffer.setSpan(new BackgroundColorSpan(color),
+ buffer.setSpan(getColor(sub, false),
style[i+1], style[i+2]+1,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
+
+ sub = subtag(tag, ";face=");
+ if (sub != null) {
+ buffer.setSpan(new TypefaceSpan(sub),
+ style[i+1], style[i+2]+1,
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
} else if (tag.startsWith("a;")) {
String sub;
@@ -289,6 +306,48 @@ final class StringBlock {
}
/**
+ * Returns a span for the specified color string representation.
+ * If the specified string does not represent a color (null, empty, etc.)
+ * the color black is returned instead.
+ *
+ * @param color The color as a string. Can be a resource reference,
+ * HTML hexadecimal, octal or a name
+ * @param foreground True if the color will be used as the foreground color,
+ * false otherwise
+ *
+ * @return A CharacterStyle
+ *
+ * @see Color#getHtmlColor(String)
+ */
+ private static CharacterStyle getColor(String color, boolean foreground) {
+ int c = 0xff000000;
+
+ if (!TextUtils.isEmpty(color)) {
+ if (color.startsWith("@")) {
+ Resources res = Resources.getSystem();
+ String name = color.substring(1);
+ int colorRes = res.getIdentifier(name, "color", "android");
+ if (colorRes != 0) {
+ ColorStateList colors = res.getColorStateList(colorRes);
+ if (foreground) {
+ return new TextAppearanceSpan(null, 0, 0, colors, null);
+ } else {
+ c = colors.getDefaultColor();
+ }
+ }
+ } else {
+ c = Color.getHtmlColor(color);
+ }
+ }
+
+ if (foreground) {
+ return new ForegroundColorSpan(c);
+ } else {
+ return new BackgroundColorSpan(c);
+ }
+ }
+
+ /**
* If a translator has messed up the edges of paragraph-level markup,
* fix it to actually cover the entire paragraph that it is attached to
* instead of just whatever range they put it on.
@@ -423,11 +482,11 @@ final class StringBlock {
+ ": " + nativeGetSize(mNative));
}
- private static final native int nativeCreate(byte[] data,
+ private static native int nativeCreate(byte[] data,
int offset,
int size);
- private static final native int nativeGetSize(int obj);
- private static final native String nativeGetString(int obj, int idx);
- private static final native int[] nativeGetStyle(int obj, int idx);
- private static final native void nativeDestroy(int obj);
+ private static native int nativeGetSize(int obj);
+ private static native String nativeGetString(int obj, int idx);
+ private static native int[] nativeGetStyle(int obj, int idx);
+ private static native void nativeDestroy(int obj);
}
diff --git a/core/java/android/hardware/SerialPort.java b/core/java/android/hardware/SerialPort.java
index 5ef122b..f50cdef 100644
--- a/core/java/android/hardware/SerialPort.java
+++ b/core/java/android/hardware/SerialPort.java
@@ -82,7 +82,9 @@ public class SerialPort {
}
/**
- * Reads data into the provided buffer
+ * Reads data into the provided buffer.
+ * Note that the value returned by {@link java.nio.Buffer#position()} on this buffer is
+ * unchanged after a call to this method.
*
* @param buffer to read into
* @return number of bytes read
@@ -98,7 +100,9 @@ public class SerialPort {
}
/**
- * Writes data from provided buffer
+ * Writes data from provided buffer.
+ * Note that the value returned by {@link java.nio.Buffer#position()} on this buffer is
+ * unchanged after a call to this method.
*
* @param buffer to write
* @param length number of bytes to write
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 6f1cc94..99624cc 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -1955,7 +1955,7 @@ public class InputMethodService extends AbstractInputMethodService {
ic.sendKeyEvent(new KeyEvent(eventTime, eventTime,
KeyEvent.ACTION_DOWN, keyEventCode, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE));
- ic.sendKeyEvent(new KeyEvent(SystemClock.uptimeMillis(), eventTime,
+ ic.sendKeyEvent(new KeyEvent(eventTime, SystemClock.uptimeMillis(),
KeyEvent.ACTION_UP, keyEventCode, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE));
}
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 6ff1a33..a8a68d0 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -328,6 +328,18 @@ public class ConnectivityManager {
/** {@hide} */
public static final int MAX_NETWORK_TYPE = TYPE_WIFI_P2P;
+ /**
+ * If you want to set the default network preference,you can directly
+ * change the networkAttributes array in framework's config.xml.
+ *
+ * @deprecated Since we support so many more networks now, the single
+ * network default network preference can't really express
+ * the heirarchy. Instead, the default is defined by the
+ * networkAttributes in config.xml. You can determine
+ * the current value by calling {@link #getNetworkPreference()}
+ * from an App.
+ */
+ @Deprecated
public static final int DEFAULT_NETWORK_PREFERENCE = TYPE_WIFI;
/**
diff --git a/core/java/android/net/DhcpInfo.java b/core/java/android/net/DhcpInfo.java
index e2660e4..2b359eb 100644
--- a/core/java/android/net/DhcpInfo.java
+++ b/core/java/android/net/DhcpInfo.java
@@ -22,16 +22,17 @@ import java.net.InetAddress;
/**
* A simple object for retrieving the results of a DHCP request.
+ * @deprecated - use LinkProperties - To be removed 11/2013
+ * STOPSHIP - make sure we expose LinkProperties through ConnectivityManager
*/
public class DhcpInfo implements Parcelable {
public int ipAddress;
public int gateway;
public int netmask;
-
public int dns1;
public int dns2;
-
public int serverAddress;
+
public int leaseDuration;
public DhcpInfo() {
diff --git a/core/java/android/net/DhcpInfoInternal.java b/core/java/android/net/DhcpInfoInternal.java
deleted file mode 100644
index f3508c1..0000000
--- a/core/java/android/net/DhcpInfoInternal.java
+++ /dev/null
@@ -1,166 +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.net;
-
-import android.text.TextUtils;
-import android.util.Log;
-
-import java.net.Inet4Address;
-import java.net.InetAddress;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-
-/**
- * A simple object for retrieving the results of a DHCP request.
- * Replaces (internally) the IPv4-only DhcpInfo class.
- * @hide
- */
-public class DhcpInfoInternal {
- private final static String TAG = "DhcpInfoInternal";
- public String ipAddress;
- public int prefixLength;
-
- public String dns1;
- public String dns2;
-
- public String serverAddress;
- public int leaseDuration;
-
- /**
- * Vendor specific information (from RFC 2132).
- */
- public String vendorInfo;
-
- private Collection<RouteInfo> mRoutes;
-
- public DhcpInfoInternal() {
- mRoutes = new ArrayList<RouteInfo>();
- }
-
- public void addRoute(RouteInfo routeInfo) {
- mRoutes.add(routeInfo);
- }
-
- public Collection<RouteInfo> getRoutes() {
- return Collections.unmodifiableCollection(mRoutes);
- }
-
- private int convertToInt(String addr) {
- if (addr != null) {
- try {
- InetAddress inetAddress = NetworkUtils.numericToInetAddress(addr);
- if (inetAddress instanceof Inet4Address) {
- return NetworkUtils.inetAddressToInt(inetAddress);
- }
- } catch (IllegalArgumentException e) {}
- }
- return 0;
- }
-
- public DhcpInfo makeDhcpInfo() {
- DhcpInfo info = new DhcpInfo();
- info.ipAddress = convertToInt(ipAddress);
- for (RouteInfo route : mRoutes) {
- if (route.isDefaultRoute()) {
- info.gateway = convertToInt(route.getGateway().getHostAddress());
- break;
- }
- }
- try {
- InetAddress inetAddress = NetworkUtils.numericToInetAddress(ipAddress);
- info.netmask = NetworkUtils.prefixLengthToNetmaskInt(prefixLength);
- } catch (IllegalArgumentException e) {}
- info.dns1 = convertToInt(dns1);
- info.dns2 = convertToInt(dns2);
- info.serverAddress = convertToInt(serverAddress);
- info.leaseDuration = leaseDuration;
- return info;
- }
-
- public LinkAddress makeLinkAddress() {
- if (TextUtils.isEmpty(ipAddress)) {
- Log.e(TAG, "makeLinkAddress with empty ipAddress");
- return null;
- }
- return new LinkAddress(NetworkUtils.numericToInetAddress(ipAddress), prefixLength);
- }
-
- public LinkProperties makeLinkProperties() {
- LinkProperties p = new LinkProperties();
- p.addLinkAddress(makeLinkAddress());
- for (RouteInfo route : mRoutes) {
- p.addRoute(route);
- }
- //if empty, connectivity configures default DNS
- if (TextUtils.isEmpty(dns1) == false) {
- p.addDns(NetworkUtils.numericToInetAddress(dns1));
- } else {
- Log.d(TAG, "makeLinkProperties with empty dns1!");
- }
- if (TextUtils.isEmpty(dns2) == false) {
- p.addDns(NetworkUtils.numericToInetAddress(dns2));
- } else {
- Log.d(TAG, "makeLinkProperties with empty dns2!");
- }
- return p;
- }
-
- /* Updates the DHCP fields that need to be retained from
- * original DHCP request if the DHCP renewal shows them as
- * being empty
- */
- public void updateFromDhcpRequest(DhcpInfoInternal orig) {
- if (orig == null) return;
-
- if (TextUtils.isEmpty(dns1)) {
- dns1 = orig.dns1;
- }
-
- if (TextUtils.isEmpty(dns2)) {
- dns2 = orig.dns2;
- }
-
- if (mRoutes.size() == 0) {
- for (RouteInfo route : orig.getRoutes()) {
- addRoute(route);
- }
- }
- }
-
- /**
- * Test if this DHCP lease includes vendor hint that network link is
- * metered, and sensitive to heavy data transfers.
- */
- public boolean hasMeteredHint() {
- if (vendorInfo != null) {
- return vendorInfo.contains("ANDROID_METERED");
- } else {
- return false;
- }
- }
-
- public String toString() {
- String routeString = "";
- for (RouteInfo route : mRoutes) routeString += route.toString() + " | ";
- return "addr: " + ipAddress + "/" + prefixLength +
- " mRoutes: " + routeString +
- " dns: " + dns1 + "," + dns2 +
- " dhcpServer: " + serverAddress +
- " leaseDuration: " + leaseDuration;
- }
-}
diff --git a/core/java/android/util/Pool.java b/core/java/android/net/DhcpResults.aidl
index 8cd4f3e..f4db3c3 100644
--- a/core/java/android/util/Pool.java
+++ b/core/java/android/net/DhcpResults.aidl
@@ -1,11 +1,11 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
+/**
+ * Copyright (c) 2012, 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
+ * 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,
@@ -14,12 +14,6 @@
* limitations under the License.
*/
-package android.util;
+package android.net;
-/**
- * @hide
- */
-public interface Pool<T extends Poolable<T>> {
- public abstract T acquire();
- public abstract void release(T element);
-}
+parcelable DhcpResults;
diff --git a/core/java/android/net/DhcpResults.java b/core/java/android/net/DhcpResults.java
new file mode 100644
index 0000000..2f300de
--- /dev/null
+++ b/core/java/android/net/DhcpResults.java
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2012 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.net;
+
+import android.os.Parcelable;
+import android.os.Parcel;
+import android.util.Log;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+
+/**
+ * A simple object for retrieving the results of a DHCP request.
+ * Optimized (attempted) for that jni interface
+ * TODO - remove when DhcpInfo is deprecated. Move the remaining api to LinkProperties.
+ * @hide
+ */
+public class DhcpResults implements Parcelable {
+ private static final String TAG = "DhcpResults";
+
+ public final LinkProperties linkProperties;
+
+ public InetAddress serverAddress;
+
+ /**
+ * Vendor specific information (from RFC 2132).
+ */
+ public String vendorInfo;
+
+ public int leaseDuration;
+
+ public DhcpResults() {
+ linkProperties = new LinkProperties();
+ }
+
+ /** copy constructor */
+ public DhcpResults(DhcpResults source) {
+ if (source != null) {
+ linkProperties = new LinkProperties(source.linkProperties);
+ serverAddress = source.serverAddress;
+ leaseDuration = source.leaseDuration;
+ vendorInfo = source.vendorInfo;
+ } else {
+ linkProperties = new LinkProperties();
+ }
+ }
+
+ public DhcpResults(LinkProperties lp) {
+ linkProperties = new LinkProperties(lp);
+ }
+
+ /**
+ * Updates the DHCP fields that need to be retained from
+ * original DHCP request if the current renewal shows them
+ * being empty.
+ */
+ public void updateFromDhcpRequest(DhcpResults orig) {
+ if (orig == null || orig.linkProperties == null) return;
+ if (linkProperties.getRoutes().size() == 0) {
+ for (RouteInfo r : orig.linkProperties.getRoutes()) linkProperties.addRoute(r);
+ }
+ if (linkProperties.getDnses().size() == 0) {
+ for (InetAddress d : orig.linkProperties.getDnses()) linkProperties.addDns(d);
+ }
+ }
+
+ /**
+ * Test if this DHCP lease includes vendor hint that network link is
+ * metered, and sensitive to heavy data transfers.
+ */
+ public boolean hasMeteredHint() {
+ if (vendorInfo != null) {
+ return vendorInfo.contains("ANDROID_METERED");
+ } else {
+ return false;
+ }
+ }
+
+ public void clear() {
+ linkProperties.clear();
+ serverAddress = null;
+ vendorInfo = null;
+ leaseDuration = 0;
+ }
+
+ @Override
+ public String toString() {
+ StringBuffer str = new StringBuffer(linkProperties.toString());
+
+ str.append(" DHCP server ").append(serverAddress);
+ str.append(" Vendor info ").append(vendorInfo);
+ str.append(" lease ").append(leaseDuration).append(" seconds");
+
+ return str.toString();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+
+ if (!(obj instanceof DhcpResults)) return false;
+
+ DhcpResults target = (DhcpResults)obj;
+
+ if (linkProperties == null) {
+ if (target.linkProperties != null) return false;
+ } else if (!linkProperties.equals(target.linkProperties)) return false;
+ if (serverAddress == null) {
+ if (target.serverAddress != null) return false;
+ } else if (!serverAddress.equals(target.serverAddress)) return false;
+ if (vendorInfo == null) {
+ if (target.vendorInfo != null) return false;
+ } else if (!vendorInfo.equals(target.vendorInfo)) return false;
+ if (leaseDuration != target.leaseDuration) return false;
+
+ return true;
+ }
+
+ /** Implement the Parcelable interface */
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Implement the Parcelable interface */
+ public void writeToParcel(Parcel dest, int flags) {
+ linkProperties.writeToParcel(dest, flags);
+
+ dest.writeInt(leaseDuration);
+
+ if (serverAddress != null) {
+ dest.writeByte((byte)1);
+ dest.writeByteArray(serverAddress.getAddress());
+ } else {
+ dest.writeByte((byte)0);
+ }
+
+ dest.writeString(vendorInfo);
+ }
+
+ /** Implement the Parcelable interface */
+ public static final Creator<DhcpResults> CREATOR =
+ new Creator<DhcpResults>() {
+ public DhcpResults createFromParcel(Parcel in) {
+ DhcpResults prop = new DhcpResults((LinkProperties)in.readParcelable(null));
+
+ prop.leaseDuration = in.readInt();
+
+ if (in.readByte() == 1) {
+ try {
+ prop.serverAddress = InetAddress.getByAddress(in.createByteArray());
+ } catch (UnknownHostException e) {}
+ }
+
+ prop.vendorInfo = in.readString();
+
+ return prop;
+ }
+
+ public DhcpResults[] newArray(int size) {
+ return new DhcpResults[size];
+ }
+ };
+
+ // Utils for jni population - false on success
+ public void setInterfaceName(String interfaceName) {
+ linkProperties.setInterfaceName(interfaceName);
+ }
+
+ public boolean addLinkAddress(String addrString, int prefixLength) {
+ InetAddress addr;
+ try {
+ addr = NetworkUtils.numericToInetAddress(addrString);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "addLinkAddress failed with addrString " + addrString);
+ return true;
+ }
+
+ LinkAddress linkAddress = new LinkAddress(addr, prefixLength);
+ linkProperties.addLinkAddress(linkAddress);
+
+ RouteInfo routeInfo = new RouteInfo(linkAddress);
+ linkProperties.addRoute(routeInfo);
+ return false;
+ }
+
+ public boolean addGateway(String addrString) {
+ try {
+ linkProperties.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(addrString)));
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "addGateway failed with addrString " + addrString);
+ return true;
+ }
+ return false;
+ }
+
+ public boolean addDns(String addrString) {
+ try {
+ linkProperties.addDns(NetworkUtils.numericToInetAddress(addrString));
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "addDns failed with addrString " + addrString);
+ return true;
+ }
+ return false;
+ }
+
+ public boolean setServerAddress(String addrString) {
+ try {
+ serverAddress = NetworkUtils.numericToInetAddress(addrString);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "setServerAddress failed with addrString " + addrString);
+ return true;
+ }
+ return false;
+ }
+
+ public void setLeaseDuration(int duration) {
+ leaseDuration = duration;
+ }
+
+ public void setVendorInfo(String info) {
+ vendorInfo = info;
+ }
+
+ public void setDomains(String domains) {
+ linkProperties.setDomains(domains);
+ }
+}
diff --git a/core/java/android/net/DhcpStateMachine.java b/core/java/android/net/DhcpStateMachine.java
index 8dc900e..fd22b10 100644
--- a/core/java/android/net/DhcpStateMachine.java
+++ b/core/java/android/net/DhcpStateMachine.java
@@ -26,7 +26,7 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.net.DhcpInfoInternal;
+import android.net.DhcpResults;
import android.net.NetworkUtils;
import android.os.Message;
import android.os.PowerManager;
@@ -64,7 +64,7 @@ public class DhcpStateMachine extends StateMachine {
private static final String WAKELOCK_TAG = "DHCP";
//Remember DHCP configuration from first request
- private DhcpInfoInternal mDhcpInfo;
+ private DhcpResults mDhcpResults;
private static final int DHCP_RENEW = 0;
private static final String ACTION_DHCP_RENEW = "android.net.wifi.DHCP_RENEW";
@@ -348,23 +348,21 @@ public class DhcpStateMachine extends StateMachine {
private boolean runDhcp(DhcpAction dhcpAction) {
boolean success = false;
- DhcpInfoInternal dhcpInfoInternal = new DhcpInfoInternal();
+ DhcpResults dhcpResults = new DhcpResults();
if (dhcpAction == DhcpAction.START) {
/* Stop any existing DHCP daemon before starting new */
NetworkUtils.stopDhcp(mInterfaceName);
if (DBG) Log.d(TAG, "DHCP request on " + mInterfaceName);
- success = NetworkUtils.runDhcp(mInterfaceName, dhcpInfoInternal);
- mDhcpInfo = dhcpInfoInternal;
+ success = NetworkUtils.runDhcp(mInterfaceName, dhcpResults);
} else if (dhcpAction == DhcpAction.RENEW) {
if (DBG) Log.d(TAG, "DHCP renewal on " + mInterfaceName);
- success = NetworkUtils.runDhcpRenew(mInterfaceName, dhcpInfoInternal);
- dhcpInfoInternal.updateFromDhcpRequest(mDhcpInfo);
+ success = NetworkUtils.runDhcpRenew(mInterfaceName, dhcpResults);
+ dhcpResults.updateFromDhcpRequest(mDhcpResults);
}
-
if (success) {
if (DBG) Log.d(TAG, "DHCP succeeded on " + mInterfaceName);
- long leaseDuration = dhcpInfoInternal.leaseDuration; //int to long conversion
+ long leaseDuration = dhcpResults.leaseDuration; //int to long conversion
//Sanity check for renewal
if (leaseDuration >= 0) {
@@ -384,7 +382,8 @@ public class DhcpStateMachine extends StateMachine {
//infinite lease time, no renewal needed
}
- mController.obtainMessage(CMD_POST_DHCP_ACTION, DHCP_SUCCESS, 0, dhcpInfoInternal)
+ mDhcpResults = dhcpResults;
+ mController.obtainMessage(CMD_POST_DHCP_ACTION, DHCP_SUCCESS, 0, dhcpResults)
.sendToTarget();
} else {
Log.e(TAG, "DHCP failed on " + mInterfaceName + ": " +
diff --git a/core/java/android/net/EthernetDataTracker.java b/core/java/android/net/EthernetDataTracker.java
index 37601fc..8947162 100644
--- a/core/java/android/net/EthernetDataTracker.java
+++ b/core/java/android/net/EthernetDataTracker.java
@@ -170,13 +170,12 @@ public class EthernetDataTracker implements NetworkStateTracker {
private void runDhcp() {
Thread dhcpThread = new Thread(new Runnable() {
public void run() {
- DhcpInfoInternal dhcpInfoInternal = new DhcpInfoInternal();
- if (!NetworkUtils.runDhcp(mIface, dhcpInfoInternal)) {
+ DhcpResults dhcpResults = new DhcpResults();
+ if (!NetworkUtils.runDhcp(mIface, dhcpResults)) {
Log.e(TAG, "DHCP request error:" + NetworkUtils.getDhcpError());
return;
}
- mLinkProperties = dhcpInfoInternal.makeLinkProperties();
- mLinkProperties.setInterfaceName(mIface);
+ mLinkProperties = dhcpResults.linkProperties;
mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, mHwAddr);
Message msg = mCsHandler.obtainMessage(EVENT_STATE_CHANGED, mNetworkInfo);
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index 75646fd..b9362da 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -51,9 +51,10 @@ import java.util.Collections;
*/
public class LinkProperties implements Parcelable {
- String mIfaceName;
+ private String mIfaceName;
private Collection<LinkAddress> mLinkAddresses = new ArrayList<LinkAddress>();
private Collection<InetAddress> mDnses = new ArrayList<InetAddress>();
+ private String mDomains;
private Collection<RouteInfo> mRoutes = new ArrayList<RouteInfo>();
private ProxyProperties mHttpProxy;
@@ -82,9 +83,10 @@ public class LinkProperties implements Parcelable {
mIfaceName = source.getInterfaceName();
for (LinkAddress l : source.getLinkAddresses()) mLinkAddresses.add(l);
for (InetAddress i : source.getDnses()) mDnses.add(i);
+ mDomains = source.getDomains();
for (RouteInfo r : source.getRoutes()) mRoutes.add(r);
mHttpProxy = (source.getHttpProxy() == null) ?
- null : new ProxyProperties(source.getHttpProxy());
+ null : new ProxyProperties(source.getHttpProxy());
}
}
@@ -120,6 +122,14 @@ public class LinkProperties implements Parcelable {
return Collections.unmodifiableCollection(mDnses);
}
+ public String getDomains() {
+ return mDomains;
+ }
+
+ public void setDomains(String domains) {
+ mDomains = domains;
+ }
+
public void addRoute(RouteInfo route) {
if (route != null) mRoutes.add(route);
}
@@ -138,6 +148,7 @@ public class LinkProperties implements Parcelable {
mIfaceName = null;
mLinkAddresses.clear();
mDnses.clear();
+ mDomains = null;
mRoutes.clear();
mHttpProxy = null;
}
@@ -162,12 +173,14 @@ public class LinkProperties implements Parcelable {
for (InetAddress addr : mDnses) dns += addr.getHostAddress() + ",";
dns += "] ";
- String routes = "Routes: [";
+ String domainName = "Domains: " + mDomains;
+
+ String routes = " Routes: [";
for (RouteInfo route : mRoutes) routes += route.toString() + ",";
routes += "] ";
String proxy = (mHttpProxy == null ? "" : "HttpProxy: " + mHttpProxy.toString() + " ");
- return ifaceName + linkAddresses + routes + dns + proxy;
+ return ifaceName + linkAddresses + routes + dns + domainName + proxy;
}
/**
@@ -181,7 +194,7 @@ public class LinkProperties implements Parcelable {
}
/**
- * Compares this {@code LinkProperties} interface name against the target
+ * Compares this {@code LinkProperties} interface addresses against the target
*
* @param target LinkProperties to compare.
* @return {@code true} if both are identical, {@code false} otherwise.
@@ -201,6 +214,12 @@ public class LinkProperties implements Parcelable {
*/
public boolean isIdenticalDnses(LinkProperties target) {
Collection<InetAddress> targetDnses = target.getDnses();
+ String targetDomains = target.getDomains();
+ if (mDomains == null) {
+ if (targetDomains != null) return false;
+ } else {
+ if (mDomains.equals(targetDomains) == false) return false;
+ }
return (mDnses.size() == targetDnses.size()) ?
mDnses.containsAll(targetDnses) : false;
}
@@ -359,13 +378,13 @@ public class LinkProperties implements Parcelable {
return ((null == mIfaceName) ? 0 : mIfaceName.hashCode()
+ mLinkAddresses.size() * 31
+ mDnses.size() * 37
+ + ((null == mDomains) ? 0 : mDomains.hashCode())
+ mRoutes.size() * 41
+ ((null == mHttpProxy) ? 0 : mHttpProxy.hashCode()));
}
/**
* Implement the Parcelable interface.
- * @hide
*/
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(getInterfaceName());
@@ -378,6 +397,7 @@ public class LinkProperties implements Parcelable {
for(InetAddress d : mDnses) {
dest.writeByteArray(d.getAddress());
}
+ dest.writeString(mDomains);
dest.writeInt(mRoutes.size());
for(RouteInfo route : mRoutes) {
@@ -394,19 +414,15 @@ public class LinkProperties implements Parcelable {
/**
* Implement the Parcelable interface.
- * @hide
*/
public static final Creator<LinkProperties> CREATOR =
new Creator<LinkProperties>() {
public LinkProperties createFromParcel(Parcel in) {
LinkProperties netProp = new LinkProperties();
+
String iface = in.readString();
if (iface != null) {
- try {
- netProp.setInterfaceName(iface);
- } catch (Exception e) {
- return null;
- }
+ netProp.setInterfaceName(iface);
}
int addressCount = in.readInt();
for (int i=0; i<addressCount; i++) {
@@ -418,6 +434,7 @@ public class LinkProperties implements Parcelable {
netProp.addDns(InetAddress.getByAddress(in.createByteArray()));
} catch (UnknownHostException e) { }
}
+ netProp.setDomains(in.readString());
addressCount = in.readInt();
for (int i=0; i<addressCount; i++) {
netProp.addRoute((RouteInfo)in.readParcelable(null));
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index d39e741..4ab479e 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -62,21 +62,21 @@ public class NetworkUtils {
* addresses. This call blocks until it obtains a result (either success
* or failure) from the daemon.
* @param interfaceName the name of the interface to configure
- * @param ipInfo if the request succeeds, this object is filled in with
+ * @param dhcpResults if the request succeeds, this object is filled in with
* the IP address information.
* @return {@code true} for success, {@code false} for failure
*/
- public native static boolean runDhcp(String interfaceName, DhcpInfoInternal ipInfo);
+ public native static boolean runDhcp(String interfaceName, DhcpResults dhcpResults);
/**
* Initiate renewal on the Dhcp client daemon. This call blocks until it obtains
* a result (either success or failure) from the daemon.
* @param interfaceName the name of the interface to configure
- * @param ipInfo if the request succeeds, this object is filled in with
+ * @param dhcpResults if the request succeeds, this object is filled in with
* the IP address information.
* @return {@code true} for success, {@code false} for failure
*/
- public native static boolean runDhcpRenew(String interfaceName, DhcpInfoInternal ipInfo);
+ public native static boolean runDhcpRenew(String interfaceName, DhcpResults dhcpResults);
/**
* Shut down the DHCP client daemon.
@@ -124,12 +124,9 @@ public class NetworkUtils {
* @param inetAddr is an InetAddress corresponding to the IPv4 address
* @return the IP address as an integer in network byte order
*/
- public static int inetAddressToInt(InetAddress inetAddr)
+ public static int inetAddressToInt(Inet4Address inetAddr)
throws IllegalArgumentException {
byte [] addr = inetAddr.getAddress();
- if (addr.length != 4) {
- throw new IllegalArgumentException("Not an IPv4 address");
- }
return ((addr[3] & 0xff) << 24) | ((addr[2] & 0xff) << 16) |
((addr[1] & 0xff) << 8) | (addr[0] & 0xff);
}
diff --git a/core/java/android/net/RouteInfo.java b/core/java/android/net/RouteInfo.java
index 275f32a..112e143 100644
--- a/core/java/android/net/RouteInfo.java
+++ b/core/java/android/net/RouteInfo.java
@@ -76,6 +76,10 @@ public class RouteInfo implements Parcelable {
this(null, gateway);
}
+ public RouteInfo(LinkAddress host) {
+ this(host, null);
+ }
+
public static RouteInfo makeHostRoute(InetAddress host) {
return makeHostRoute(host, null);
}
diff --git a/core/java/android/net/http/AndroidHttpClient.java b/core/java/android/net/http/AndroidHttpClient.java
index fabe018..04f3974 100644
--- a/core/java/android/net/http/AndroidHttpClient.java
+++ b/core/java/android/net/http/AndroidHttpClient.java
@@ -17,6 +17,7 @@
package android.net.http;
import com.android.internal.http.HttpDateTime;
+
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpEntityEnclosingRequest;
@@ -25,18 +26,18 @@ import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpRequestInterceptor;
import org.apache.http.HttpResponse;
-import org.apache.http.entity.AbstractHttpEntity;
-import org.apache.http.entity.ByteArrayEntity;
+import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.ResponseHandler;
-import org.apache.http.client.ClientProtocolException;
-import org.apache.http.client.protocol.ClientContext;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.params.HttpClientParams;
+import org.apache.http.client.protocol.ClientContext;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
+import org.apache.http.entity.AbstractHttpEntity;
+import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.client.RequestWrapper;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
@@ -44,26 +45,26 @@ import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;
+import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.BasicHttpProcessor;
import org.apache.http.protocol.HttpContext;
-import org.apache.http.protocol.BasicHttpContext;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.OutputStream;
-import java.util.zip.GZIPInputStream;
-import java.util.zip.GZIPOutputStream;
-import java.net.URI;
-
-import android.content.Context;
import android.content.ContentResolver;
+import android.content.Context;
import android.net.SSLCertificateSocketFactory;
import android.net.SSLSessionCache;
import android.os.Looper;
import android.util.Base64;
import android.util.Log;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URI;
+import java.util.zip.GZIPInputStream;
+import java.util.zip.GZIPOutputStream;
+
/**
* Implementation of the Apache {@link DefaultHttpClient} that is configured with
* reasonable default settings and registered schemes for Android.
@@ -266,7 +267,7 @@ public final class AndroidHttpClient implements HttpClient {
return delegate.execute(target, request, context);
}
- public <T> T execute(HttpUriRequest request,
+ public <T> T execute(HttpUriRequest request,
ResponseHandler<? extends T> responseHandler)
throws IOException, ClientProtocolException {
return delegate.execute(request, responseHandler);
@@ -404,6 +405,11 @@ public final class AndroidHttpClient implements HttpClient {
builder.append("curl ");
+ // add in the method
+ builder.append("-X ");
+ builder.append(request.getMethod());
+ builder.append(" ");
+
for (Header header: request.getAllHeaders()) {
if (!logAuthToken
&& (header.getName().equals("Authorization") ||
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index 2179fa1..80abd0f 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -351,7 +351,7 @@ interface INetworkManagementService
/**
* Bind name servers to an interface in the DNS resolver.
*/
- void setDnsServersForInterface(String iface, in String[] servers);
+ void setDnsServersForInterface(String iface, in String[] servers, String domains);
/**
* Flush the DNS cache associated with the default interface.
diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java
index 0ca9183..27ed6b6 100644
--- a/core/java/android/os/Trace.java
+++ b/core/java/android/os/Trace.java
@@ -31,7 +31,7 @@ import android.util.Log;
public final class Trace {
private static final String TAG = "Trace";
- // These tags must be kept in sync with frameworks/native/include/utils/Trace.h.
+ // These tags must be kept in sync with system/core/include/cutils/trace.h.
public static final long TRACE_TAG_NEVER = 0;
public static final long TRACE_TAG_ALWAYS = 1L << 0;
public static final long TRACE_TAG_GRAPHICS = 1L << 1;
@@ -44,12 +44,13 @@ public final class Trace {
public static final long TRACE_TAG_AUDIO = 1L << 8;
public static final long TRACE_TAG_VIDEO = 1L << 9;
public static final long TRACE_TAG_CAMERA = 1L << 10;
+ public static final long TRACE_TAG_HAL = 1L << 11;
private static final long TRACE_TAG_NOT_READY = 1L << 63;
public static final int TRACE_FLAGS_START_BIT = 1;
public static final String[] TRACE_TAGS = {
"Graphics", "Input", "View", "WebView", "Window Manager",
- "Activity Manager", "Sync Manager", "Audio", "Video", "Camera",
+ "Activity Manager", "Sync Manager", "Audio", "Video", "Camera", "HAL",
};
public static final String PROPERTY_TRACE_TAG_ENABLEFLAGS = "debug.atrace.tags.enableflags";
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 477356e..0f735e8 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -84,7 +84,7 @@ public abstract class WallpaperService extends Service {
* tag.
*/
public static final String SERVICE_META_DATA = "android.service.wallpaper";
-
+
static final String TAG = "WallpaperService";
static final boolean DEBUG = false;
@@ -100,7 +100,6 @@ public abstract class WallpaperService extends Service {
private static final int MSG_WINDOW_MOVED = 10035;
private static final int MSG_TOUCH_EVENT = 10040;
- private Looper mCallbackLooper;
private final ArrayList<Engine> mActiveEngines
= new ArrayList<Engine>();
@@ -1105,13 +1104,14 @@ public abstract class WallpaperService extends Service {
mTarget = context;
}
+ @Override
public void attach(IWallpaperConnection conn, IBinder windowToken,
int windowType, boolean isPreview, int reqWidth, int reqHeight) {
new IWallpaperEngineWrapper(mTarget, conn, windowToken,
windowType, isPreview, reqWidth, reqHeight);
}
}
-
+
@Override
public void onCreate() {
super.onCreate();
@@ -1134,20 +1134,7 @@ public abstract class WallpaperService extends Service {
public final IBinder onBind(Intent intent) {
return new IWallpaperServiceWrapper(this);
}
-
- /**
- * This allows subclasses to change the thread that most callbacks
- * occur on. Currently hidden because it is mostly needed for the
- * image wallpaper (which runs in the system process and doesn't want
- * to get stuck running on that seriously in use main thread). Not
- * exposed right now because the semantics of this are not totally
- * well defined and some callbacks can still happen on the main thread).
- * @hide
- */
- public void setCallbackLooper(Looper looper) {
- mCallbackLooper = looper;
- }
-
+
/**
* Must be implemented to return a new instance of the wallpaper's engine.
* Note that multiple instances may be active at the same time, such as
diff --git a/core/java/android/speech/tts/ITextToSpeechService.aidl b/core/java/android/speech/tts/ITextToSpeechService.aidl
index ab63187..580d52c 100644
--- a/core/java/android/speech/tts/ITextToSpeechService.aidl
+++ b/core/java/android/speech/tts/ITextToSpeechService.aidl
@@ -131,6 +131,8 @@ interface ITextToSpeechService {
/**
* Notifies the engine that it should load a speech synthesis language.
*
+ * @param caller a binder representing the identity of the calling
+ * TextToSpeech object.
* @param lang ISO-3 language code.
* @param country ISO-3 country code. May be empty or null.
* @param variant Language variant. May be empty or null.
@@ -141,13 +143,14 @@ interface ITextToSpeechService {
* {@link TextToSpeech#LANG_MISSING_DATA}
* {@link TextToSpeech#LANG_NOT_SUPPORTED}.
*/
- int loadLanguage(in String lang, in String country, in String variant);
+ int loadLanguage(in IBinder caller, in String lang, in String country, in String variant);
/**
* Sets the callback that will be notified when playback of utterance from the
* given app are completed.
*
- * @param callingApp Package name for the app whose utterance the callback will handle.
+ * @param caller Instance a binder representing the identity of the calling
+ * TextToSpeech object.
* @param cb The callback.
*/
void setCallback(in IBinder caller, ITextToSpeechCallback cb);
diff --git a/core/java/android/speech/tts/SynthesisCallback.java b/core/java/android/speech/tts/SynthesisCallback.java
index d70c371..f98bb09 100644
--- a/core/java/android/speech/tts/SynthesisCallback.java
+++ b/core/java/android/speech/tts/SynthesisCallback.java
@@ -22,10 +22,11 @@ package android.speech.tts;
* {@link #start}, then {@link #audioAvailable} until all audio has been provided, then finally
* {@link #done}.
*
- *
* {@link #error} can be called at any stage in the synthesis process to
* indicate that an error has occurred, but if the call is made after a call
* to {@link #done}, it might be discarded.
+ *
+ * After {@link #start} been called, {@link #done} must be called regardless of errors.
*/
public interface SynthesisCallback {
/**
@@ -72,6 +73,8 @@ public interface SynthesisCallback {
* This method should only be called on the synthesis thread,
* while in {@link TextToSpeechService#onSynthesizeText}.
*
+ * This method has to be called if {@link #start} was called.
+ *
* @return {@link TextToSpeech#SUCCESS} or {@link TextToSpeech#ERROR}.
*/
public int done();
diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java
index 5e367cb..cb5e0cd 100644
--- a/core/java/android/speech/tts/TextToSpeech.java
+++ b/core/java/android/speech/tts/TextToSpeech.java
@@ -24,6 +24,7 @@ import android.content.Intent;
import android.content.ServiceConnection;
import android.media.AudioManager;
import android.net.Uri;
+import android.os.AsyncTask;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
@@ -793,10 +794,16 @@ public class TextToSpeech {
}
/**
- * Speaks the string using the specified queuing strategy and speech
- * parameters.
+ * Speaks the string using the specified queuing strategy and speech parameters.
+ * This method is asynchronous, i.e. the method just adds the request to the queue of TTS
+ * requests and then returns. The synthesis might not have finished (or even started!) at the
+ * time when this method returns. In order to reliably detect errors during synthesis,
+ * we recommend setting an utterance progress listener (see
+ * {@link #setOnUtteranceProgressListener}) and using the
+ * {@link Engine#KEY_PARAM_UTTERANCE_ID} parameter.
*
- * @param text The string of text to be spoken.
+ * @param text The string of text to be spoken. No longer than
+ * {@link #getMaxSpeechInputLength()} characters.
* @param queueMode The queuing strategy to use, {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}.
* @param params Parameters for the request. Can be null.
* Supported parameter names:
@@ -809,7 +816,7 @@ public class TextToSpeech {
* the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the
* engine named "com.svox.pico" if it is being used.
*
- * @return {@link #ERROR} or {@link #SUCCESS}.
+ * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the speak operation.
*/
public int speak(final String text, final int queueMode, final HashMap<String, String> params) {
return runAction(new Action<Integer>() {
@@ -830,6 +837,12 @@ public class TextToSpeech {
* Plays the earcon using the specified queueing mode and parameters.
* The earcon must already have been added with {@link #addEarcon(String, String)} or
* {@link #addEarcon(String, String, int)}.
+ * This method is asynchronous, i.e. the method just adds the request to the queue of TTS
+ * requests and then returns. The synthesis might not have finished (or even started!) at the
+ * time when this method returns. In order to reliably detect errors during synthesis,
+ * we recommend setting an utterance progress listener (see
+ * {@link #setOnUtteranceProgressListener}) and using the
+ * {@link Engine#KEY_PARAM_UTTERANCE_ID} parameter.
*
* @param earcon The earcon that should be played
* @param queueMode {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}.
@@ -842,7 +855,7 @@ public class TextToSpeech {
* the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the
* engine named "com.svox.pico" if it is being used.
*
- * @return {@link #ERROR} or {@link #SUCCESS}.
+ * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the playEarcon operation.
*/
public int playEarcon(final String earcon, final int queueMode,
final HashMap<String, String> params) {
@@ -862,6 +875,12 @@ public class TextToSpeech {
/**
* Plays silence for the specified amount of time using the specified
* queue mode.
+ * This method is asynchronous, i.e. the method just adds the request to the queue of TTS
+ * requests and then returns. The synthesis might not have finished (or even started!) at the
+ * time when this method returns. In order to reliably detect errors during synthesis,
+ * we recommend setting an utterance progress listener (see
+ * {@link #setOnUtteranceProgressListener}) and using the
+ * {@link Engine#KEY_PARAM_UTTERANCE_ID} parameter.
*
* @param durationInMs The duration of the silence.
* @param queueMode {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}.
@@ -873,7 +892,7 @@ public class TextToSpeech {
* the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the
* engine named "com.svox.pico" if it is being used.
*
- * @return {@link #ERROR} or {@link #SUCCESS}.
+ * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the playSilence operation.
*/
public int playSilence(final long durationInMs, final int queueMode,
final HashMap<String, String> params) {
@@ -1031,7 +1050,8 @@ public class TextToSpeech {
// the available parts.
// Note that the language is not actually set here, instead it is cached so it
// will be associated with all upcoming utterances.
- int result = service.loadLanguage(language, country, variant);
+
+ int result = service.loadLanguage(getCallerIdentity(), language, country, variant);
if (result >= LANG_AVAILABLE){
if (result < LANG_COUNTRY_VAR_AVAILABLE) {
variant = "";
@@ -1049,21 +1069,30 @@ public class TextToSpeech {
}
/**
- * Returns a Locale instance describing the language currently being used by the TextToSpeech
- * engine.
+ * Returns a Locale instance describing the language currently being used for synthesis
+ * requests sent to the TextToSpeech engine.
*
- * @return language, country (if any) and variant (if any) used by the engine stored in a Locale
- * instance, or {@code null} on error.
+ * In Android 4.2 and before (API <= 17) this function returns the language that is currently
+ * being used by the TTS engine. That is the last language set by this or any other
+ * client by a {@link TextToSpeech#setLanguage} call to the same engine.
+ *
+ * In Android versions after 4.2 this function returns the language that is currently being
+ * used for the synthesis requests sent from this client. That is the last language set
+ * by a {@link TextToSpeech#setLanguage} call on this instance.
+ *
+ * @return language, country (if any) and variant (if any) used by the client stored in a
+ * Locale instance, or {@code null} on error.
*/
public Locale getLanguage() {
return runAction(new Action<Locale>() {
@Override
- public Locale run(ITextToSpeechService service) throws RemoteException {
- String[] locStrings = service.getLanguage();
- if (locStrings != null && locStrings.length == 3) {
- return new Locale(locStrings[0], locStrings[1], locStrings[2]);
- }
- return null;
+ public Locale run(ITextToSpeechService service) {
+ /* No service call, but we're accessing mParams, hence need for
+ wrapping it as an Action instance */
+ String lang = mParams.getString(Engine.KEY_PARAM_LANGUAGE, "");
+ String country = mParams.getString(Engine.KEY_PARAM_COUNTRY, "");
+ String variant = mParams.getString(Engine.KEY_PARAM_VARIANT, "");
+ return new Locale(lang, country, variant);
}
}, null, "getLanguage");
}
@@ -1089,8 +1118,15 @@ public class TextToSpeech {
/**
* Synthesizes the given text to a file using the specified parameters.
+ * This method is asynchronous, i.e. the method just adds the request to the queue of TTS
+ * requests and then returns. The synthesis might not have finished (or even started!) at the
+ * time when this method returns. In order to reliably detect errors during synthesis,
+ * we recommend setting an utterance progress listener (see
+ * {@link #setOnUtteranceProgressListener}) and using the
+ * {@link Engine#KEY_PARAM_UTTERANCE_ID} parameter.
*
- * @param text The text that should be synthesized
+ * @param text The text that should be synthesized. No longer than
+ * {@link #getMaxSpeechInputLength()} characters.
* @param params Parameters for the request. Can be null.
* Supported parameter names:
* {@link Engine#KEY_PARAM_UTTERANCE_ID}.
@@ -1101,7 +1137,7 @@ public class TextToSpeech {
* @param filename Absolute file filename to write the generated audio data to.It should be
* something like "/sdcard/myappsounds/mysound.wav".
*
- * @return {@link #ERROR} or {@link #SUCCESS}.
+ * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the synthesizeToFile operation.
*/
public int synthesizeToFile(final String text, final HashMap<String, String> params,
final String filename) {
@@ -1253,9 +1289,11 @@ public class TextToSpeech {
return mEnginesHelper.getEngines();
}
-
private class Connection implements ServiceConnection {
private ITextToSpeechService mService;
+
+ private OnServiceConnectedAsyncTask mOnServiceConnectedAsyncTask;
+
private final ITextToSpeechCallback.Stub mCallback = new ITextToSpeechCallback.Stub() {
@Override
public void onDone(String utteranceId) {
@@ -1282,23 +1320,59 @@ public class TextToSpeech {
}
};
+ private class OnServiceConnectedAsyncTask extends AsyncTask<Void, Void, Integer> {
+ private final ComponentName mName;
+ private final ITextToSpeechService mConnectedService;
+
+ public OnServiceConnectedAsyncTask(ComponentName name, IBinder service) {
+ mName = name;
+ mConnectedService = ITextToSpeechService.Stub.asInterface(service);
+ }
+
+ @Override
+ protected Integer doInBackground(Void... params) {
+ synchronized(mStartLock) {
+ if (isCancelled()) {
+ return null;
+ }
+
+ try {
+ mConnectedService.setCallback(getCallerIdentity(), mCallback);
+ Log.i(TAG, "Setuped connection to " + mName);
+ return SUCCESS;
+ } catch (RemoteException re) {
+ Log.e(TAG, "Error connecting to service, setCallback() failed");
+ return ERROR;
+ }
+ }
+ }
+
+ @Override
+ protected void onPostExecute(Integer result) {
+ synchronized(mStartLock) {
+ if (mOnServiceConnectedAsyncTask == this) {
+ mOnServiceConnectedAsyncTask = null;
+ }
+
+ mServiceConnection = Connection.this;
+ mService = mConnectedService;
+
+ dispatchOnInit(result);
+ }
+ }
+ }
+
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
- Log.i(TAG, "Connected to " + name);
synchronized(mStartLock) {
- if (mServiceConnection != null) {
- // Disconnect any previous service connection
- mServiceConnection.disconnect();
- }
- mServiceConnection = this;
- mService = ITextToSpeechService.Stub.asInterface(service);
- try {
- mService.setCallback(getCallerIdentity(), mCallback);
- dispatchOnInit(SUCCESS);
- } catch (RemoteException re) {
- Log.e(TAG, "Error connecting to service, setCallback() failed");
- dispatchOnInit(ERROR);
+ Log.i(TAG, "Connected to " + name);
+
+ if (mOnServiceConnectedAsyncTask != null) {
+ mOnServiceConnectedAsyncTask.cancel(false);
}
+
+ mOnServiceConnectedAsyncTask = new OnServiceConnectedAsyncTask(name, service);
+ mOnServiceConnectedAsyncTask.execute();
}
}
@@ -1306,28 +1380,45 @@ public class TextToSpeech {
return mCallback;
}
- @Override
- public void onServiceDisconnected(ComponentName name) {
+ /**
+ * Clear connection related fields and cancel mOnServiceConnectedAsyncTask if set.
+ *
+ * @return true if we cancel mOnServiceConnectedAsyncTask in progress.
+ */
+ private boolean clearServiceConnection() {
synchronized(mStartLock) {
+ boolean result = false;
+ if (mOnServiceConnectedAsyncTask != null) {
+ result = mOnServiceConnectedAsyncTask.cancel(false);
+ mOnServiceConnectedAsyncTask = null;
+ }
+
mService = null;
// If this is the active connection, clear it
if (mServiceConnection == this) {
mServiceConnection = null;
}
+ return result;
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ Log.i(TAG, "Asked to disconnect from " + name);
+ if (clearServiceConnection()) {
+ /* We need to protect against a rare case where engine
+ * dies just after successful connection - and we process onServiceDisconnected
+ * before OnServiceConnectedAsyncTask.onPostExecute. onServiceDisconnected cancels
+ * OnServiceConnectedAsyncTask.onPostExecute and we don't call dispatchOnInit
+ * with ERROR as argument.
+ */
+ dispatchOnInit(ERROR);
}
}
public void disconnect() {
mContext.unbindService(this);
-
- synchronized (mStartLock) {
- mService = null;
- // If this is the active connection, clear it
- if (mServiceConnection == this) {
- mServiceConnection = null;
- }
-
- }
+ clearServiceConnection();
}
public <R> R runAction(Action<R> action, R errorResult, String method, boolean reconnect) {
@@ -1394,4 +1485,13 @@ public class TextToSpeech {
}
+ /**
+ * Limit of length of input string passed to speak and synthesizeToFile.
+ *
+ * @see #speak
+ * @see #synthesizeToFile
+ */
+ public static int getMaxSpeechInputLength() {
+ return 4000;
+ }
}
diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java
index d124e68..458350d 100644
--- a/core/java/android/speech/tts/TextToSpeechService.java
+++ b/core/java/android/speech/tts/TextToSpeechService.java
@@ -34,7 +34,6 @@ import android.text.TextUtils;
import android.util.Log;
import java.io.File;
-import java.io.IOException;
import java.util.HashMap;
import java.util.Locale;
import java.util.Set;
@@ -74,7 +73,7 @@ public abstract class TextToSpeechService extends Service {
private static final boolean DBG = false;
private static final String TAG = "TextToSpeechService";
- private static final int MAX_SPEECH_ITEM_CHAR_LENGTH = 4000;
+
private static final String SYNTH_THREAD_NAME = "SynthThread";
private SynthHandler mSynthHandler;
@@ -129,6 +128,8 @@ public abstract class TextToSpeechService extends Service {
*
* Can be called on multiple threads.
*
+ * Its return values HAVE to be consistent with onLoadLanguage.
+ *
* @param lang ISO-3 language code.
* @param country ISO-3 country code. May be empty or null.
* @param variant Language variant. May be empty or null.
@@ -163,6 +164,8 @@ public abstract class TextToSpeechService extends Service {
* at some point in the future.
*
* Can be called on multiple threads.
+ * In <= Android 4.2 (<= API 17) can be called on main and service binder threads.
+ * In > Android 4.2 (> API 17) can be called on main and synthesis threads.
*
* @param lang ISO-3 language code.
* @param country ISO-3 country code. May be empty or null.
@@ -256,7 +259,6 @@ public abstract class TextToSpeechService extends Service {
}
private class SynthHandler extends Handler {
-
private SpeechItem mCurrentSpeechItem = null;
public SynthHandler(Looper looper) {
@@ -275,7 +277,7 @@ public abstract class TextToSpeechService extends Service {
private synchronized SpeechItem maybeRemoveCurrentSpeechItem(Object callerIdentity) {
if (mCurrentSpeechItem != null &&
- mCurrentSpeechItem.getCallerIdentity() == callerIdentity) {
+ (mCurrentSpeechItem.getCallerIdentity() == callerIdentity)) {
SpeechItem current = mCurrentSpeechItem;
mCurrentSpeechItem = null;
return current;
@@ -296,7 +298,6 @@ public abstract class TextToSpeechService extends Service {
if (current != null) {
current.stop();
}
-
// The AudioPlaybackHandler will be destroyed by the caller.
}
@@ -306,8 +307,15 @@ public abstract class TextToSpeechService extends Service {
* Called on a service binder thread.
*/
public int enqueueSpeechItem(int queueMode, final SpeechItem speechItem) {
+ UtteranceProgressDispatcher utterenceProgress = null;
+ if (speechItem instanceof UtteranceProgressDispatcher) {
+ utterenceProgress = (UtteranceProgressDispatcher) speechItem;
+ }
+
if (!speechItem.isValid()) {
- speechItem.dispatchOnError();
+ if (utterenceProgress != null) {
+ utterenceProgress.dispatchOnError();
+ }
return TextToSpeech.ERROR;
}
@@ -325,6 +333,7 @@ public abstract class TextToSpeechService extends Service {
}
};
Message msg = Message.obtain(this, runnable);
+
// The obj is used to remove all callbacks from the given app in
// stopForApp(String).
//
@@ -334,7 +343,9 @@ public abstract class TextToSpeechService extends Service {
return TextToSpeech.SUCCESS;
} else {
Log.w(TAG, "SynthThread has quit");
- speechItem.dispatchOnError();
+ if (utterenceProgress != null) {
+ utterenceProgress.dispatchOnError();
+ }
return TextToSpeech.ERROR;
}
}
@@ -370,7 +381,7 @@ public abstract class TextToSpeechService extends Service {
}
public int stopAll() {
- // Stop the current speech item unconditionally.
+ // Stop the current speech item unconditionally .
SpeechItem current = setCurrentSpeechItem(null);
if (current != null) {
current.stop();
@@ -393,7 +404,7 @@ public abstract class TextToSpeechService extends Service {
/**
* An item in the synth thread queue.
*/
- private abstract class SpeechItem implements UtteranceProgressDispatcher {
+ private abstract class SpeechItem {
private final Object mCallerIdentity;
protected final Bundle mParams;
private final int mCallerUid;
@@ -412,6 +423,15 @@ public abstract class TextToSpeechService extends Service {
return mCallerIdentity;
}
+
+ public int getCallerUid() {
+ return mCallerUid;
+ }
+
+ public int getCallerPid() {
+ return mCallerPid;
+ }
+
/**
* Checker whether the item is valid. If this method returns false, the item should not
* be played.
@@ -436,6 +456,8 @@ public abstract class TextToSpeechService extends Service {
return playImpl();
}
+ protected abstract int playImpl();
+
/**
* Stops the speech item.
* Must not be called more than once.
@@ -452,6 +474,23 @@ public abstract class TextToSpeechService extends Service {
stopImpl();
}
+ protected abstract void stopImpl();
+
+ protected synchronized boolean isStopped() {
+ return mStopped;
+ }
+ }
+
+ /**
+ * An item in the synth thread queue that process utterance.
+ */
+ private abstract class UtteranceSpeechItem extends SpeechItem
+ implements UtteranceProgressDispatcher {
+
+ public UtteranceSpeechItem(Object caller, int callerUid, int callerPid, Bundle params) {
+ super(caller, callerUid, callerPid, params);
+ }
+
@Override
public void dispatchOnDone() {
final String utteranceId = getUtteranceId();
@@ -476,22 +515,6 @@ public abstract class TextToSpeechService extends Service {
}
}
- public int getCallerUid() {
- return mCallerUid;
- }
-
- public int getCallerPid() {
- return mCallerPid;
- }
-
- protected synchronized boolean isStopped() {
- return mStopped;
- }
-
- protected abstract int playImpl();
-
- protected abstract void stopImpl();
-
public int getStreamType() {
return getIntParam(Engine.KEY_PARAM_STREAM, Engine.DEFAULT_STREAM);
}
@@ -519,9 +542,10 @@ public abstract class TextToSpeechService extends Service {
protected float getFloatParam(String key, float defaultValue) {
return mParams == null ? defaultValue : mParams.getFloat(key, defaultValue);
}
+
}
- class SynthesisSpeechItem extends SpeechItem {
+ class SynthesisSpeechItem extends UtteranceSpeechItem {
// Never null.
private final String mText;
private final SynthesisRequest mSynthesisRequest;
@@ -552,7 +576,7 @@ public abstract class TextToSpeechService extends Service {
Log.e(TAG, "null synthesis text");
return false;
}
- if (mText.length() >= MAX_SPEECH_ITEM_CHAR_LENGTH) {
+ if (mText.length() >= TextToSpeech.getMaxSpeechInputLength()) {
Log.w(TAG, "Text too long: " + mText.length() + " chars");
return false;
}
@@ -658,7 +682,7 @@ public abstract class TextToSpeechService extends Service {
}
}
- private class AudioSpeechItem extends SpeechItem {
+ private class AudioSpeechItem extends UtteranceSpeechItem {
private final AudioPlaybackQueueItem mItem;
public AudioSpeechItem(Object callerIdentity, int callerUid, int callerPid,
Bundle params, Uri uri) {
@@ -684,7 +708,7 @@ public abstract class TextToSpeechService extends Service {
}
}
- private class SilenceSpeechItem extends SpeechItem {
+ private class SilenceSpeechItem extends UtteranceSpeechItem {
private final long mDuration;
public SilenceSpeechItem(Object callerIdentity, int callerUid, int callerPid,
@@ -711,6 +735,41 @@ public abstract class TextToSpeechService extends Service {
}
}
+ private class LoadLanguageItem extends SpeechItem {
+ private final String mLanguage;
+ private final String mCountry;
+ private final String mVariant;
+
+ public LoadLanguageItem(Object callerIdentity, int callerUid, int callerPid,
+ Bundle params, String language, String country, String variant) {
+ super(callerIdentity, callerUid, callerPid, params);
+ mLanguage = language;
+ mCountry = country;
+ mVariant = variant;
+ }
+
+ @Override
+ public boolean isValid() {
+ return true;
+ }
+
+ @Override
+ protected int playImpl() {
+ int result = TextToSpeechService.this.onLoadLanguage(mLanguage, mCountry, mVariant);
+ if (result == TextToSpeech.LANG_AVAILABLE ||
+ result == TextToSpeech.LANG_COUNTRY_AVAILABLE ||
+ result == TextToSpeech.LANG_COUNTRY_VAR_AVAILABLE) {
+ return TextToSpeech.SUCCESS;
+ }
+ return TextToSpeech.ERROR;
+ }
+
+ @Override
+ protected void stopImpl() {
+ // No-op
+ }
+ }
+
@Override
public IBinder onBind(Intent intent) {
if (TextToSpeech.Engine.INTENT_ACTION_TTS_SERVICE.equals(intent.getAction())) {
@@ -822,12 +881,25 @@ public abstract class TextToSpeechService extends Service {
* are enforced.
*/
@Override
- public int loadLanguage(String lang, String country, String variant) {
+ public int loadLanguage(IBinder caller, String lang, String country, String variant) {
if (!checkNonNull(lang)) {
return TextToSpeech.ERROR;
}
+ int retVal = onIsLanguageAvailable(lang, country, variant);
+
+ if (retVal == TextToSpeech.LANG_AVAILABLE ||
+ retVal == TextToSpeech.LANG_COUNTRY_AVAILABLE ||
+ retVal == TextToSpeech.LANG_COUNTRY_VAR_AVAILABLE) {
- return onLoadLanguage(lang, country, variant);
+ SpeechItem item = new LoadLanguageItem(caller, Binder.getCallingUid(),
+ Binder.getCallingPid(), null, lang, country, variant);
+
+ if (mSynthHandler.enqueueSpeechItem(TextToSpeech.QUEUE_ADD, item) !=
+ TextToSpeech.SUCCESS) {
+ return TextToSpeech.ERROR;
+ }
+ }
+ return retVal;
}
@Override
diff --git a/core/java/android/text/Html.java b/core/java/android/text/Html.java
index 1aab911..160c630 100644
--- a/core/java/android/text/Html.java
+++ b/core/java/android/text/Html.java
@@ -16,6 +16,7 @@
package android.text;
+import android.graphics.Color;
import com.android.internal.util.ArrayUtils;
import org.ccil.cowan.tagsoup.HTMLSchema;
import org.ccil.cowan.tagsoup.Parser;
@@ -168,7 +169,7 @@ public class Html {
for(int j = 0; j < style.length; j++) {
if (style[j] instanceof AlignmentSpan) {
- Layout.Alignment align =
+ Layout.Alignment align =
((AlignmentSpan) style[j]).getAlignment();
needDiv = true;
if (align == Layout.Alignment.ALIGN_CENTER) {
@@ -181,7 +182,7 @@ public class Html {
}
}
if (needDiv) {
- out.append("<div " + elements + ">");
+ out.append("<div ").append(elements).append(">");
}
withinDiv(out, text, i, next);
@@ -199,13 +200,13 @@ public class Html {
next = text.nextSpanTransition(i, end, QuoteSpan.class);
QuoteSpan[] quotes = text.getSpans(i, next, QuoteSpan.class);
- for (QuoteSpan quote: quotes) {
+ for (QuoteSpan quote : quotes) {
out.append("<blockquote>");
}
withinBlockquote(out, text, i, next);
- for (QuoteSpan quote: quotes) {
+ for (QuoteSpan quote : quotes) {
out.append("</blockquote>\n");
}
}
@@ -391,7 +392,7 @@ public class Html {
} else if (c == '&') {
out.append("&amp;");
} else if (c > 0x7E || c < ' ') {
- out.append("&#" + ((int) c) + ";");
+ out.append("&#").append((int) c).append(";");
} else if (c == ' ') {
while (i + 1 < end && text.charAt(i + 1) == ' ') {
out.append("&nbsp;");
@@ -616,8 +617,6 @@ class HtmlToSpannedConverter implements ContentHandler {
if (where != len) {
text.setSpan(repl, where, len, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
-
- return;
}
private static void startImg(SpannableStringBuilder text,
@@ -673,7 +672,7 @@ class HtmlToSpannedConverter implements ContentHandler {
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
} else {
- int c = getHtmlColor(f.mColor);
+ int c = Color.getHtmlColor(f.mColor);
if (c != -1) {
text.setSpan(new ForegroundColorSpan(c | 0xFF000000),
where, len,
@@ -842,47 +841,4 @@ class HtmlToSpannedConverter implements ContentHandler {
mLevel = level;
}
}
-
- private static HashMap<String,Integer> COLORS = buildColorMap();
-
- private static HashMap<String,Integer> buildColorMap() {
- HashMap<String,Integer> map = new HashMap<String,Integer>();
- map.put("aqua", 0x00FFFF);
- map.put("black", 0x000000);
- map.put("blue", 0x0000FF);
- map.put("fuchsia", 0xFF00FF);
- map.put("green", 0x008000);
- map.put("grey", 0x808080);
- map.put("lime", 0x00FF00);
- map.put("maroon", 0x800000);
- map.put("navy", 0x000080);
- map.put("olive", 0x808000);
- map.put("purple", 0x800080);
- map.put("red", 0xFF0000);
- map.put("silver", 0xC0C0C0);
- map.put("teal", 0x008080);
- map.put("white", 0xFFFFFF);
- map.put("yellow", 0xFFFF00);
- return map;
- }
-
- /**
- * Converts an HTML color (named or numeric) to an integer RGB value.
- *
- * @param color Non-null color string.
- * @return A color value, or {@code -1} if the color string could not be interpreted.
- */
- private static int getHtmlColor(String color) {
- Integer i = COLORS.get(color.toLowerCase());
- if (i != null) {
- return i;
- } else {
- try {
- return XmlUtils.convertValueToInt(color, -1);
- } catch (NumberFormatException nfe) {
- return -1;
- }
- }
- }
-
}
diff --git a/core/java/android/text/method/DigitsKeyListener.java b/core/java/android/text/method/DigitsKeyListener.java
index 3d9daed..c95df46 100644
--- a/core/java/android/text/method/DigitsKeyListener.java
+++ b/core/java/android/text/method/DigitsKeyListener.java
@@ -49,13 +49,22 @@ public class DigitsKeyListener extends NumberKeyListener
* @see KeyEvent#getMatch
* @see #getAcceptedChars
*/
- private static final char[][] CHARACTERS = new char[][] {
- new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' },
- new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-' },
- new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.' },
- new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '.' },
+ private static final char[][] CHARACTERS = {
+ { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' },
+ { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '+' },
+ { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.' },
+ { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '+', '.' },
};
+ private static boolean isSignChar(final char c) {
+ return c == '-' || c == '+';
+ }
+
+ // TODO: Needs internationalization
+ private static boolean isDecimalPointChar(final char c) {
+ return c == '.';
+ }
+
/**
* Allocates a DigitsKeyListener that accepts the digits 0 through 9.
*/
@@ -145,32 +154,32 @@ public class DigitsKeyListener extends NumberKeyListener
int dlen = dest.length();
/*
- * Find out if the existing text has '-' or '.' characters.
+ * Find out if the existing text has a sign or decimal point characters.
*/
for (int i = 0; i < dstart; i++) {
char c = dest.charAt(i);
- if (c == '-') {
+ if (isSignChar(c)) {
sign = i;
- } else if (c == '.') {
+ } else if (isDecimalPointChar(c)) {
decimal = i;
}
}
for (int i = dend; i < dlen; i++) {
char c = dest.charAt(i);
- if (c == '-') {
- return ""; // Nothing can be inserted in front of a '-'.
- } else if (c == '.') {
+ if (isSignChar(c)) {
+ return ""; // Nothing can be inserted in front of a sign character.
+ } else if (isDecimalPointChar(c)) {
decimal = i;
}
}
/*
* If it does, we must strip them out from the source.
- * In addition, '-' must be the very first character,
- * and nothing can be inserted before an existing '-'.
+ * In addition, a sign character must be the very first character,
+ * and nothing can be inserted before an existing sign character.
* Go in reverse order so the offsets are stable.
*/
@@ -180,7 +189,7 @@ public class DigitsKeyListener extends NumberKeyListener
char c = source.charAt(i);
boolean strip = false;
- if (c == '-') {
+ if (isSignChar(c)) {
if (i != start || dstart != 0) {
strip = true;
} else if (sign >= 0) {
@@ -188,7 +197,7 @@ public class DigitsKeyListener extends NumberKeyListener
} else {
sign = i;
}
- } else if (c == '.') {
+ } else if (isDecimalPointChar(c)) {
if (decimal >= 0) {
strip = true;
} else {
diff --git a/core/java/android/text/style/SuggestionSpan.java b/core/java/android/text/style/SuggestionSpan.java
index 5dc206f..0ec7e84 100644
--- a/core/java/android/text/style/SuggestionSpan.java
+++ b/core/java/android/text/style/SuggestionSpan.java
@@ -17,6 +17,7 @@
package android.text.style;
import android.content.Context;
+import android.content.Intent;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.os.Parcel;
@@ -26,6 +27,7 @@ import android.text.ParcelableSpan;
import android.text.TextPaint;
import android.text.TextUtils;
import android.util.Log;
+import android.view.inputmethod.InputMethodManager;
import android.widget.TextView;
import java.util.Arrays;
@@ -45,6 +47,8 @@ import java.util.Locale;
*/
public class SuggestionSpan extends CharacterStyle implements ParcelableSpan {
+ private static final String TAG = "SuggestionSpan";
+
/**
* Sets this flag if the suggestions should be easily accessible with few interactions.
* This flag should be set for every suggestions that the user is likely to use.
@@ -82,6 +86,7 @@ public class SuggestionSpan extends CharacterStyle implements ParcelableSpan {
private final String[] mSuggestions;
private final String mLocaleString;
private final String mNotificationTargetClassName;
+ private final String mNotificationTargetPackageName;
private final int mHashCode;
private float mEasyCorrectUnderlineThickness;
@@ -134,6 +139,12 @@ public class SuggestionSpan extends CharacterStyle implements ParcelableSpan {
mLocaleString = "";
}
+ if (context != null) {
+ mNotificationTargetPackageName = context.getPackageName();
+ } else {
+ mNotificationTargetPackageName = null;
+ }
+
if (notificationTargetClass != null) {
mNotificationTargetClassName = notificationTargetClass.getCanonicalName();
} else {
@@ -185,6 +196,7 @@ public class SuggestionSpan extends CharacterStyle implements ParcelableSpan {
mFlags = src.readInt();
mLocaleString = src.readString();
mNotificationTargetClassName = src.readString();
+ mNotificationTargetPackageName = src.readString();
mHashCode = src.readInt();
mEasyCorrectUnderlineColor = src.readInt();
mEasyCorrectUnderlineThickness = src.readFloat();
@@ -240,6 +252,7 @@ public class SuggestionSpan extends CharacterStyle implements ParcelableSpan {
dest.writeInt(mFlags);
dest.writeString(mLocaleString);
dest.writeString(mNotificationTargetClassName);
+ dest.writeString(mNotificationTargetPackageName);
dest.writeInt(mHashCode);
dest.writeInt(mEasyCorrectUnderlineColor);
dest.writeFloat(mEasyCorrectUnderlineThickness);
@@ -325,4 +338,40 @@ public class SuggestionSpan extends CharacterStyle implements ParcelableSpan {
}
return 0;
}
+
+ /**
+ * Notifies a suggestion selection.
+ *
+ * @hide
+ */
+ public void notifySelection(Context context, String original, int index) {
+ final Intent intent = new Intent();
+
+ if (context == null || mNotificationTargetClassName == null) {
+ return;
+ }
+ // Ensures that only a class in the original IME package will receive the
+ // notification.
+ if (mSuggestions == null || index < 0 || index >= mSuggestions.length) {
+ Log.w(TAG, "Unable to notify the suggestion as the index is out of range index=" + index
+ + " length=" + mSuggestions.length);
+ return;
+ }
+
+ // The package name is not mandatory (legacy from JB), and if the package name
+ // is missing, we try to notify the suggestion through the input method manager.
+ if (mNotificationTargetPackageName != null) {
+ intent.setClassName(mNotificationTargetPackageName, mNotificationTargetClassName);
+ intent.setAction(SuggestionSpan.ACTION_SUGGESTION_PICKED);
+ intent.putExtra(SuggestionSpan.SUGGESTION_SPAN_PICKED_BEFORE, original);
+ intent.putExtra(SuggestionSpan.SUGGESTION_SPAN_PICKED_AFTER, mSuggestions[index]);
+ intent.putExtra(SuggestionSpan.SUGGESTION_SPAN_PICKED_HASHCODE, hashCode());
+ context.sendBroadcast(intent);
+ } else {
+ InputMethodManager imm = InputMethodManager.peekInstance();
+ if (imm != null) {
+ imm.notifySuggestionPicked(this, original, index);
+ }
+ }
+ }
}
diff --git a/core/java/android/util/FinitePool.java b/core/java/android/util/FinitePool.java
deleted file mode 100644
index b30f2bf..0000000
--- a/core/java/android/util/FinitePool.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.util;
-
-/**
- * @hide
- */
-class FinitePool<T extends Poolable<T>> implements Pool<T> {
- private static final String LOG_TAG = "FinitePool";
-
- /**
- * Factory used to create new pool objects
- */
- private final PoolableManager<T> mManager;
- /**
- * Maximum number of objects in the pool
- */
- private final int mLimit;
- /**
- * If true, mLimit is ignored
- */
- private final boolean mInfinite;
-
- /**
- * Next object to acquire
- */
- private T mRoot;
- /**
- * Number of objects in the pool
- */
- private int mPoolCount;
-
- FinitePool(PoolableManager<T> manager) {
- mManager = manager;
- mLimit = 0;
- mInfinite = true;
- }
-
- FinitePool(PoolableManager<T> manager, int limit) {
- if (limit <= 0) throw new IllegalArgumentException("The pool limit must be > 0");
-
- mManager = manager;
- mLimit = limit;
- mInfinite = false;
- }
-
- public T acquire() {
- T element;
-
- if (mRoot != null) {
- element = mRoot;
- mRoot = element.getNextPoolable();
- mPoolCount--;
- } else {
- element = mManager.newInstance();
- }
-
- if (element != null) {
- element.setNextPoolable(null);
- element.setPooled(false);
- mManager.onAcquired(element);
- }
-
- return element;
- }
-
- public void release(T element) {
- if (!element.isPooled()) {
- if (mInfinite || mPoolCount < mLimit) {
- mPoolCount++;
- element.setNextPoolable(mRoot);
- element.setPooled(true);
- mRoot = element;
- }
- mManager.onReleased(element);
- } else {
- Log.w(LOG_TAG, "Element is already in pool: " + element);
- }
- }
-}
diff --git a/core/java/android/util/Poolable.java b/core/java/android/util/Poolable.java
deleted file mode 100644
index 87e0529..0000000
--- a/core/java/android/util/Poolable.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.util;
-
-/**
- * @hide
- */
-public interface Poolable<T> {
- void setNextPoolable(T element);
- T getNextPoolable();
- boolean isPooled();
- void setPooled(boolean isPooled);
-}
diff --git a/core/java/android/util/PoolableManager.java b/core/java/android/util/PoolableManager.java
deleted file mode 100644
index 8773e63..0000000
--- a/core/java/android/util/PoolableManager.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.util;
-
-/**
- * @hide
- */
-public interface PoolableManager<T extends Poolable<T>> {
- T newInstance();
-
- void onAcquired(T element);
- void onReleased(T element);
-}
diff --git a/core/java/android/util/Pools.java b/core/java/android/util/Pools.java
index 8edb3e6..9f5f36e 100644
--- a/core/java/android/util/Pools.java
+++ b/core/java/android/util/Pools.java
@@ -17,25 +17,169 @@
package android.util;
/**
+ * Helper class for crating pools of objects. An example use looks like this:
+ * <pre>
+ * public class MyPooledClass {
+ *
+ * private static final SynchronizedPool<MyPooledClass> sPool =
+ * new SynchronizedPool<MyPooledClass>(10);
+ *
+ * public static MyPooledClass obtain() {
+ * MyPooledClass instance = sPool.acquire();
+ * return (instance != null) ? instance : new MyPooledClass();
+ * }
+ *
+ * public void recycle() {
+ * // Clear state if needed.
+ * sPool.release(this);
+ * }
+ *
+ * . . .
+ * }
+ * </pre>
+ *
* @hide
*/
-public class Pools {
- private Pools() {
+public final class Pools {
+
+ /**
+ * Interface for managing a pool of objects.
+ *
+ * @param <T> The pooled type.
+ */
+ public static interface Pool<T> {
+
+ /**
+ * @return An instance from the pool if such, null otherwise.
+ */
+ public T acquire();
+
+ /**
+ * Release an instance to the pool.
+ *
+ * @param instance The instance to release.
+ * @return Whether the instance was put in the pool.
+ *
+ * @throws IllegalStateException If the instance is already in the pool.
+ */
+ public boolean release(T instance);
}
- public static <T extends Poolable<T>> Pool<T> simplePool(PoolableManager<T> manager) {
- return new FinitePool<T>(manager);
+ private Pools() {
+ /* do nothing - hiding constructor */
}
-
- public static <T extends Poolable<T>> Pool<T> finitePool(PoolableManager<T> manager, int limit) {
- return new FinitePool<T>(manager, limit);
+
+ private static class PoolableHolder<T> {
+ T mPoolable;
+ PoolableHolder<T> mNext;
}
- public static <T extends Poolable<T>> Pool<T> synchronizedPool(Pool<T> pool) {
- return new SynchronizedPool<T>(pool);
+ /**
+ * Simple (non-synchronized) pool of objects.
+ *
+ * @param <T> The pooled type.
+ */
+ public static class SimplePool<T> implements Pool<T> {
+ private final int mMaxPoolSize;
+
+ private int mPoolSize;
+
+ private PoolableHolder<T> mEmptyHolders;
+ private PoolableHolder<T> mPool;
+
+ /**
+ * Creates a new instance.
+ *
+ * @param maxPoolSize The max pool size.
+ *
+ * @throws IllegalArgumentException If the max pool size is less than zero.
+ */
+ public SimplePool(int maxPoolSize) {
+ if (maxPoolSize <= 0) {
+ throw new IllegalArgumentException("The max pool size must be > 0");
+ }
+ mMaxPoolSize = maxPoolSize;
+ }
+
+ @Override
+ public T acquire() {
+ if (mPool != null) {
+ PoolableHolder<T> holder = mPool;
+ mPool = holder.mNext;
+ T poolable = holder.mPoolable;
+ holder.mPoolable = null;
+ holder.mNext = mEmptyHolders;
+ mEmptyHolders = holder;
+ mPoolSize--;
+ return poolable;
+ }
+ return null;
+ }
+
+ @Override
+ public boolean release(T instance) {
+ if (isInPool(instance)) {
+ throw new IllegalStateException("Already in the pool!");
+ }
+ if (mPoolSize < mMaxPoolSize) {
+ PoolableHolder<T> holder = mEmptyHolders;
+ if (holder == null) {
+ holder = new PoolableHolder<T>();
+ } else {
+ mEmptyHolders = holder.mNext;
+ }
+ holder.mPoolable = instance;
+ holder.mNext = mPool;
+ mPool = holder;
+ mPoolSize++;
+ return true;
+ }
+ return false;
+ }
+
+ private boolean isInPool(T instance) {
+ PoolableHolder<T> current = mPool;
+ while (current != null) {
+ if (current.mPoolable == instance) {
+ return true;
+ }
+ current = current.mNext;
+ }
+ return false;
+ }
}
- public static <T extends Poolable<T>> Pool<T> synchronizedPool(Pool<T> pool, Object lock) {
- return new SynchronizedPool<T>(pool, lock);
+ /**
+ * Synchronized) pool of objects.
+ *
+ * @param <T> The pooled type.
+ */
+ public static class SynchronizedPool<T> extends SimplePool<T> {
+ private final Object mLock = new Object();
+
+ /**
+ * Creates a new instance.
+ *
+ * @param maxPoolSize The max pool size.
+ *
+ * @throws IllegalArgumentException If the max pool size is less than zero.
+ */
+ public SynchronizedPool(int maxPoolSize) {
+ super(maxPoolSize);
+ }
+
+ @Override
+ public T acquire() {
+ synchronized (mLock) {
+ return super.acquire();
+ }
+ }
+
+ @Override
+ public boolean release(T element) {
+ synchronized (mLock) {
+ return super.release(element);
+ }
+ }
}
}
diff --git a/core/java/android/util/PropertyValueModel.java b/core/java/android/util/PropertyValueModel.java
new file mode 100755
index 0000000..eb9c47d
--- /dev/null
+++ b/core/java/android/util/PropertyValueModel.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+/**
+ * A value model for a {@link Property property} of a host object. This class can be used for
+ * both reflective and non-reflective property implementations.
+ *
+ * @param <H> the host type, where the host is the object that holds this property
+ * @param <T> the value type
+ *
+ * @see Property
+ * @see ValueModel
+ */
+public class PropertyValueModel<H, T> extends ValueModel<T> {
+ private final H mHost;
+ private final Property<H, T> mProperty;
+
+ private PropertyValueModel(H host, Property<H, T> property) {
+ mProperty = property;
+ mHost = host;
+ }
+
+ /**
+ * Returns the host.
+ *
+ * @return the host
+ */
+ public H getHost() {
+ return mHost;
+ }
+
+ /**
+ * Returns the property.
+ *
+ * @return the property
+ */
+ public Property<H, T> getProperty() {
+ return mProperty;
+ }
+
+ @Override
+ public Class<T> getType() {
+ return mProperty.getType();
+ }
+
+ @Override
+ public T get() {
+ return mProperty.get(mHost);
+ }
+
+ @Override
+ public void set(T value) {
+ mProperty.set(mHost, value);
+ }
+
+ /**
+ * Return an appropriate PropertyValueModel for this host and property.
+ *
+ * @param host the host
+ * @param property the property
+ * @return the value model
+ */
+ public static <H, T> PropertyValueModel<H, T> of(H host, Property<H, T> property) {
+ return new PropertyValueModel<H, T>(host, property);
+ }
+
+ /**
+ * Return a PropertyValueModel for this {@code host} and a
+ * reflective property, constructed from this {@code propertyType} and {@code propertyName}.
+ *
+ * @param host
+ * @param propertyType the property type
+ * @param propertyName the property name
+ * @return a value model with this host and a reflective property with this type and name
+ *
+ * @see Property#of
+ */
+ public static <H, T> PropertyValueModel<H, T> of(H host, Class<T> propertyType,
+ String propertyName) {
+ return of(host, Property.of((Class<H>) host.getClass(), propertyType, propertyName));
+ }
+
+ private static Class getNullaryMethodReturnType(Class c, String name) {
+ try {
+ return c.getMethod(name).getReturnType();
+ } catch (NoSuchMethodException e) {
+ return null;
+ }
+ }
+
+ private static Class getFieldType(Class c, String name) {
+ try {
+ return c.getField(name).getType();
+ } catch (NoSuchFieldException e) {
+ return null;
+ }
+ }
+
+ private static String capitalize(String name) {
+ if (name.isEmpty()) {
+ return name;
+ }
+ return Character.toUpperCase(name.charAt(0)) + name.substring(1);
+ }
+
+ /**
+ * Return a PropertyValueModel for this {@code host} and and {@code propertyName}.
+ *
+ * @param host the host
+ * @param propertyName the property name
+ * @return a value model with this host and a reflective property with this name
+ */
+ public static PropertyValueModel of(Object host, String propertyName) {
+ Class clazz = host.getClass();
+ String suffix = capitalize(propertyName);
+ Class propertyType = getNullaryMethodReturnType(clazz, "get" + suffix);
+ if (propertyType == null) {
+ propertyType = getNullaryMethodReturnType(clazz, "is" + suffix);
+ }
+ if (propertyType == null) {
+ propertyType = getFieldType(clazz, propertyName);
+ }
+ if (propertyType == null) {
+ throw new NoSuchPropertyException(propertyName);
+ }
+ return of(host, propertyType, propertyName);
+ }
+}
diff --git a/core/java/android/util/SparseLongArray.java b/core/java/android/util/SparseLongArray.java
index a08d5cb..2f7a6fe 100644
--- a/core/java/android/util/SparseLongArray.java
+++ b/core/java/android/util/SparseLongArray.java
@@ -22,8 +22,6 @@ import com.android.internal.util.ArrayUtils;
* SparseLongArrays map integers to longs. Unlike a normal array of longs,
* there can be gaps in the indices. It is intended to be more efficient
* than using a HashMap to map Integers to Longs.
- *
- * @hide
*/
public class SparseLongArray implements Cloneable {
diff --git a/core/java/android/util/SynchronizedPool.java b/core/java/android/util/SynchronizedPool.java
deleted file mode 100644
index 651e0c3..0000000
--- a/core/java/android/util/SynchronizedPool.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.util;
-
-/**
- *
- * @hide
- */
-class SynchronizedPool<T extends Poolable<T>> implements Pool<T> {
- private final Pool<T> mPool;
- private final Object mLock;
-
- public SynchronizedPool(Pool<T> pool) {
- mPool = pool;
- mLock = this;
- }
-
- public SynchronizedPool(Pool<T> pool, Object lock) {
- mPool = pool;
- mLock = lock;
- }
-
- public T acquire() {
- synchronized (mLock) {
- return mPool.acquire();
- }
- }
-
- public void release(T element) {
- synchronized (mLock) {
- mPool.release(element);
- }
- }
-}
diff --git a/core/java/android/util/ValueModel.java b/core/java/android/util/ValueModel.java
new file mode 100755
index 0000000..4789682
--- /dev/null
+++ b/core/java/android/util/ValueModel.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+/**
+ * A ValueModel is an abstraction for a 'slot' or place in memory in which a value
+ * may be stored and retrieved. A common implementation of ValueModel is a regular property of
+ * an object, whose value may be retrieved by calling the appropriate <em>getter</em>
+ * method and set by calling the corresponding <em>setter</em> method.
+ *
+ * @param <T> the value type
+ *
+ * @see PropertyValueModel
+ */
+public abstract class ValueModel<T> {
+ /**
+ * The empty model should be used in place of {@code null} to indicate that a
+ * model has not been set. The empty model has no value and does nothing when it is set.
+ */
+ public static final ValueModel EMPTY = new ValueModel() {
+ @Override
+ public Class getType() {
+ return Object.class;
+ }
+
+ @Override
+ public Object get() {
+ return null;
+ }
+
+ @Override
+ public void set(Object value) {
+
+ }
+ };
+
+ protected ValueModel() {
+ }
+
+ /**
+ * Returns the type of this property.
+ *
+ * @return the property type
+ */
+ public abstract Class<T> getType();
+
+ /**
+ * Returns the value of this property.
+ *
+ * @return the property value
+ */
+ public abstract T get();
+
+ /**
+ * Sets the value of this property.
+ *
+ * @param value the new value for this property
+ */
+ public abstract void set(T value);
+} \ No newline at end of file
diff --git a/core/java/android/view/AccessibilityInteractionController.java b/core/java/android/view/AccessibilityInteractionController.java
index 9bee4bf..ba82d79 100644
--- a/core/java/android/view/AccessibilityInteractionController.java
+++ b/core/java/android/view/AccessibilityInteractionController.java
@@ -18,6 +18,7 @@ package android.view;
import static android.view.accessibility.AccessibilityNodeInfo.INCLUDE_NOT_IMPORTANT_VIEWS;
+import android.graphics.Point;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.Handler;
@@ -26,6 +27,7 @@ import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
import android.util.SparseLongArray;
+import android.view.View.AttachInfo;
import android.view.accessibility.AccessibilityInteractionClient;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeProvider;
@@ -62,7 +64,10 @@ final class AccessibilityInteractionController {
private final ArrayList<View> mTempArrayList = new ArrayList<View>();
+ private final Point mTempPoint = new Point();
private final Rect mTempRect = new Rect();
+ private final Rect mTempRect1 = new Rect();
+ private final Rect mTempRect2 = new Rect();
public AccessibilityInteractionController(ViewRootImpl viewRootImpl) {
Looper looper = viewRootImpl.mHandler.getLooper();
@@ -86,7 +91,7 @@ final class AccessibilityInteractionController {
public void findAccessibilityNodeInfoByAccessibilityIdClientThread(
long accessibilityNodeId, int interactionId,
IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
- long interrogatingTid) {
+ long interrogatingTid, MagnificationSpec spec) {
Message message = mHandler.obtainMessage();
message.what = PrivateHandler.MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID;
message.arg1 = flags;
@@ -96,6 +101,7 @@ final class AccessibilityInteractionController {
args.argi2 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId);
args.argi3 = interactionId;
args.arg1 = callback;
+ args.arg2 = spec;
message.obj = args;
// If the interrogation is performed by the same thread as the main UI
@@ -119,6 +125,7 @@ final class AccessibilityInteractionController {
final int interactionId = args.argi3;
final IAccessibilityInteractionConnectionCallback callback =
(IAccessibilityInteractionConnectionCallback) args.arg1;
+ final MagnificationSpec spec = (MagnificationSpec) args.arg2;
args.recycle();
@@ -142,7 +149,10 @@ final class AccessibilityInteractionController {
} finally {
try {
mViewRootImpl.mAttachInfo.mIncludeNotImportantViews = false;
- applyApplicationScaleIfNeeded(infos);
+ applyAppScaleAndMagnificationSpecIfNeeded(infos, spec);
+ if (spec != null) {
+ spec.recycle();
+ }
callback.setFindAccessibilityNodeInfosResult(infos, interactionId);
infos.clear();
} catch (RemoteException re) {
@@ -153,7 +163,7 @@ final class AccessibilityInteractionController {
public void findAccessibilityNodeInfoByViewIdClientThread(long accessibilityNodeId,
int viewId, int interactionId, IAccessibilityInteractionConnectionCallback callback,
- int flags, int interrogatingPid, long interrogatingTid) {
+ int flags, int interrogatingPid, long interrogatingTid, MagnificationSpec spec) {
Message message = mHandler.obtainMessage();
message.what = PrivateHandler.MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID;
message.arg1 = flags;
@@ -163,6 +173,7 @@ final class AccessibilityInteractionController {
args.argi1 = viewId;
args.argi2 = interactionId;
args.arg1 = callback;
+ args.arg2 = spec;
message.obj = args;
@@ -187,6 +198,7 @@ final class AccessibilityInteractionController {
final int interactionId = args.argi2;
final IAccessibilityInteractionConnectionCallback callback =
(IAccessibilityInteractionConnectionCallback) args.arg1;
+ final MagnificationSpec spec = (MagnificationSpec) args.arg2;
args.recycle();
@@ -212,7 +224,10 @@ final class AccessibilityInteractionController {
} finally {
try {
mViewRootImpl.mAttachInfo.mIncludeNotImportantViews = false;
- applyApplicationScaleIfNeeded(info);
+ applyAppScaleAndMagnificationSpecIfNeeded(info, spec);
+ if (spec != null) {
+ spec.recycle();
+ }
callback.setFindAccessibilityNodeInfoResult(info, interactionId);
} catch (RemoteException re) {
/* ignore - the other side will time out */
@@ -222,7 +237,7 @@ final class AccessibilityInteractionController {
public void findAccessibilityNodeInfosByTextClientThread(long accessibilityNodeId,
String text, int interactionId, IAccessibilityInteractionConnectionCallback callback,
- int flags, int interrogatingPid, long interrogatingTid) {
+ int flags, int interrogatingPid, long interrogatingTid, MagnificationSpec spec) {
Message message = mHandler.obtainMessage();
message.what = PrivateHandler.MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT;
message.arg1 = flags;
@@ -230,10 +245,10 @@ final class AccessibilityInteractionController {
SomeArgs args = SomeArgs.obtain();
args.arg1 = text;
args.arg2 = callback;
+ args.arg3 = spec;
args.argi1 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
args.argi2 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId);
args.argi3 = interactionId;
-
message.obj = args;
// If the interrogation is performed by the same thread as the main UI
@@ -255,6 +270,7 @@ final class AccessibilityInteractionController {
final String text = (String) args.arg1;
final IAccessibilityInteractionConnectionCallback callback =
(IAccessibilityInteractionConnectionCallback) args.arg2;
+ final MagnificationSpec spec = (MagnificationSpec) args.arg3;
final int accessibilityViewId = args.argi1;
final int virtualDescendantId = args.argi2;
final int interactionId = args.argi3;
@@ -310,7 +326,10 @@ final class AccessibilityInteractionController {
} finally {
try {
mViewRootImpl.mAttachInfo.mIncludeNotImportantViews = false;
- applyApplicationScaleIfNeeded(infos);
+ applyAppScaleAndMagnificationSpecIfNeeded(infos, spec);
+ if (spec != null) {
+ spec.recycle();
+ }
callback.setFindAccessibilityNodeInfosResult(infos, interactionId);
} catch (RemoteException re) {
/* ignore - the other side will time out */
@@ -320,7 +339,7 @@ final class AccessibilityInteractionController {
public void findFocusClientThread(long accessibilityNodeId, int focusType, int interactionId,
IAccessibilityInteractionConnectionCallback callback, int flags, int interogatingPid,
- long interrogatingTid) {
+ long interrogatingTid, MagnificationSpec spec) {
Message message = mHandler.obtainMessage();
message.what = PrivateHandler.MSG_FIND_FOCUS;
message.arg1 = flags;
@@ -331,6 +350,7 @@ final class AccessibilityInteractionController {
args.argi2 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
args.argi3 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId);
args.arg1 = callback;
+ args.arg2 = spec;
message.obj = args;
@@ -356,7 +376,7 @@ final class AccessibilityInteractionController {
final int virtualDescendantId = args.argi3;
final IAccessibilityInteractionConnectionCallback callback =
(IAccessibilityInteractionConnectionCallback) args.arg1;
-
+ final MagnificationSpec spec = (MagnificationSpec) args.arg2;
args.recycle();
AccessibilityNodeInfo focused = null;
@@ -407,7 +427,10 @@ final class AccessibilityInteractionController {
} finally {
try {
mViewRootImpl.mAttachInfo.mIncludeNotImportantViews = false;
- applyApplicationScaleIfNeeded(focused);
+ applyAppScaleAndMagnificationSpecIfNeeded(focused, spec);
+ if (spec != null) {
+ spec.recycle();
+ }
callback.setFindAccessibilityNodeInfoResult(focused, interactionId);
} catch (RemoteException re) {
/* ignore - the other side will time out */
@@ -417,7 +440,7 @@ final class AccessibilityInteractionController {
public void focusSearchClientThread(long accessibilityNodeId, int direction, int interactionId,
IAccessibilityInteractionConnectionCallback callback, int flags, int interogatingPid,
- long interrogatingTid) {
+ long interrogatingTid, MagnificationSpec spec) {
Message message = mHandler.obtainMessage();
message.what = PrivateHandler.MSG_FOCUS_SEARCH;
message.arg1 = flags;
@@ -427,6 +450,7 @@ final class AccessibilityInteractionController {
args.argi2 = direction;
args.argi3 = interactionId;
args.arg1 = callback;
+ args.arg2 = spec;
message.obj = args;
@@ -451,6 +475,7 @@ final class AccessibilityInteractionController {
final int interactionId = args.argi3;
final IAccessibilityInteractionConnectionCallback callback =
(IAccessibilityInteractionConnectionCallback) args.arg1;
+ final MagnificationSpec spec = (MagnificationSpec) args.arg2;
args.recycle();
@@ -476,7 +501,10 @@ final class AccessibilityInteractionController {
} finally {
try {
mViewRootImpl.mAttachInfo.mIncludeNotImportantViews = false;
- applyApplicationScaleIfNeeded(next);
+ applyAppScaleAndMagnificationSpecIfNeeded(next, spec);
+ if (spec != null) {
+ spec.recycle();
+ }
callback.setFindAccessibilityNodeInfoResult(next, interactionId);
} catch (RemoteException re) {
/* ignore - the other side will time out */
@@ -572,38 +600,84 @@ final class AccessibilityInteractionController {
return foundView;
}
- private void applyApplicationScaleIfNeeded(List<AccessibilityNodeInfo> infos) {
+ private void applyAppScaleAndMagnificationSpecIfNeeded(List<AccessibilityNodeInfo> infos,
+ MagnificationSpec spec) {
if (infos == null) {
return;
}
final float applicationScale = mViewRootImpl.mAttachInfo.mApplicationScale;
- if (applicationScale != 1.0f) {
+ if (shouldApplyAppScaleAndMagnificationSpec(applicationScale, spec)) {
final int infoCount = infos.size();
for (int i = 0; i < infoCount; i++) {
AccessibilityNodeInfo info = infos.get(i);
- applyApplicationScaleIfNeeded(info);
+ applyAppScaleAndMagnificationSpecIfNeeded(info, spec);
}
}
}
- private void applyApplicationScaleIfNeeded(AccessibilityNodeInfo info) {
+ private void applyAppScaleAndMagnificationSpecIfNeeded(AccessibilityNodeInfo info,
+ MagnificationSpec spec) {
if (info == null) {
return;
}
+
final float applicationScale = mViewRootImpl.mAttachInfo.mApplicationScale;
+ if (!shouldApplyAppScaleAndMagnificationSpec(applicationScale, spec)) {
+ return;
+ }
+
+ Rect boundsInParent = mTempRect;
+ Rect boundsInScreen = mTempRect1;
+
+ info.getBoundsInParent(boundsInParent);
+ info.getBoundsInScreen(boundsInScreen);
if (applicationScale != 1.0f) {
- Rect bounds = mTempRect;
+ boundsInParent.scale(applicationScale);
+ boundsInScreen.scale(applicationScale);
+ }
+ if (spec != null) {
+ boundsInParent.scale(spec.scale);
+ // boundsInParent must not be offset.
+ boundsInScreen.scale(spec.scale);
+ boundsInScreen.offset((int) spec.offsetX, (int) spec.offsetY);
+ }
+ info.setBoundsInParent(boundsInParent);
+ info.setBoundsInScreen(boundsInScreen);
+
+ if (spec != null) {
+ AttachInfo attachInfo = mViewRootImpl.mAttachInfo;
+ if (attachInfo.mDisplay == null) {
+ return;
+ }
- info.getBoundsInParent(bounds);
- bounds.scale(applicationScale);
- info.setBoundsInParent(bounds);
+ final float scale = attachInfo.mApplicationScale * spec.scale;
- info.getBoundsInScreen(bounds);
- bounds.scale(applicationScale);
- info.setBoundsInScreen(bounds);
+ Rect visibleWinFrame = mTempRect1;
+ visibleWinFrame.left = (int) (attachInfo.mWindowLeft * scale + spec.offsetX);
+ visibleWinFrame.top = (int) (attachInfo.mWindowTop * scale + spec.offsetY);
+ visibleWinFrame.right = (int) (visibleWinFrame.left + mViewRootImpl.mWidth * scale);
+ visibleWinFrame.bottom = (int) (visibleWinFrame.top + mViewRootImpl.mHeight * scale);
+
+ attachInfo.mDisplay.getRealSize(mTempPoint);
+ final int displayWidth = mTempPoint.x;
+ final int displayHeight = mTempPoint.y;
+
+ Rect visibleDisplayFrame = mTempRect2;
+ visibleDisplayFrame.set(0, 0, displayWidth, displayHeight);
+
+ visibleWinFrame.intersect(visibleDisplayFrame);
+
+ if (!visibleWinFrame.intersects(boundsInScreen.left, boundsInScreen.top,
+ boundsInScreen.right, boundsInScreen.bottom)) {
+ info.setVisibleToUser(false);
+ }
}
}
+ private boolean shouldApplyAppScaleAndMagnificationSpec(float appScale,
+ MagnificationSpec spec) {
+ return (appScale != 1.0f || (spec != null && !spec.isNop()));
+ }
/**
* This class encapsulates a prefetching strategy for the accessibility APIs for
diff --git a/core/java/android/view/DisplayList.java b/core/java/android/view/DisplayList.java
index 5e34a36..e996e67 100644
--- a/core/java/android/view/DisplayList.java
+++ b/core/java/android/view/DisplayList.java
@@ -28,6 +28,8 @@ import android.graphics.Matrix;
* @hide
*/
public abstract class DisplayList {
+ private boolean mDirty;
+
/**
* Flag used when calling
* {@link HardwareCanvas#drawDisplayList(DisplayList, android.graphics.Rect, int)}
@@ -96,6 +98,28 @@ public abstract class DisplayList {
public abstract void clear();
/**
+ * Sets the dirty flag. When a display list is dirty, both
+ * {@link #invalidate()} and {@link #clear()} should be invoked whenever
+ * possible.
+ *
+ * @param dirty True to mark the display list dirty, false otherwise
+ *
+ * @see #isDirty()
+ */
+ public void setDirty(boolean dirty) {
+ mDirty = dirty;
+ }
+
+ /**
+ * Indicates whether the display list is dirty.
+ *
+ * @see #setDirty(boolean)
+ */
+ public boolean isDirty() {
+ return mDirty;
+ }
+
+ /**
* Returns whether the display list is currently usable. If this returns false,
* the display list should be re-recorded prior to replaying it.
*
diff --git a/core/java/android/view/GLES20RecordingCanvas.java b/core/java/android/view/GLES20RecordingCanvas.java
index d2df45a..168ae81 100644
--- a/core/java/android/view/GLES20RecordingCanvas.java
+++ b/core/java/android/view/GLES20RecordingCanvas.java
@@ -24,10 +24,7 @@ import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Shader;
-import android.util.Pool;
-import android.util.Poolable;
-import android.util.PoolableManager;
-import android.util.Pools;
+import android.util.Pools.SynchronizedPool;
/**
* An implementation of a GL canvas that records drawing operations.
@@ -35,26 +32,13 @@ import android.util.Pools;
* Bitmap objects that it draws, preventing the backing memory of Bitmaps from being freed while
* the DisplayList is still holding a native reference to the memory.
*/
-class GLES20RecordingCanvas extends GLES20Canvas implements Poolable<GLES20RecordingCanvas> {
+class GLES20RecordingCanvas extends GLES20Canvas {
// The recording canvas pool should be large enough to handle a deeply nested
// view hierarchy because display lists are generated recursively.
private static final int POOL_LIMIT = 25;
- private static final Pool<GLES20RecordingCanvas> sPool = Pools.synchronizedPool(
- Pools.finitePool(new PoolableManager<GLES20RecordingCanvas>() {
- public GLES20RecordingCanvas newInstance() {
- return new GLES20RecordingCanvas();
- }
- @Override
- public void onAcquired(GLES20RecordingCanvas element) {
- }
- @Override
- public void onReleased(GLES20RecordingCanvas element) {
- }
- }, POOL_LIMIT));
-
- private GLES20RecordingCanvas mNextPoolable;
- private boolean mIsPooled;
+ private static final SynchronizedPool<GLES20RecordingCanvas> sPool =
+ new SynchronizedPool<GLES20RecordingCanvas>(POOL_LIMIT);
private GLES20DisplayList mDisplayList;
@@ -64,6 +48,9 @@ class GLES20RecordingCanvas extends GLES20Canvas implements Poolable<GLES20Recor
static GLES20RecordingCanvas obtain(GLES20DisplayList displayList) {
GLES20RecordingCanvas canvas = sPool.acquire();
+ if (canvas == null) {
+ canvas = new GLES20RecordingCanvas();
+ }
canvas.mDisplayList = displayList;
return canvas;
}
@@ -300,24 +287,4 @@ class GLES20RecordingCanvas extends GLES20Canvas implements Poolable<GLES20Recor
colorOffset, indices, indexOffset, indexCount, paint);
recordShaderBitmap(paint);
}
-
- @Override
- public GLES20RecordingCanvas getNextPoolable() {
- return mNextPoolable;
- }
-
- @Override
- public void setNextPoolable(GLES20RecordingCanvas element) {
- mNextPoolable = element;
- }
-
- @Override
- public boolean isPooled() {
- return mIsPooled;
- }
-
- @Override
- public void setPooled(boolean isPooled) {
- mIsPooled = isPooled;
- }
}
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index 5b7a5af..157c321 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -76,16 +76,6 @@ public abstract class HardwareRenderer {
* "false", to disable partial invalidates
*/
static final String RENDER_DIRTY_REGIONS_PROPERTY = "debug.hwui.render_dirty_regions";
-
- /**
- * System property used to enable or disable vsync.
- * The default value of this property is assumed to be false.
- *
- * Possible values:
- * "true", to disable vsync
- * "false", to enable vsync
- */
- static final String DISABLE_VSYNC_PROPERTY = "debug.hwui.disable_vsync";
/**
* System property used to enable or disable hardware rendering profiling.
@@ -323,6 +313,20 @@ public abstract class HardwareRenderer {
abstract long getFrameCount();
/**
+ * Loads system properties used by the renderer. This method is invoked
+ * whenever system properties are modified. Implementations can use this
+ * to trigger live updates of the renderer based on properties.
+ *
+ * @param surface The surface to update with the new properties.
+ * Can be null.
+ *
+ * @return True if a property has changed.
+ */
+ abstract boolean loadSystemProperties(Surface surface);
+
+ private static native boolean nLoadProperties();
+
+ /**
* Sets the directory to use as a persistent storage for hardware rendering
* resources.
*
@@ -373,15 +377,6 @@ public abstract class HardwareRenderer {
private static native boolean nIsBackBufferPreserved();
/**
- * Disables v-sync. For performance testing only.
- */
- static void disableVsync() {
- nDisableVsync();
- }
-
- private static native void nDisableVsync();
-
- /**
* Indicates that the specified hardware layer needs to be updated
* as soon as possible.
*
@@ -652,15 +647,13 @@ public abstract class HardwareRenderer {
boolean mDirtyRegionsEnabled;
boolean mUpdateDirtyRegions;
- final boolean mVsyncDisabled;
-
- final boolean mProfileEnabled;
- final float[] mProfileData;
- final ReentrantLock mProfileLock;
+ boolean mProfileEnabled;
+ float[] mProfileData;
+ ReentrantLock mProfileLock;
int mProfileCurrentFrame = -PROFILE_FRAME_DATA_COUNT;
- final boolean mDebugDirtyRegions;
- final boolean mShowOverdraw;
+ boolean mDebugDirtyRegions;
+ boolean mShowOverdraw;
final int mGlVersion;
final boolean mTranslucent;
@@ -675,44 +668,67 @@ public abstract class HardwareRenderer {
GlRenderer(int glVersion, boolean translucent) {
mGlVersion = glVersion;
mTranslucent = translucent;
-
- String property;
- property = SystemProperties.get(DISABLE_VSYNC_PROPERTY, "false");
- mVsyncDisabled = "true".equalsIgnoreCase(property);
- if (mVsyncDisabled) {
- Log.d(LOG_TAG, "Disabling v-sync");
- }
+ loadSystemProperties(null);
+ }
- property = SystemProperties.get(PROFILE_PROPERTY, "false");
- mProfileEnabled = "true".equalsIgnoreCase(property);
- if (mProfileEnabled) {
- Log.d(LOG_TAG, "Profiling hardware renderer");
- }
+ @Override
+ boolean loadSystemProperties(Surface surface) {
+ boolean changed = false;
- if (mProfileEnabled) {
- property = SystemProperties.get(PROFILE_MAXFRAMES_PROPERTY,
- Integer.toString(PROFILE_MAX_FRAMES));
- int maxProfileFrames = Integer.valueOf(property);
- mProfileData = new float[maxProfileFrames * PROFILE_FRAME_DATA_COUNT];
- for (int i = 0; i < mProfileData.length; i += PROFILE_FRAME_DATA_COUNT) {
- mProfileData[i] = mProfileData[i + 1] = mProfileData[i + 2] = -1;
+ boolean value = SystemProperties.getBoolean(PROFILE_PROPERTY, false);
+ if (value != mProfileEnabled) {
+ changed = true;
+ mProfileEnabled = value;
+
+ if (mProfileEnabled) {
+ Log.d(LOG_TAG, "Profiling hardware renderer");
}
- mProfileLock = new ReentrantLock();
- } else {
- mProfileData = null;
- mProfileLock = null;
+ if (mProfileEnabled) {
+ int maxProfileFrames = SystemProperties.getInt(PROFILE_MAXFRAMES_PROPERTY,
+ PROFILE_MAX_FRAMES);
+ mProfileData = new float[maxProfileFrames * PROFILE_FRAME_DATA_COUNT];
+ for (int i = 0; i < mProfileData.length; i += PROFILE_FRAME_DATA_COUNT) {
+ mProfileData[i] = mProfileData[i + 1] = mProfileData[i + 2] = -1;
+ }
+
+ mProfileLock = new ReentrantLock();
+ } else {
+ mProfileData = null;
+ mProfileLock = null;
+ }
}
- property = SystemProperties.get(DEBUG_DIRTY_REGIONS_PROPERTY, "false");
- mDebugDirtyRegions = "true".equalsIgnoreCase(property);
- if (mDebugDirtyRegions) {
- Log.d(LOG_TAG, "Debugging dirty regions");
+ value = SystemProperties.getBoolean(DEBUG_DIRTY_REGIONS_PROPERTY, false);
+ if (value != mDebugDirtyRegions) {
+ changed = true;
+ mDebugDirtyRegions = value;
+
+ if (mDebugDirtyRegions) {
+ Log.d(LOG_TAG, "Debugging dirty regions");
+ }
}
- mShowOverdraw = SystemProperties.getBoolean(
+ value = SystemProperties.getBoolean(
HardwareRenderer.DEBUG_SHOW_OVERDRAW_PROPERTY, false);
+ if (value != mShowOverdraw) {
+ changed = true;
+ mShowOverdraw = value;
+
+ if (surface != null && isEnabled()) {
+ if (validate()) {
+ sEglConfig = loadEglConfig();
+ invalidate(surface);
+ }
+ }
+ }
+
+ if (nLoadProperties()) {
+ changed = true;
+ }
+
+ return changed;
}
@Override
@@ -842,19 +858,7 @@ public abstract class HardwareRenderer {
checkEglErrorsForced();
- sEglConfig = chooseEglConfig();
- if (sEglConfig == null) {
- // We tried to use EGL_SWAP_BEHAVIOR_PRESERVED_BIT, try again without
- if (sDirtyRegions) {
- sDirtyRegions = false;
- sEglConfig = chooseEglConfig();
- if (sEglConfig == null) {
- throw new RuntimeException("eglConfig not initialized");
- }
- } else {
- throw new RuntimeException("eglConfig not initialized");
- }
- }
+ sEglConfig = loadEglConfig();
}
}
@@ -868,6 +872,23 @@ public abstract class HardwareRenderer {
}
}
+ private EGLConfig loadEglConfig() {
+ EGLConfig eglConfig = chooseEglConfig();
+ if (eglConfig == null) {
+ // We tried to use EGL_SWAP_BEHAVIOR_PRESERVED_BIT, try again without
+ if (sDirtyRegions) {
+ sDirtyRegions = false;
+ eglConfig = chooseEglConfig();
+ if (eglConfig == null) {
+ throw new RuntimeException("eglConfig not initialized");
+ }
+ } else {
+ throw new RuntimeException("eglConfig not initialized");
+ }
+ }
+ return eglConfig;
+ }
+
abstract ManagedEGLContext createManagedContext(EGLContext eglContext);
private EGLConfig chooseEglConfig() {
@@ -1484,14 +1505,6 @@ public abstract class HardwareRenderer {
}
@Override
- void setup(int width, int height) {
- super.setup(width, height);
- if (mVsyncDisabled) {
- disableVsync();
- }
- }
-
- @Override
void pushLayerUpdate(HardwareLayer layer) {
mGlCanvas.pushLayerUpdate(layer);
}
diff --git a/core/java/android/view/IDisplayContentChangeListener.aidl b/core/java/android/view/IDisplayMagnificationController.aidl
index ef7edea..efe2775 100644
--- a/core/java/android/view/IDisplayContentChangeListener.aidl
+++ b/core/java/android/view/IDisplayMagnificationController.aidl
@@ -1,7 +1,7 @@
/*
** Copyright 2012, The Android Open Source Project
**
-** Licensed under the Apache License, Version 2.0 (the "License")
+** 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
**
@@ -16,18 +16,12 @@
package android.view;
-import android.os.IBinder;
-import android.view.WindowInfo;
-import android.graphics.Rect;
-
/**
- * Interface for observing content changes on a display.
- *
* {@hide}
*/
-oneway interface IDisplayContentChangeListener {
- void onWindowTransition(int displayId, int transition, in WindowInfo info);
- void onRectangleOnScreenRequested(int displayId, in Rect rectangle, boolean immediate);
- void onWindowLayersChanged(int displayId);
+oneway interface IDisplayMagnificationController {
+ void onMagnifedFrameChanged(int left, int top, int right, int bottom);
+ void onRectangleOnScreenRequested(int left, int top, int right, int bottom);
void onRotationChanged(int rotation);
+ void onUserContextChanged();
}
diff --git a/core/java/android/view/IDisplayMagnificationMediator.aidl b/core/java/android/view/IDisplayMagnificationMediator.aidl
new file mode 100644
index 0000000..aa25dac
--- /dev/null
+++ b/core/java/android/view/IDisplayMagnificationMediator.aidl
@@ -0,0 +1,31 @@
+/*
+** Copyright 2012, 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.view;
+
+import android.view.IDisplayMagnificationController;
+import android.view.MagnificationSpec;
+
+/**
+ * {@hide}
+ */
+interface IDisplayMagnificationMediator {
+ void addController(int displayId, in IDisplayMagnificationController controller);
+ void removeController(in IDisplayMagnificationController controller);
+ void setMagnificationSpec(in IDisplayMagnificationController controller,
+ in MagnificationSpec spec);
+ MagnificationSpec getCompatibleMagnificationSpec(in IBinder windowToken);
+}
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 2b6cbcf..17f04e9 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -27,17 +27,17 @@ import android.graphics.Rect;
import android.os.Bundle;
import android.os.IRemoteCallback;
import android.view.IApplicationToken;
-import android.view.IDisplayContentChangeListener;
+import android.view.IDisplayMagnificationMediator;
import android.view.IOnKeyguardExitResult;
import android.view.IRotationWatcher;
import android.view.IWindowSession;
import android.view.KeyEvent;
import android.view.InputEvent;
+import android.view.MagnificationSpec;
import android.view.MotionEvent;
import android.view.InputChannel;
import android.view.InputDevice;
import android.view.IInputFilter;
-import android.view.WindowInfo;
/**
* System private interface to the window manager.
@@ -74,7 +74,7 @@ interface IWindowManager
void setEventDispatching(boolean enabled);
void addWindowToken(IBinder token, int type);
void removeWindowToken(IBinder token);
- void addAppToken(int addPos, int userId, IApplicationToken token,
+ void addAppToken(int addPos, IApplicationToken token,
int groupId, int requestedOrientation, boolean fullscreen, boolean showWhenLocked);
void setAppGroupId(IBinder token, int groupId);
void setAppOrientation(IApplicationToken token, int requestedOrientation);
@@ -221,39 +221,19 @@ interface IWindowManager
IBinder getFocusedWindowToken();
/**
- * Gets the compatibility scale of e window given its token.
- */
- float getWindowCompatibilityScale(IBinder windowToken);
-
- /**
* Sets an input filter for manipulating the input event stream.
*/
void setInputFilter(in IInputFilter filter);
/**
- * Sets the scale and offset for implementing accessibility magnification.
- */
- void magnifyDisplay(int dipslayId, float scale, float offsetX, float offsetY);
-
- /**
- * Adds a listener for display content changes.
- */
- void addDisplayContentChangeListener(int displayId, IDisplayContentChangeListener listener);
-
- /**
- * Removes a listener for display content changes.
- */
- void removeDisplayContentChangeListener(int displayId, IDisplayContentChangeListener listener);
-
- /**
- * Gets the info for a window given its token.
+ * Gets the display magnification mediator.
*/
- WindowInfo getWindowInfo(IBinder token);
+ IDisplayMagnificationMediator getDisplayMagnificationMediator();
/**
- * Gets the infos for all visible windows.
+ * Gets the frame of a window given its token.
*/
- void getVisibleWindowsForDisplay(int displayId, out List<WindowInfo> outInfos);
+ void getWindowFrame(IBinder token, out Rect outFrame);
/**
* Device is in safe mode.
diff --git a/core/java/android/view/WindowInfo.aidl b/core/java/android/view/MagnificationSpec.aidl
index 23e927a..d5fbdef 100644
--- a/core/java/android/view/WindowInfo.aidl
+++ b/core/java/android/view/MagnificationSpec.aidl
@@ -17,4 +17,4 @@
package android.view;
-parcelable WindowInfo;
+parcelable MagnificationSpec;
diff --git a/core/java/android/view/MagnificationSpec.java b/core/java/android/view/MagnificationSpec.java
new file mode 100644
index 0000000..7fb5615
--- /dev/null
+++ b/core/java/android/view/MagnificationSpec.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2012 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.view;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Pools.SynchronizedPool;
+
+/**
+ * This class represents spec for performing screen magnification.
+ *
+ * @hide
+ */
+public class MagnificationSpec implements Parcelable {
+ private static final int MAX_POOL_SIZE = 20;
+ private static final SynchronizedPool<MagnificationSpec> sPool =
+ new SynchronizedPool<MagnificationSpec>(MAX_POOL_SIZE);
+
+ public float scale = 1.0f;
+ public float offsetX;
+ public float offsetY;
+
+ private MagnificationSpec() {
+ /* do nothing - reducing visibility */
+ }
+
+ public void initialize(float scale, float offsetX, float offsetY) {
+ this.scale = scale;
+ this.offsetX = offsetX;
+ this.offsetY = offsetY;
+ }
+
+ public boolean isNop() {
+ return scale == 1.0f && offsetX == 0 && offsetY == 0;
+ }
+
+ public static MagnificationSpec obtain(MagnificationSpec other) {
+ MagnificationSpec info = obtain();
+ info.scale = other.scale;
+ info.offsetX = other.offsetX;
+ info.offsetY = other.offsetY;
+ return info;
+ }
+
+ public static MagnificationSpec obtain() {
+ MagnificationSpec spec = sPool.acquire();
+ return (spec != null) ? spec : new MagnificationSpec();
+ }
+
+ public void recycle() {
+ clear();
+ sPool.release(this);
+ }
+
+ public void clear() {
+ scale = 1.0f;
+ offsetX = 0.0f;
+ offsetY = 0.0f;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeFloat(scale);
+ parcel.writeFloat(offsetX);
+ parcel.writeFloat(offsetY);
+ recycle();
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("<scale:");
+ builder.append(scale);
+ builder.append(",offsetX:");
+ builder.append(offsetX);
+ builder.append(",offsetY:");
+ builder.append(offsetY);
+ builder.append(">");
+ return builder.toString();
+ }
+
+ private void initFromParcel(Parcel parcel) {
+ scale = parcel.readFloat();
+ offsetX = parcel.readFloat();
+ offsetY = parcel.readFloat();
+ }
+
+ public static final Creator<MagnificationSpec> CREATOR = new Creator<MagnificationSpec>() {
+ @Override
+ public MagnificationSpec[] newArray(int size) {
+ return new MagnificationSpec[size];
+ }
+
+ @Override
+ public MagnificationSpec createFromParcel(Parcel parcel) {
+ MagnificationSpec spec = MagnificationSpec.obtain();
+ spec.initFromParcel(parcel);
+ return spec;
+ }
+ };
+}
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index 0a81a71..f2c5eac 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -225,9 +225,6 @@ public class Surface implements Parcelable {
// non compatibility mode.
private Matrix mCompatibleMatrix;
- private int mWidth;
- private int mHeight;
-
private native void nativeCreate(SurfaceSession session, String name,
int w, int h, int format, int flags)
throws OutOfResourcesException;
@@ -333,8 +330,6 @@ public class Surface implements Parcelable {
checkHeadless();
mName = name;
- mWidth = w;
- mHeight = h;
nativeCreate(session, name, w, h, format, flags);
mCloseGuard.open("release");
@@ -543,7 +538,7 @@ public class Surface implements Parcelable {
/** @hide */
public void setPosition(int x, int y) {
- nativeSetPosition(x, y);
+ nativeSetPosition((float)x, (float)y);
}
/** @hide */
@@ -553,22 +548,10 @@ public class Surface implements Parcelable {
/** @hide */
public void setSize(int w, int h) {
- mWidth = w;
- mHeight = h;
nativeSetSize(w, h);
}
/** @hide */
- public int getWidth() {
- return mWidth;
- }
-
- /** @hide */
- public int getHeight() {
- return mHeight;
- }
-
- /** @hide */
public void hide() {
nativeSetFlags(SURFACE_HIDDEN, SURFACE_HIDDEN);
}
@@ -825,6 +808,32 @@ public class Surface implements Parcelable {
}
/**
+ * Returns a human readable representation of a rotation.
+ *
+ * @param rotation The rotation.
+ * @return The rotation symbolic name.
+ */
+ public static String rotationToString(int rotation) {
+ switch (rotation) {
+ case Surface.ROTATION_0: {
+ return "ROTATION_0";
+ }
+ case Surface.ROTATION_90: {
+ return "ROATATION_90";
+ }
+ case Surface.ROTATION_180: {
+ return "ROATATION_180";
+ }
+ case Surface.ROTATION_270: {
+ return "ROATATION_270";
+ }
+ default: {
+ throw new IllegalArgumentException("Invalid rotation: " + rotation);
+ }
+ }
+ }
+
+ /**
* A Canvas class that can handle the compatibility mode.
* This does two things differently.
* <ul>
diff --git a/core/java/android/view/VelocityTracker.java b/core/java/android/view/VelocityTracker.java
index 82b3963..eb81f72 100644
--- a/core/java/android/view/VelocityTracker.java
+++ b/core/java/android/view/VelocityTracker.java
@@ -16,10 +16,7 @@
package android.view;
-import android.util.Poolable;
-import android.util.Pool;
-import android.util.Pools;
-import android.util.PoolableManager;
+import android.util.Pools.SynchronizedPool;
/**
* Helper for tracking the velocity of touch events, for implementing
@@ -31,30 +28,15 @@ import android.util.PoolableManager;
* {@link #computeCurrentVelocity(int)} and then call {@link #getXVelocity(int)}
* 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(
- Pools.finitePool(new PoolableManager<VelocityTracker>() {
- public VelocityTracker newInstance() {
- return new VelocityTracker(null);
- }
-
- public void onAcquired(VelocityTracker element) {
- // Intentionally empty
- }
-
- public void onReleased(VelocityTracker element) {
- element.clear();
- }
- }, 2));
+public final class VelocityTracker {
+ private static final SynchronizedPool<VelocityTracker> sPool =
+ new SynchronizedPool<VelocityTracker>(2);
private static final int ACTIVE_POINTER_ID = -1;
private int mPtr;
private final String mStrategy;
- private VelocityTracker mNext;
- private boolean mIsPooled;
-
private static native int nativeInitialize(String strategy);
private static native void nativeDispose(int ptr);
private static native void nativeClear(int ptr);
@@ -73,7 +55,8 @@ public final class VelocityTracker implements Poolable<VelocityTracker> {
* @return Returns a new VelocityTracker.
*/
static public VelocityTracker obtain() {
- return sPool.acquire();
+ VelocityTracker instance = sPool.acquire();
+ return (instance != null) ? instance : new VelocityTracker(null);
}
/**
@@ -98,38 +81,11 @@ public final class VelocityTracker implements Poolable<VelocityTracker> {
*/
public void recycle() {
if (mStrategy == null) {
+ clear();
sPool.release(this);
}
}
- /**
- * @hide
- */
- public void setNextPoolable(VelocityTracker element) {
- mNext = element;
- }
-
- /**
- * @hide
- */
- public VelocityTracker getNextPoolable() {
- return mNext;
- }
-
- /**
- * @hide
- */
- public boolean isPooled() {
- return mIsPooled;
- }
-
- /**
- * @hide
- */
- public void setPooled(boolean isPooled) {
- mIsPooled = isPooled;
- }
-
private VelocityTracker(String strategy) {
mPtr = nativeInitialize(strategy);
mStrategy = strategy;
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index f05371a..080e7c0 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -40,6 +40,7 @@ import android.graphics.Shader;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.hardware.display.DisplayManagerGlobal;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -52,10 +53,7 @@ import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.FloatProperty;
import android.util.Log;
-import android.util.Pool;
-import android.util.Poolable;
-import android.util.PoolableManager;
-import android.util.Pools;
+import android.util.Pools.SynchronizedPool;
import android.util.Property;
import android.util.SparseArray;
import android.util.TypedValue;
@@ -691,6 +689,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*/
public static final int NO_ID = -1;
+ private static boolean sUseBrokenMakeMeasureSpec = false;
+
/**
* This view does not want keystrokes. Use with TAKES_FOCUS_MASK when
* calling setFlags.
@@ -3237,6 +3237,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
setOverScrollMode(OVER_SCROLL_IF_CONTENT_SCROLLS);
mUserPaddingStart = UNDEFINED_PADDING;
mUserPaddingEnd = UNDEFINED_PADDING;
+
+ if (!sUseBrokenMakeMeasureSpec && context.getApplicationInfo().targetSdkVersion <=
+ Build.VERSION_CODES.JELLY_BEAN_MR1 ) {
+ // Older apps may need this compatibility hack for measurement.
+ sUseBrokenMakeMeasureSpec = true;
+ }
}
/**
@@ -10750,7 +10756,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
// if we are not attached to our window
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
- final AttachInfo.InvalidateInfo info = AttachInfo.InvalidateInfo.acquire();
+ final AttachInfo.InvalidateInfo info = AttachInfo.InvalidateInfo.obtain();
info.target = this;
info.left = left;
info.top = top;
@@ -10799,7 +10805,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
// if we are not attached to our window
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
- final AttachInfo.InvalidateInfo info = AttachInfo.InvalidateInfo.acquire();
+ final AttachInfo.InvalidateInfo info = AttachInfo.InvalidateInfo.obtain();
info.target = this;
info.left = left;
info.top = top;
@@ -11565,8 +11571,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
imm.focusIn(this);
}
- if (mAttachInfo != null && mDisplayList != null) {
- mAttachInfo.mViewRootImpl.dequeueDisplayList(mDisplayList);
+ if (mDisplayList != null) {
+ mDisplayList.setDirty(false);
}
}
@@ -11846,6 +11852,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
if (mAttachInfo != null) {
if (mDisplayList != null) {
+ mDisplayList.setDirty(true);
mAttachInfo.mViewRootImpl.enqueueDisplayList(mDisplayList);
}
mAttachInfo.mViewRootImpl.cancelInvalidate(this);
@@ -13978,6 +13985,25 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
/**
+ * Return true if o is a ViewGroup that is laying out using optical bounds.
+ * @hide
+ */
+ public static boolean isLayoutModeOptical(Object o) {
+ return o instanceof ViewGroup && ((ViewGroup) o).isLayoutModeOptical();
+ }
+
+ private boolean setOpticalFrame(int left, int top, int right, int bottom) {
+ Insets parentInsets = mParent instanceof View ?
+ ((View) mParent).getOpticalInsets() : Insets.NONE;
+ Insets childInsets = getOpticalInsets();
+ return setFrame(
+ left + parentInsets.left - childInsets.left,
+ top + parentInsets.top - childInsets.top,
+ right + parentInsets.left + childInsets.right,
+ bottom + parentInsets.top + childInsets.bottom);
+ }
+
+ /**
* Assign a size and position to a view and all of its
* descendants
*
@@ -14003,7 +14029,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
int oldT = mTop;
int oldB = mBottom;
int oldR = mRight;
- boolean changed = setFrame(l, t, r, b);
+ boolean changed = isLayoutModeOptical(mParent) ?
+ setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);
if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
onLayout(changed, l, t, r, b);
mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED;
@@ -14445,6 +14472,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
if (mBackground instanceof ColorDrawable) {
((ColorDrawable) mBackground.mutate()).setColor(color);
computeOpaqueFlags();
+ mBackgroundResource = 0;
} else {
setBackground(new ColorDrawable(color));
}
@@ -14819,6 +14847,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
return (mUserPaddingStart != UNDEFINED_PADDING || mUserPaddingEnd != UNDEFINED_PADDING);
}
+ Insets computeOpticalInsets() {
+ return (mBackground == null) ? Insets.NONE : mBackground.getOpticalInsets();
+ }
+
/**
* @hide
*/
@@ -14842,19 +14874,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*/
public Insets getOpticalInsets() {
if (mLayoutInsets == null) {
- mLayoutInsets = (mBackground == null) ? Insets.NONE : mBackground.getLayoutInsets();
+ mLayoutInsets = computeOpticalInsets();
}
return mLayoutInsets;
}
/**
- * @hide
- */
- public void setLayoutInsets(Insets layoutInsets) {
- mLayoutInsets = layoutInsets;
- }
-
- /**
* Changes the selection state of this view. A view can be selected or not.
* Note that selection is not the same as focus. Views are typically
* selected in the context of an AdapterView like ListView or GridView;
@@ -15461,11 +15486,34 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
/**
+ * Returns whether the view hierarchy is currently undergoing a layout pass. This
+ * information is useful to avoid situations such as calling {@link #requestLayout()} during
+ * a layout pass.
+ *
+ * @return whether the view hierarchy is currently undergoing a layout pass
+ */
+ public boolean isInLayout() {
+ ViewRootImpl viewRoot = getViewRootImpl();
+ return (viewRoot != null && viewRoot.isInLayout());
+ }
+
+ /**
* Call this when something has changed which has invalidated the
* layout of this view. This will schedule a layout pass of the view
- * tree.
+ * tree. This should not be called while the view hierarchy is currently in a layout
+ * pass ({@link #isInLayout()}. If layout is happening, the request may be honored at the
+ * end of the current layout pass (and then layout will run again) or after the current
+ * frame is drawn and the next layout occurs.
+ *
+ * <p>Subclasses which override this method should call the superclass method to
+ * handle possible request-during-layout errors correctly.</p>
*/
public void requestLayout() {
+ ViewRootImpl viewRoot = getViewRootImpl();
+ if (viewRoot != null && viewRoot.isInLayout()) {
+ viewRoot.requestLayoutDuringLayout(this);
+ return;
+ }
mPrivateFlags |= PFLAG_FORCE_LAYOUT;
mPrivateFlags |= PFLAG_INVALIDATED;
@@ -15505,6 +15553,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* @see #onMeasure(int, int)
*/
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
+ boolean optical = isLayoutModeOptical(this);
+ if (optical != isLayoutModeOptical(mParent)) {
+ Insets insets = getOpticalInsets();
+ int oWidth = insets.left + insets.right;
+ int oHeight = insets.top + insets.bottom;
+ widthMeasureSpec = MeasureSpec.adjust(widthMeasureSpec, optical ? -oWidth : oWidth);
+ heightMeasureSpec = MeasureSpec.adjust(heightMeasureSpec, optical ? -oHeight : oHeight);
+ }
if ((mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT ||
widthMeasureSpec != mOldWidthMeasureSpec ||
heightMeasureSpec != mOldHeightMeasureSpec) {
@@ -15596,6 +15652,15 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* {@link #MEASURED_STATE_TOO_SMALL}.
*/
protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {
+ boolean optical = isLayoutModeOptical(this);
+ if (optical != isLayoutModeOptical(mParent)) {
+ Insets insets = getOpticalInsets();
+ int opticalWidth = insets.left + insets.right;
+ int opticalHeight = insets.top + insets.bottom;
+
+ measuredWidth += optical ? opticalWidth : -opticalWidth;
+ measuredHeight += optical ? opticalHeight : -opticalHeight;
+ }
mMeasuredWidth = measuredWidth;
mMeasuredHeight = measuredHeight;
@@ -17267,12 +17332,23 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* <li>{@link android.view.View.MeasureSpec#AT_MOST}</li>
* </ul>
*
+ * <p><strong>Note:</strong> On API level 17 and lower, makeMeasureSpec's
+ * implementation was such that the order of arguments did not matter
+ * and overflow in either value could impact the resulting MeasureSpec.
+ * {@link android.widget.RelativeLayout} was affected by this bug.
+ * Apps targeting API levels greater than 17 will get the fixed, more strict
+ * behavior.</p>
+ *
* @param size the size of the measure specification
* @param mode the mode of the measure specification
* @return the measure specification based on size and mode
*/
public static int makeMeasureSpec(int size, int mode) {
- return size + mode;
+ if (sUseBrokenMakeMeasureSpec) {
+ return size + mode;
+ } else {
+ return (size & ~MODE_MASK) | (mode & MODE_MASK);
+ }
}
/**
@@ -17297,6 +17373,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
return (measureSpec & ~MODE_MASK);
}
+ static int adjust(int measureSpec, int delta) {
+ return makeMeasureSpec(getSize(measureSpec + delta), getMode(measureSpec));
+ }
+
/**
* Returns a String representation of the specified measure
* specification.
@@ -17634,25 +17714,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* POOL_LIMIT objects that get reused. This reduces memory allocations
* whenever possible.
*/
- static class InvalidateInfo implements Poolable<InvalidateInfo> {
+ static class InvalidateInfo {
private static final int POOL_LIMIT = 10;
- private static final Pool<InvalidateInfo> sPool = Pools.synchronizedPool(
- Pools.finitePool(new PoolableManager<InvalidateInfo>() {
- public InvalidateInfo newInstance() {
- return new InvalidateInfo();
- }
- public void onAcquired(InvalidateInfo element) {
- }
-
- public void onReleased(InvalidateInfo element) {
- element.target = null;
- }
- }, POOL_LIMIT)
- );
-
- private InvalidateInfo mNext;
- private boolean mIsPooled;
+ private static final SynchronizedPool<InvalidateInfo> sPool =
+ new SynchronizedPool<InvalidateInfo>(POOL_LIMIT);
View target;
@@ -17661,29 +17727,15 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
int right;
int bottom;
- public void setNextPoolable(InvalidateInfo element) {
- mNext = element;
+ public static InvalidateInfo obtain() {
+ InvalidateInfo instance = sPool.acquire();
+ return (instance != null) ? instance : new InvalidateInfo();
}
- public InvalidateInfo getNextPoolable() {
- return mNext;
- }
-
- static InvalidateInfo acquire() {
- return sPool.acquire();
- }
-
- void release() {
+ public void recycle() {
+ target = null;
sPool.release(this);
}
-
- public boolean isPooled() {
- return mIsPooled;
- }
-
- public void setPooled(boolean isPooled) {
- mIsPooled = isPooled;
- }
}
final IWindowSession mSession;
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index dbbcde6..c274f27 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -35,6 +35,7 @@ import android.os.Parcelable;
import android.os.SystemClock;
import android.util.AttributeSet;
import android.util.Log;
+import android.util.Pools.SynchronizedPool;
import android.util.SparseArray;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
@@ -83,6 +84,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
private static final String TAG = "ViewGroup";
private static final boolean DBG = false;
+ /** @hide */
+ public static boolean DEBUG_DRAW = false;
/**
* Views which have been hidden or removed which need to be animated on
@@ -180,10 +183,10 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
})
protected int mGroupFlags;
- /*
- * The layout mode: either {@link #CLIP_BOUNDS} or {@link #OPTICAL_BOUNDS}
+ /**
+ * Either {@link #LAYOUT_MODE_CLIP_BOUNDS} or {@link #LAYOUT_MODE_OPTICAL_BOUNDS}.
*/
- private int mLayoutMode = CLIP_BOUNDS;
+ private int mLayoutMode = DEFAULT_LAYOUT_MODE;
/**
* NOTE: If you change the flags below make sure to reflect the changes
@@ -356,20 +359,19 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
* This constant is a {@link #setLayoutMode(int) layoutMode}.
* Clip bounds are the raw values of {@link #getLeft() left}, {@link #getTop() top},
* {@link #getRight() right} and {@link #getBottom() bottom}.
- *
- * @hide
*/
- public static final int CLIP_BOUNDS = 0;
+ public static final int LAYOUT_MODE_CLIP_BOUNDS = 0;
/**
* This constant is a {@link #setLayoutMode(int) layoutMode}.
* Optical bounds describe where a widget appears to be. They sit inside the clip
* bounds which need to cover a larger area to allow other effects,
* such as shadows and glows, to be drawn.
- *
- * @hide
*/
- public static final int OPTICAL_BOUNDS = 1;
+ public static final int LAYOUT_MODE_OPTICAL_BOUNDS = 1;
+
+ /** @hide */
+ public static int DEFAULT_LAYOUT_MODE = LAYOUT_MODE_CLIP_BOUNDS;
/**
* We clip to padding when FLAG_CLIP_TO_PADDING and FLAG_PADDING_NOT_NULL
@@ -434,7 +436,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
}
private boolean debugDraw() {
- return mAttachInfo != null && mAttachInfo.mDebugLayout;
+ return DEBUG_DRAW || mAttachInfo != null && mAttachInfo.mDebugLayout;
}
private void initViewGroup() {
@@ -504,6 +506,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
setLayoutTransition(new LayoutTransition());
}
break;
+ case R.styleable.ViewGroup_layoutMode:
+ setLayoutMode(a.getInt(attr, DEFAULT_LAYOUT_MODE));
+ break;
}
}
@@ -2420,7 +2425,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
for (int i = 0; i < count; i++) {
final View child = children[i];
child.dispatchAttachedToWindow(info,
- visibility | (child.mViewFlags&VISIBILITY_MASK));
+ visibility | (child.mViewFlags & VISIBILITY_MASK));
}
}
@@ -2682,20 +2687,89 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
return b;
}
- private static void drawRect(Canvas canvas, int x1, int y1, int x2, int y2, int color) {
- Paint paint = getDebugPaint();
- paint.setColor(color);
+ /** Return true if this ViewGroup is laying out using optical bounds. */
+ boolean isLayoutModeOptical() {
+ return mLayoutMode == LAYOUT_MODE_OPTICAL_BOUNDS;
+ }
+
+ Insets computeOpticalInsets() {
+ if (isLayoutModeOptical()) {
+ int left = 0;
+ int top = 0;
+ int right = 0;
+ int bottom = 0;
+ for (int i = 0; i < mChildrenCount; i++) {
+ View child = getChildAt(i);
+ if (child.getVisibility() == VISIBLE) {
+ Insets insets = child.getOpticalInsets();
+ left = Math.max(left, insets.left);
+ top = Math.max(top, insets.top);
+ right = Math.max(right, insets.right);
+ bottom = Math.max(bottom, insets.bottom);
+ }
+ }
+ return Insets.of(left, top, right, bottom);
+ } else {
+ return Insets.NONE;
+ }
+ }
+
+ private static void fillRect(Canvas canvas, Paint paint, int x1, int y1, int x2, int y2) {
+ if (x1 != x2 && y1 != y2) {
+ if (x1 > x2) {
+ int tmp = x1; x1 = x2; x2 = tmp;
+ }
+ if (y1 > y2) {
+ int tmp = y1; y1 = y2; y2 = tmp;
+ }
+ canvas.drawRect(x1, y1, x2, y2, paint);
+ }
+ }
+
+ private static int sign(int x) {
+ return (x >= 0) ? 1 : -1;
+ }
+
+ private static void drawCorner(Canvas c, Paint paint, int x1, int y1, int dx, int dy, int lw) {
+ fillRect(c, paint, x1, y1, x1 + dx, y1 + lw * sign(dy));
+ fillRect(c, paint, x1, y1, x1 + lw * sign(dx), y1 + dy);
+ }
- canvas.drawLines(getDebugLines(x1, y1, x2, y2), paint);
+ private int dipsToPixels(int dips) {
+ float scale = getContext().getResources().getDisplayMetrics().density;
+ return (int) (dips * scale + 0.5f);
+ }
+
+ private void drawRectCorners(Canvas canvas, int x1, int y1, int x2, int y2, Paint paint,
+ int lineLength, int lineWidth) {
+ drawCorner(canvas, paint, x1, y1, lineLength, lineLength, lineWidth);
+ drawCorner(canvas, paint, x1, y2, lineLength, -lineLength, lineWidth);
+ drawCorner(canvas, paint, x2, y1, -lineLength, lineLength, lineWidth);
+ drawCorner(canvas, paint, x2, y2, -lineLength, -lineLength, lineWidth);
+ }
+
+ private static void fillDifference(Canvas canvas,
+ int x2, int y2, int x3, int y3,
+ int dx1, int dy1, int dx2, int dy2, Paint paint) {
+ int x1 = x2 - dx1;
+ int y1 = y2 - dy1;
+
+ int x4 = x3 + dx2;
+ int y4 = y3 + dy2;
+
+ fillRect(canvas, paint, x1, y1, x4, y2);
+ fillRect(canvas, paint, x1, y2, x2, y3);
+ fillRect(canvas, paint, x3, y2, x4, y3);
+ fillRect(canvas, paint, x1, y3, x4, y4);
}
/**
* @hide
*/
- protected void onDebugDrawMargins(Canvas canvas) {
+ protected void onDebugDrawMargins(Canvas canvas, Paint paint) {
for (int i = 0; i < getChildCount(); i++) {
View c = getChildAt(i);
- c.getLayoutParams().onDebugDraw(c, canvas);
+ c.getLayoutParams().onDebugDraw(c, canvas, paint);
}
}
@@ -2703,26 +2777,45 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
* @hide
*/
protected void onDebugDraw(Canvas canvas) {
+ Paint paint = getDebugPaint();
+
// Draw optical bounds
- if (getLayoutMode() == OPTICAL_BOUNDS) {
+ {
+ paint.setColor(Color.RED);
+ paint.setStyle(Paint.Style.STROKE);
+
for (int i = 0; i < getChildCount(); i++) {
View c = getChildAt(i);
Insets insets = c.getOpticalInsets();
- drawRect(canvas,
- c.getLeft() + insets.left,
- c.getTop() + insets.top,
- c.getRight() - insets.right,
- c.getBottom() - insets.bottom, Color.RED);
+
+ drawRect(canvas, paint,
+ c.getLeft() + insets.left,
+ c.getTop() + insets.top,
+ c.getRight() - insets.right - 1,
+ c.getBottom() - insets.bottom - 1);
}
}
// Draw margins
- onDebugDrawMargins(canvas);
+ {
+ paint.setColor(Color.argb(63, 255, 0, 255));
+ paint.setStyle(Paint.Style.FILL);
- // Draw bounds
- for (int i = 0; i < getChildCount(); i++) {
- View c = getChildAt(i);
- drawRect(canvas, c.getLeft(), c.getTop(), c.getRight(), c.getBottom(), Color.BLUE);
+ onDebugDrawMargins(canvas, paint);
+ }
+
+ // Draw clip bounds
+ {
+ paint.setColor(Color.rgb(63, 127, 255));
+ paint.setStyle(Paint.Style.FILL);
+
+ int lineLength = dipsToPixels(8);
+ int lineWidth = dipsToPixels(1);
+ for (int i = 0; i < getChildCount(); i++) {
+ View c = getChildAt(i);
+ drawRectCorners(canvas, c.getLeft(), c.getTop(), c.getRight(), c.getBottom(),
+ paint, lineLength, lineWidth);
+ }
}
}
@@ -3620,8 +3713,6 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
childHasTransientStateChanged(view, false);
}
- onViewRemoved(view);
-
needGlobalAttributesUpdate(false);
removeFromArray(index);
@@ -3634,6 +3725,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
if (view.isAccessibilityFocused()) {
view.clearAccessibilityFocus();
}
+
+ onViewRemoved(view);
}
/**
@@ -4610,13 +4703,11 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
/**
* Returns the basis of alignment during layout operations on this view group:
- * either {@link #CLIP_BOUNDS} or {@link #OPTICAL_BOUNDS}.
+ * either {@link #LAYOUT_MODE_CLIP_BOUNDS} or {@link #LAYOUT_MODE_OPTICAL_BOUNDS}.
*
* @return the layout mode to use during layout operations
*
* @see #setLayoutMode(int)
- *
- * @hide
*/
public int getLayoutMode() {
return mLayoutMode;
@@ -4624,15 +4715,14 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
/**
* Sets the basis of alignment during the layout of this view group.
- * Valid values are either {@link #CLIP_BOUNDS} or {@link #OPTICAL_BOUNDS}.
+ * Valid values are either {@link #LAYOUT_MODE_CLIP_BOUNDS} or
+ * {@link #LAYOUT_MODE_OPTICAL_BOUNDS}.
* <p>
- * The default is {@link #CLIP_BOUNDS}.
+ * The default is {@link #LAYOUT_MODE_CLIP_BOUNDS}.
*
* @param layoutMode the layout mode to use during layout operations
*
* @see #getLayoutMode()
- *
- * @hide
*/
public void setLayoutMode(int layoutMode) {
if (mLayoutMode != layoutMode) {
@@ -5650,7 +5740,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
*
* @hide
*/
- public void onDebugDraw(View view, Canvas canvas) {
+ public void onDebugDraw(View view, Canvas canvas, Paint paint) {
}
/**
@@ -5998,12 +6088,19 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
* @hide
*/
@Override
- public void onDebugDraw(View view, Canvas canvas) {
- drawRect(canvas,
- view.getLeft() - leftMargin,
- view.getTop() - topMargin,
- view.getRight() + rightMargin,
- view.getBottom() + bottomMargin, Color.MAGENTA);
+ public void onDebugDraw(View view, Canvas canvas, Paint paint) {
+ Insets oi = isLayoutModeOptical(view.mParent) ? view.getOpticalInsets() : Insets.NONE;
+
+ fillDifference(canvas,
+ view.getLeft() + oi.left,
+ view.getTop() + oi.top,
+ view.getRight() - oi.right,
+ view.getBottom() - oi.bottom,
+ leftMargin,
+ topMargin,
+ rightMargin,
+ bottomMargin,
+ paint);
}
}
@@ -6119,50 +6216,25 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
private static final int MAX_POOL_SIZE = 32;
- private static final Object sPoolLock = new Object();
-
- private static ChildListForAccessibility sPool;
-
- private static int sPoolSize;
-
- private boolean mIsPooled;
-
- private ChildListForAccessibility mNext;
+ private static final SynchronizedPool<ChildListForAccessibility> sPool =
+ new SynchronizedPool<ChildListForAccessibility>(MAX_POOL_SIZE);
private final ArrayList<View> mChildren = new ArrayList<View>();
private final ArrayList<ViewLocationHolder> mHolders = new ArrayList<ViewLocationHolder>();
public static ChildListForAccessibility obtain(ViewGroup parent, boolean sort) {
- ChildListForAccessibility list = null;
- synchronized (sPoolLock) {
- if (sPool != null) {
- list = sPool;
- sPool = list.mNext;
- list.mNext = null;
- list.mIsPooled = false;
- sPoolSize--;
- } else {
- list = new ChildListForAccessibility();
- }
- list.init(parent, sort);
- return list;
+ ChildListForAccessibility list = sPool.acquire();
+ if (list == null) {
+ list = new ChildListForAccessibility();
}
+ list.init(parent, sort);
+ return list;
}
public void recycle() {
- if (mIsPooled) {
- throw new IllegalStateException("Instance already recycled.");
- }
clear();
- synchronized (sPoolLock) {
- if (sPoolSize < MAX_POOL_SIZE) {
- mNext = sPool;
- mIsPooled = true;
- sPool = this;
- sPoolSize++;
- }
- }
+ sPool.release(this);
}
public int getChildCount() {
@@ -6216,15 +6288,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
private static final int MAX_POOL_SIZE = 32;
- private static final Object sPoolLock = new Object();
-
- private static ViewLocationHolder sPool;
-
- private static int sPoolSize;
-
- private boolean mIsPooled;
-
- private ViewLocationHolder mNext;
+ private static final SynchronizedPool<ViewLocationHolder> sPool =
+ new SynchronizedPool<ViewLocationHolder>(MAX_POOL_SIZE);
private final Rect mLocation = new Rect();
@@ -6233,35 +6298,17 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
private int mLayoutDirection;
public static ViewLocationHolder obtain(ViewGroup root, View view) {
- ViewLocationHolder holder = null;
- synchronized (sPoolLock) {
- if (sPool != null) {
- holder = sPool;
- sPool = holder.mNext;
- holder.mNext = null;
- holder.mIsPooled = false;
- sPoolSize--;
- } else {
- holder = new ViewLocationHolder();
- }
- holder.init(root, view);
- return holder;
+ ViewLocationHolder holder = sPool.acquire();
+ if (holder == null) {
+ holder = new ViewLocationHolder();
}
+ holder.init(root, view);
+ return holder;
}
public void recycle() {
- if (mIsPooled) {
- throw new IllegalStateException("Instance already recycled.");
- }
clear();
- synchronized (sPoolLock) {
- if (sPoolSize < MAX_POOL_SIZE) {
- mNext = sPool;
- mIsPooled = true;
- sPool = this;
- sPoolSize++;
- }
- }
+ sPool.release(this);
}
@Override
@@ -6337,14 +6384,11 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
return sDebugPaint;
}
- private static float[] getDebugLines(int x1, int y1, int x2, int y2) {
+ private void drawRect(Canvas canvas, Paint paint, int x1, int y1, int x2, int y2) {
if (sDebugLines== null) {
sDebugLines = new float[16];
}
- x2--;
- y2--;
-
sDebugLines[0] = x1;
sDebugLines[1] = y1;
sDebugLines[2] = x2;
@@ -6353,18 +6397,18 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
sDebugLines[4] = x2;
sDebugLines[5] = y1;
sDebugLines[6] = x2;
- sDebugLines[7] = y2 + 1;
+ sDebugLines[7] = y2;
- sDebugLines[8] = x2 + 1;
+ sDebugLines[8] = x2;
sDebugLines[9] = y2;
sDebugLines[10] = x1;
sDebugLines[11] = y2;
- sDebugLines[12] = x1;
- sDebugLines[13] = y2;
+ sDebugLines[12] = x1;
+ sDebugLines[13] = y2;
sDebugLines[14] = x1;
sDebugLines[15] = y1;
- return sDebugLines;
+ canvas.drawLines(sDebugLines, paint);
}
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 27d770b..85fab28 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -114,7 +114,7 @@ public final class ViewRootImpl implements ViewParent,
* Set this system property to true to force the view hierarchy to render
* at 60 Hz. This can be used to measure the potential framerate.
*/
- private static final String PROPERTY_PROFILE_RENDERING = "viewancestor.profile_rendering";
+ private static final String PROPERTY_PROFILE_RENDERING = "viewancestor.profile_rendering";
private static final boolean MEASURE_LATENCY = false;
private static LatencyTimer lt;
@@ -289,15 +289,15 @@ public final class ViewRootImpl implements ViewParent,
final PointF mLastTouchPoint = new PointF();
private boolean mProfileRendering;
- private Thread mRenderProfiler;
- private volatile boolean mRenderProfilingEnabled;
+ private Choreographer.FrameCallback mRenderProfiler;
+ private boolean mRenderProfilingEnabled;
// Variables to track frames per second, enabled via DEBUG_FPS flag
private long mFpsStartTime = -1;
private long mFpsPrevTime = -1;
private int mFpsNumFrames;
- private final ArrayList<DisplayList> mDisplayLists = new ArrayList<DisplayList>(24);
+ private final ArrayList<DisplayList> mDisplayLists = new ArrayList<DisplayList>();
/**
* see {@link #playSoundEffect(int)}
@@ -317,6 +317,10 @@ public final class ViewRootImpl implements ViewParent,
private final int mDensity;
private final int mNoncompatDensity;
+ private boolean mInLayout = false;
+ ArrayList<View> mLayoutRequesters = new ArrayList<View>();
+ boolean mHandlingLayoutInLayoutRequest = false;
+
private int mViewLayoutDirectionInitial;
/**
@@ -378,8 +382,6 @@ public final class ViewRootImpl implements ViewParent,
mDensity = context.getResources().getDisplayMetrics().densityDpi;
mNoncompatDensity = context.getResources().getDisplayMetrics().noncompatDensityDpi;
mFallbackEventHandler = PolicyManager.makeNewFallbackEventHandler(context);
- mProfileRendering = Boolean.parseBoolean(
- SystemProperties.get(PROPERTY_PROFILE_RENDERING, "false"));
mChoreographer = Choreographer.getInstance();
PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
@@ -822,9 +824,11 @@ public final class ViewRootImpl implements ViewParent,
@Override
public void requestLayout() {
- checkThread();
- mLayoutRequested = true;
- scheduleTraversals();
+ if (!mHandlingLayoutInLayoutRequest) {
+ checkThread();
+ mLayoutRequested = true;
+ scheduleTraversals();
+ }
}
@Override
@@ -1710,7 +1714,7 @@ public final class ViewRootImpl implements ViewParent,
boolean triggerGlobalLayoutListener = didLayout
|| attachInfo.mRecomputeGlobalAttributes;
if (didLayout) {
- performLayout();
+ performLayout(lp, desiredWindowWidth, desiredWindowHeight);
// By this point all views have been sized and positionned
// We can compute the transparent area
@@ -1879,9 +1883,50 @@ public final class ViewRootImpl implements ViewParent,
}
}
- private void performLayout() {
+ /**
+ * Called by {@link android.view.View#isInLayout()} to determine whether the view hierarchy
+ * is currently undergoing a layout pass.
+ *
+ * @return whether the view hierarchy is currently undergoing a layout pass
+ */
+ boolean isInLayout() {
+ return mInLayout;
+ }
+
+ /**
+ * Called by {@link android.view.View#requestLayout()} if the view hiearchy is currently
+ * undergoing a layout pass. requestLayout() should not be called during layout, but if it
+ * is called anyway, we handle the situation here rather than leave the hierarchy in an
+ * indeterminate state. The solution is to queue up all requests during layout, apply those
+ * requests as soon as layout is complete, and then run layout once more immediately. If
+ * more requestLayout() calls are received during that second layout pass, we post those
+ * requests to the next frame, to avoid possible infinite loops.
+ *
+ * @param view the view that requested the layout.
+ */
+ void requestLayoutDuringLayout(final View view) {
+ if (!mHandlingLayoutInLayoutRequest) {
+ if (!mLayoutRequesters.contains(view)) {
+ Log.w("View", "requestLayout() called by " + view + " during layout pass");
+ mLayoutRequesters.add(view);
+ }
+ } else {
+ Log.w("View", "requestLayout() called by " + view + " during second layout pass: " +
+ "posting to next frame");
+ view.post(new Runnable() {
+ @Override
+ public void run() {
+ view.requestLayout();
+ }
+ });
+ }
+ }
+
+ private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
+ int desiredWindowHeight) {
mLayoutRequested = false;
mScrollMayChange = true;
+ mInLayout = true;
final View host = mView;
if (DEBUG_ORIENTATION || DEBUG_LAYOUT) {
@@ -1892,9 +1937,26 @@ public final class ViewRootImpl implements ViewParent,
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "layout");
try {
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
+ mInLayout = false;
+ int numViewsRequestingLayout = mLayoutRequesters.size();
+ if (numViewsRequestingLayout > 0) {
+ // requestLayout() was called during layout: unusual, but try to handle correctly
+ mHandlingLayoutInLayoutRequest = true;
+ for (int i = 0; i < numViewsRequestingLayout; ++i) {
+ mLayoutRequesters.get(i).requestLayout();
+ }
+ measureHierarchy(host, lp, mView.getContext().getResources(),
+ desiredWindowWidth, desiredWindowHeight);
+ // Now run layout one more time
+ mInLayout = true;
+ host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
+ mHandlingLayoutInLayoutRequest = false;
+ mLayoutRequesters.clear();
+ }
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
+ mInLayout = false;
}
public void requestTransparentRegion(View child) {
@@ -1978,30 +2040,19 @@ public final class ViewRootImpl implements ViewParent,
if (mProfileRendering) {
mRenderProfilingEnabled = enabled;
if (mRenderProfiler == null) {
- mRenderProfiler = new Thread(new Runnable() {
+ mRenderProfiler = new Choreographer.FrameCallback() {
@Override
- public void run() {
- Log.d(TAG, "Starting profiling thread");
- while (mRenderProfilingEnabled) {
- mAttachInfo.mHandler.post(new Runnable() {
- @Override
- public void run() {
- mDirty.set(0, 0, mWidth, mHeight);
- scheduleTraversals();
- }
- });
- try {
- // TODO: This should use vsync when we get an API
- Thread.sleep(15);
- } catch (InterruptedException e) {
- Log.d(TAG, "Exiting profiling thread");
- }
+ public void doFrame(long frameTimeNanos) {
+ mDirty.set(0, 0, mWidth, mHeight);
+ scheduleTraversals();
+ if (mRenderProfilingEnabled) {
+ mChoreographer.postFrameCallback(mRenderProfiler);
}
}
- }, "Rendering Profiler");
- mRenderProfiler.start();
+ };
+ mChoreographer.postFrameCallback(mRenderProfiler);
} else {
- mRenderProfiler.interrupt();
+ mChoreographer.removeFrameCallback(mRenderProfiler);
mRenderProfiler = null;
}
}
@@ -2077,7 +2128,7 @@ public final class ViewRootImpl implements ViewParent,
private void draw(boolean fullRedrawNeeded) {
Surface surface = mSurface;
- if (surface == null || !surface.isValid()) {
+ if (!surface.isValid()) {
return;
}
@@ -2158,6 +2209,8 @@ public final class ViewRootImpl implements ViewParent,
appScale + ", width=" + mWidth + ", height=" + mHeight);
}
+ invalidateDisplayLists();
+
attachInfo.mTreeObserver.dispatchOnDraw();
if (!dirty.isEmpty() || mIsAnimating) {
@@ -2368,8 +2421,11 @@ public final class ViewRootImpl implements ViewParent,
for (int i = 0; i < count; i++) {
final DisplayList displayList = displayLists.get(i);
- displayList.invalidate();
- displayList.clear();
+ if (displayList.isDirty()) {
+ displayList.invalidate();
+ displayList.clear();
+ displayList.setDirty(false);
+ }
}
displayLists.clear();
@@ -2743,11 +2799,10 @@ public final class ViewRootImpl implements ViewParent,
private final static int MSG_UPDATE_CONFIGURATION = 18;
private final static int MSG_PROCESS_INPUT_EVENTS = 19;
private final static int MSG_DISPATCH_SCREEN_STATE = 20;
- private final static int MSG_INVALIDATE_DISPLAY_LIST = 21;
- private final static int MSG_CLEAR_ACCESSIBILITY_FOCUS_HOST = 22;
- private final static int MSG_DISPATCH_DONE_ANIMATING = 23;
- private final static int MSG_INVALIDATE_WORLD = 24;
- private final static int MSG_WINDOW_MOVED = 25;
+ private final static int MSG_CLEAR_ACCESSIBILITY_FOCUS_HOST = 21;
+ private final static int MSG_DISPATCH_DONE_ANIMATING = 22;
+ private final static int MSG_INVALIDATE_WORLD = 23;
+ private final static int MSG_WINDOW_MOVED = 24;
final class ViewRootHandler extends Handler {
@Override
@@ -2793,8 +2848,6 @@ public final class ViewRootImpl implements ViewParent,
return "MSG_PROCESS_INPUT_EVENTS";
case MSG_DISPATCH_SCREEN_STATE:
return "MSG_DISPATCH_SCREEN_STATE";
- case MSG_INVALIDATE_DISPLAY_LIST:
- return "MSG_INVALIDATE_DISPLAY_LIST";
case MSG_CLEAR_ACCESSIBILITY_FOCUS_HOST:
return "MSG_CLEAR_ACCESSIBILITY_FOCUS_HOST";
case MSG_DISPATCH_DONE_ANIMATING:
@@ -2814,7 +2867,7 @@ public final class ViewRootImpl implements ViewParent,
case MSG_INVALIDATE_RECT:
final View.AttachInfo.InvalidateInfo info = (View.AttachInfo.InvalidateInfo) msg.obj;
info.target.invalidate(info.left, info.top, info.right, info.bottom);
- info.release();
+ info.recycle();
break;
case MSG_IME_FINISHED_EVENT:
handleImeFinishedEvent(msg.arg1, msg.arg2 != 0);
@@ -3001,7 +3054,7 @@ public final class ViewRootImpl implements ViewParent,
handleDragEvent(event);
} break;
case MSG_DISPATCH_SYSTEM_UI_VISIBILITY: {
- handleDispatchSystemUiVisibilityChanged((SystemUiVisibilityInfo)msg.obj);
+ handleDispatchSystemUiVisibilityChanged((SystemUiVisibilityInfo) msg.obj);
} break;
case MSG_UPDATE_CONFIGURATION: {
Configuration config = (Configuration)msg.obj;
@@ -3015,9 +3068,6 @@ public final class ViewRootImpl implements ViewParent,
handleScreenStateChange(msg.arg1 == 1);
}
} break;
- case MSG_INVALIDATE_DISPLAY_LIST: {
- invalidateDisplayLists();
- } break;
case MSG_CLEAR_ACCESSIBILITY_FOCUS_HOST: {
setAccessibilityFocus(null, null);
} break;
@@ -4102,6 +4152,7 @@ public final class ViewRootImpl implements ViewParent,
}
if (mAdded && !mFirst) {
+ invalidateDisplayLists();
destroyHardwareRenderer();
if (mView != null) {
@@ -4134,14 +4185,30 @@ public final class ViewRootImpl implements ViewParent,
}
public void loadSystemProperties() {
- boolean layout = SystemProperties.getBoolean(
- View.DEBUG_LAYOUT_PROPERTY, false);
- if (layout != mAttachInfo.mDebugLayout) {
- mAttachInfo.mDebugLayout = layout;
- if (!mHandler.hasMessages(MSG_INVALIDATE_WORLD)) {
- mHandler.sendEmptyMessageDelayed(MSG_INVALIDATE_WORLD, 200);
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ // Profiling
+ mProfileRendering = SystemProperties.getBoolean(PROPERTY_PROFILE_RENDERING, false);
+ profileRendering(mAttachInfo.mHasWindowFocus);
+
+ // Hardware rendering
+ if (mAttachInfo.mHardwareRenderer != null) {
+ if (mAttachInfo.mHardwareRenderer.loadSystemProperties(mHolder.getSurface())) {
+ invalidate();
+ }
+ }
+
+ // Layout debugging
+ boolean layout = SystemProperties.getBoolean(View.DEBUG_LAYOUT_PROPERTY, false);
+ if (layout != mAttachInfo.mDebugLayout) {
+ mAttachInfo.mDebugLayout = layout;
+ if (!mHandler.hasMessages(MSG_INVALIDATE_WORLD)) {
+ mHandler.sendEmptyMessageDelayed(MSG_INVALIDATE_WORLD, 200);
+ }
+ }
}
- }
+ });
}
private void destroyHardwareRenderer() {
@@ -4429,7 +4496,7 @@ public final class ViewRootImpl implements ViewParent,
AttachInfo.InvalidateInfo info = mViewRects.get(i);
if (info.target == view) {
mViewRects.remove(i);
- info.release();
+ info.recycle();
}
}
@@ -4470,7 +4537,7 @@ public final class ViewRootImpl implements ViewParent,
for (int i = 0; i < viewRectCount; i++) {
final View.AttachInfo.InvalidateInfo info = mTempViewRects[i];
info.target.invalidate(info.left, info.top, info.right, info.bottom);
- info.release();
+ info.recycle();
}
}
@@ -4505,19 +4572,6 @@ public final class ViewRootImpl implements ViewParent,
public void enqueueDisplayList(DisplayList displayList) {
mDisplayLists.add(displayList);
-
- mHandler.removeMessages(MSG_INVALIDATE_DISPLAY_LIST);
- Message msg = mHandler.obtainMessage(MSG_INVALIDATE_DISPLAY_LIST);
- mHandler.sendMessage(msg);
- }
-
- public void dequeueDisplayList(DisplayList displayList) {
- if (mDisplayLists.remove(displayList)) {
- displayList.invalidate();
- if (mDisplayLists.size() == 0) {
- mHandler.removeMessages(MSG_INVALIDATE_DISPLAY_LIST);
- }
- }
}
public void cancelInvalidate(View view) {
@@ -5354,12 +5408,13 @@ public final class ViewRootImpl implements ViewParent,
@Override
public void findAccessibilityNodeInfoByAccessibilityId(long accessibilityNodeId,
int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags,
- int interrogatingPid, long interrogatingTid) {
+ int interrogatingPid, long interrogatingTid, MagnificationSpec spec) {
ViewRootImpl viewRootImpl = mViewRootImpl.get();
if (viewRootImpl != null && viewRootImpl.mView != null) {
viewRootImpl.getAccessibilityInteractionController()
.findAccessibilityNodeInfoByAccessibilityIdClientThread(accessibilityNodeId,
- interactionId, callback, flags, interrogatingPid, interrogatingTid);
+ interactionId, callback, flags, interrogatingPid, interrogatingTid,
+ spec);
} else {
// We cannot make the call and notify the caller so it does not wait.
try {
@@ -5393,12 +5448,13 @@ public final class ViewRootImpl implements ViewParent,
@Override
public void findAccessibilityNodeInfoByViewId(long accessibilityNodeId, int viewId,
int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags,
- int interrogatingPid, long interrogatingTid) {
+ int interrogatingPid, long interrogatingTid, MagnificationSpec spec) {
ViewRootImpl viewRootImpl = mViewRootImpl.get();
if (viewRootImpl != null && viewRootImpl.mView != null) {
viewRootImpl.getAccessibilityInteractionController()
.findAccessibilityNodeInfoByViewIdClientThread(accessibilityNodeId, viewId,
- interactionId, callback, flags, interrogatingPid, interrogatingTid);
+ interactionId, callback, flags, interrogatingPid, interrogatingTid,
+ spec);
} else {
// We cannot make the call and notify the caller so it does not wait.
try {
@@ -5412,12 +5468,13 @@ public final class ViewRootImpl implements ViewParent,
@Override
public void findAccessibilityNodeInfosByText(long accessibilityNodeId, String text,
int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags,
- int interrogatingPid, long interrogatingTid) {
+ int interrogatingPid, long interrogatingTid, MagnificationSpec spec) {
ViewRootImpl viewRootImpl = mViewRootImpl.get();
if (viewRootImpl != null && viewRootImpl.mView != null) {
viewRootImpl.getAccessibilityInteractionController()
.findAccessibilityNodeInfosByTextClientThread(accessibilityNodeId, text,
- interactionId, callback, flags, interrogatingPid, interrogatingTid);
+ interactionId, callback, flags, interrogatingPid, interrogatingTid,
+ spec);
} else {
// We cannot make the call and notify the caller so it does not wait.
try {
@@ -5431,12 +5488,12 @@ public final class ViewRootImpl implements ViewParent,
@Override
public void findFocus(long accessibilityNodeId, int focusType, int interactionId,
IAccessibilityInteractionConnectionCallback callback, int flags,
- int interrogatingPid, long interrogatingTid) {
+ int interrogatingPid, long interrogatingTid, MagnificationSpec spec) {
ViewRootImpl viewRootImpl = mViewRootImpl.get();
if (viewRootImpl != null && viewRootImpl.mView != null) {
viewRootImpl.getAccessibilityInteractionController()
.findFocusClientThread(accessibilityNodeId, focusType, interactionId, callback,
- flags, interrogatingPid, interrogatingTid);
+ flags, interrogatingPid, interrogatingTid, spec);
} else {
// We cannot make the call and notify the caller so it does not wait.
try {
@@ -5450,12 +5507,12 @@ public final class ViewRootImpl implements ViewParent,
@Override
public void focusSearch(long accessibilityNodeId, int direction, int interactionId,
IAccessibilityInteractionConnectionCallback callback, int flags,
- int interrogatingPid, long interrogatingTid) {
+ int interrogatingPid, long interrogatingTid, MagnificationSpec spec) {
ViewRootImpl viewRootImpl = mViewRootImpl.get();
if (viewRootImpl != null && viewRootImpl.mView != null) {
viewRootImpl.getAccessibilityInteractionController()
.focusSearchClientThread(accessibilityNodeId, direction, interactionId,
- callback, flags, interrogatingPid, interrogatingTid);
+ callback, flags, interrogatingPid, interrogatingTid, spec);
} else {
// We cannot make the call and notify the caller so it does not wait.
try {
diff --git a/core/java/android/view/VolumePanel.java b/core/java/android/view/VolumePanel.java
index d7c7f46..6251c45 100644
--- a/core/java/android/view/VolumePanel.java
+++ b/core/java/android/view/VolumePanel.java
@@ -218,12 +218,14 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie
private static class WarningDialogReceiver extends BroadcastReceiver
implements DialogInterface.OnDismissListener {
- private Context mContext;
- private Dialog mDialog;
+ private final Context mContext;
+ private final Dialog mDialog;
+ private final VolumePanel mVolumePanel;
- WarningDialogReceiver(Context context, Dialog dialog) {
+ WarningDialogReceiver(Context context, Dialog dialog, VolumePanel volumePanel) {
mContext = context;
mDialog = dialog;
+ mVolumePanel = volumePanel;
IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
context.registerReceiver(this, filter);
}
@@ -231,16 +233,20 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie
@Override
public void onReceive(Context context, Intent intent) {
mDialog.cancel();
- synchronized (sConfirmSafeVolumeLock) {
- sConfirmSafeVolumeDialog = null;
- }
+ cleanUp();
}
public void onDismiss(DialogInterface unused) {
mContext.unregisterReceiver(this);
+ cleanUp();
+ }
+
+ private void cleanUp() {
synchronized (sConfirmSafeVolumeLock) {
sConfirmSafeVolumeDialog = null;
}
+ mVolumePanel.forceTimeout();
+ mVolumePanel.updateStates();
}
}
@@ -276,7 +282,8 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie
mDialog = new Dialog(context, R.style.Theme_Panel_Volume) {
public boolean onTouchEvent(MotionEvent event) {
- if (isShowing() && event.getAction() == MotionEvent.ACTION_OUTSIDE) {
+ if (isShowing() && event.getAction() == MotionEvent.ACTION_OUTSIDE &&
+ sConfirmSafeVolumeDialog == null) {
forceTimeout();
return true;
}
@@ -461,7 +468,8 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie
// never disable touch interactions for remote playback, the muting is not tied to
// the state of the phone.
sc.seekbarView.setEnabled(true);
- } else if (sc.streamType != mAudioManager.getMasterStreamType() && muted) {
+ } else if ((sc.streamType != mAudioManager.getMasterStreamType() && muted) ||
+ (sConfirmSafeVolumeDialog != null)) {
sc.seekbarView.setEnabled(false);
} else {
sc.seekbarView.setEnabled(true);
@@ -490,7 +498,7 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie
}
}
- private void updateStates() {
+ public void updateStates() {
final int count = mSliderGroup.getChildCount();
for (int i = 0; i < count; i++) {
StreamControl sc = (StreamControl) mSliderGroup.getChildAt(i).getTag();
@@ -562,9 +570,9 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie
postMuteChanged(STREAM_MASTER, flags);
}
- public void postDisplaySafeVolumeWarning() {
+ public void postDisplaySafeVolumeWarning(int flags) {
if (hasMessages(MSG_DISPLAY_SAFE_VOLUME_WARNING)) return;
- obtainMessage(MSG_DISPLAY_SAFE_VOLUME_WARNING, 0, 0).sendToTarget();
+ obtainMessage(MSG_DISPLAY_SAFE_VOLUME_WARNING, flags, 0).sendToTarget();
}
/**
@@ -598,7 +606,6 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie
removeMessages(MSG_FREE_RESOURCES);
sendMessageDelayed(obtainMessage(MSG_FREE_RESOURCES), FREE_DELAY);
-
resetTimeout();
}
@@ -704,7 +711,8 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie
if (((flags & AudioManager.FLAG_FIXED_VOLUME) != 0) ||
(streamType != mAudioManager.getMasterStreamType() &&
streamType != AudioService.STREAM_REMOTE_MUSIC &&
- isMuted(streamType))) {
+ isMuted(streamType)) ||
+ sConfirmSafeVolumeDialog != null) {
sc.seekbarView.setEnabled(false);
} else {
sc.seekbarView.setEnabled(true);
@@ -802,7 +810,6 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie
removeMessages(MSG_FREE_RESOURCES);
sendMessageDelayed(obtainMessage(MSG_FREE_RESOURCES), FREE_DELAY);
-
resetTimeout();
}
@@ -838,30 +845,34 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie
}
}
- protected void onDisplaySafeVolumeWarning() {
- synchronized (sConfirmSafeVolumeLock) {
- if (sConfirmSafeVolumeDialog != null) {
- return;
+ protected void onDisplaySafeVolumeWarning(int flags) {
+ if ((flags & AudioManager.FLAG_SHOW_UI) != 0 || mDialog.isShowing()) {
+ synchronized (sConfirmSafeVolumeLock) {
+ if (sConfirmSafeVolumeDialog != null) {
+ return;
+ }
+ sConfirmSafeVolumeDialog = new AlertDialog.Builder(mContext)
+ .setMessage(com.android.internal.R.string.safe_media_volume_warning)
+ .setPositiveButton(com.android.internal.R.string.yes,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ mAudioService.disableSafeMediaVolume();
+ }
+ })
+ .setNegativeButton(com.android.internal.R.string.no, null)
+ .setIconAttribute(android.R.attr.alertDialogIcon)
+ .create();
+ final WarningDialogReceiver warning = new WarningDialogReceiver(mContext,
+ sConfirmSafeVolumeDialog, this);
+
+ sConfirmSafeVolumeDialog.setOnDismissListener(warning);
+ sConfirmSafeVolumeDialog.getWindow().setType(
+ WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
+ sConfirmSafeVolumeDialog.show();
}
- sConfirmSafeVolumeDialog = new AlertDialog.Builder(mContext)
- .setMessage(com.android.internal.R.string.safe_media_volume_warning)
- .setPositiveButton(com.android.internal.R.string.yes,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- mAudioService.disableSafeMediaVolume();
- }
- })
- .setNegativeButton(com.android.internal.R.string.no, null)
- .setIconAttribute(android.R.attr.alertDialogIcon)
- .create();
- final WarningDialogReceiver warning = new WarningDialogReceiver(mContext,
- sConfirmSafeVolumeDialog);
-
- sConfirmSafeVolumeDialog.setOnDismissListener(warning);
- sConfirmSafeVolumeDialog.getWindow().setType(
- WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
- sConfirmSafeVolumeDialog.show();
+ updateStates();
}
+ resetTimeout();
}
/**
@@ -957,6 +968,11 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie
mDialog.dismiss();
mActiveStreamType = -1;
}
+ synchronized (sConfirmSafeVolumeLock) {
+ if (sConfirmSafeVolumeDialog != null) {
+ sConfirmSafeVolumeDialog.dismiss();
+ }
+ }
break;
}
case MSG_RINGER_MODE_CHANGED: {
@@ -980,7 +996,7 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie
break;
case MSG_DISPLAY_SAFE_VOLUME_WARNING:
- onDisplaySafeVolumeWarning();
+ onDisplaySafeVolumeWarning(msg.arg1);
break;
}
}
diff --git a/core/java/android/view/WindowInfo.java b/core/java/android/view/WindowInfo.java
deleted file mode 100644
index 7d16e14..0000000
--- a/core/java/android/view/WindowInfo.java
+++ /dev/null
@@ -1,175 +0,0 @@
-/*
- * Copyright (C) 2012 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.view;
-
-import android.graphics.Rect;
-import android.os.IBinder;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * Information the state of a window.
- *
- * @hide
- */
-public class WindowInfo implements Parcelable {
-
- private static final int MAX_POOL_SIZE = 20;
-
- private static int UNDEFINED = -1;
-
- private static Object sPoolLock = new Object();
- private static WindowInfo sPool;
- private static int sPoolSize;
-
- private WindowInfo mNext;
- private boolean mInPool;
-
- public IBinder token;
-
- public final Rect frame = new Rect();
-
- public final Rect touchableRegion = new Rect();
-
- public int type = UNDEFINED;
-
- public float compatibilityScale = UNDEFINED;
-
- public boolean visible;
-
- public int displayId = UNDEFINED;
-
- public int layer = UNDEFINED;
-
- private WindowInfo() {
- /* do nothing - reduce visibility */
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel parcel, int flags) {
- parcel.writeStrongBinder(token);
- parcel.writeParcelable(frame, 0);
- parcel.writeParcelable(touchableRegion, 0);
- parcel.writeInt(type);
- parcel.writeFloat(compatibilityScale);
- parcel.writeInt(visible ? 1 : 0);
- parcel.writeInt(displayId);
- parcel.writeInt(layer);
- recycle();
- }
-
- private void initFromParcel(Parcel parcel) {
- token = parcel.readStrongBinder();
- frame.set((Rect) parcel.readParcelable(null));
- touchableRegion.set((Rect) parcel.readParcelable(null));
- type = parcel.readInt();
- compatibilityScale = parcel.readFloat();
- visible = (parcel.readInt() == 1);
- displayId = parcel.readInt();
- layer = parcel.readInt();
- }
-
- public static WindowInfo obtain(WindowInfo other) {
- WindowInfo info = obtain();
- info.token = other.token;
- info.frame.set(other.frame);
- info.touchableRegion.set(other.touchableRegion);
- info.type = other.type;
- info.compatibilityScale = other.compatibilityScale;
- info.visible = other.visible;
- info.displayId = other.displayId;
- info.layer = other.layer;
- return info;
- }
-
- public static WindowInfo obtain() {
- synchronized (sPoolLock) {
- if (sPoolSize > 0) {
- WindowInfo info = sPool;
- sPool = info.mNext;
- info.mNext = null;
- info.mInPool = false;
- sPoolSize--;
- return info;
- } else {
- return new WindowInfo();
- }
- }
- }
-
- public void recycle() {
- if (mInPool) {
- throw new IllegalStateException("Already recycled.");
- }
- clear();
- synchronized (sPoolLock) {
- if (sPoolSize < MAX_POOL_SIZE) {
- mNext = sPool;
- sPool = this;
- mInPool = true;
- sPoolSize++;
- }
- }
- }
-
- private void clear() {
- token = null;
- frame.setEmpty();
- touchableRegion.setEmpty();
- type = UNDEFINED;
- compatibilityScale = UNDEFINED;
- visible = false;
- displayId = UNDEFINED;
- layer = UNDEFINED;
- }
-
- @Override
- public String toString() {
- StringBuilder builder = new StringBuilder();
- builder.append("Window [token:").append((token != null) ? token.hashCode() : null);
- builder.append(", displayId:").append(displayId);
- builder.append(", type:").append(type);
- builder.append(", visible:").append(visible);
- builder.append(", layer:").append(layer);
- builder.append(", compatibilityScale:").append(compatibilityScale);
- builder.append(", frame:").append(frame);
- builder.append(", touchableRegion:").append(touchableRegion);
- builder.append("]");
- return builder.toString();
- }
-
- /**
- * @see Parcelable.Creator
- */
- public static final Parcelable.Creator<WindowInfo> CREATOR =
- new Parcelable.Creator<WindowInfo>() {
- public WindowInfo createFromParcel(Parcel parcel) {
- WindowInfo info = WindowInfo.obtain();
- info.initFromParcel(parcel);
- return info;
- }
-
- public WindowInfo[] newArray(int size) {
- return new WindowInfo[size];
- }
- };
-}
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index 26739b3..7bdb44c 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -1112,14 +1112,6 @@ public interface WindowManagerPolicy {
public void setLastInputMethodWindowLw(WindowState ime, WindowState target);
/**
- * Returns whether magnification can be applied to the given window type.
- *
- * @param attrs The window's LayoutParams.
- * @return Whether magnification can be applied.
- */
- public boolean canMagnifyWindowLw(WindowManager.LayoutParams attrs);
-
- /**
* Called when the current user changes. Guaranteed to be called before the broadcast
* of the new user id is made to all listeners.
*
diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java
index 1500905..9603fe5 100644
--- a/core/java/android/view/accessibility/AccessibilityEvent.java
+++ b/core/java/android/view/accessibility/AccessibilityEvent.java
@@ -19,6 +19,7 @@ package android.view.accessibility;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
+import android.util.Pools.SynchronizedPool;
import java.util.ArrayList;
import java.util.List;
@@ -686,11 +687,8 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
public static final int TYPES_ALL_MASK = 0xFFFFFFFF;
private static final int MAX_POOL_SIZE = 10;
- private static final Object sPoolLock = new Object();
- private static AccessibilityEvent sPool;
- private static int sPoolSize;
- private AccessibilityEvent mNext;
- private boolean mIsInPool;
+ private static final SynchronizedPool<AccessibilityEvent> sPool =
+ new SynchronizedPool<AccessibilityEvent>(MAX_POOL_SIZE);
private int mEventType;
private CharSequence mPackageName;
@@ -916,17 +914,8 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
* @return An instance.
*/
public static AccessibilityEvent obtain() {
- synchronized (sPoolLock) {
- if (sPool != null) {
- AccessibilityEvent event = sPool;
- sPool = sPool.mNext;
- sPoolSize--;
- event.mNext = null;
- event.mIsInPool = false;
- return event;
- }
- return new AccessibilityEvent();
- }
+ AccessibilityEvent event = sPool.acquire();
+ return (event != null) ? event : new AccessibilityEvent();
}
/**
@@ -939,18 +928,8 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
*/
@Override
public void recycle() {
- if (mIsInPool) {
- throw new IllegalStateException("Event already recycled!");
- }
clear();
- synchronized (sPoolLock) {
- if (sPoolSize <= MAX_POOL_SIZE) {
- mNext = sPool;
- sPool = this;
- mIsInPool = true;
- sPoolSize++;
- }
- }
+ sPool.release(this);
}
/**
@@ -1137,54 +1116,176 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
* @return The string representation.
*/
public static String eventTypeToString(int eventType) {
- switch (eventType) {
- case TYPE_VIEW_CLICKED:
- return "TYPE_VIEW_CLICKED";
- case TYPE_VIEW_LONG_CLICKED:
- return "TYPE_VIEW_LONG_CLICKED";
- case TYPE_VIEW_SELECTED:
- return "TYPE_VIEW_SELECTED";
- case TYPE_VIEW_FOCUSED:
- return "TYPE_VIEW_FOCUSED";
- case TYPE_VIEW_TEXT_CHANGED:
- return "TYPE_VIEW_TEXT_CHANGED";
- case TYPE_WINDOW_STATE_CHANGED:
- return "TYPE_WINDOW_STATE_CHANGED";
- case TYPE_VIEW_HOVER_ENTER:
- return "TYPE_VIEW_HOVER_ENTER";
- case TYPE_VIEW_HOVER_EXIT:
- return "TYPE_VIEW_HOVER_EXIT";
- case TYPE_NOTIFICATION_STATE_CHANGED:
- return "TYPE_NOTIFICATION_STATE_CHANGED";
- case TYPE_TOUCH_EXPLORATION_GESTURE_START:
- return "TYPE_TOUCH_EXPLORATION_GESTURE_START";
- case TYPE_TOUCH_EXPLORATION_GESTURE_END:
- return "TYPE_TOUCH_EXPLORATION_GESTURE_END";
- case TYPE_WINDOW_CONTENT_CHANGED:
- return "TYPE_WINDOW_CONTENT_CHANGED";
- case TYPE_VIEW_TEXT_SELECTION_CHANGED:
- return "TYPE_VIEW_TEXT_SELECTION_CHANGED";
- case TYPE_VIEW_SCROLLED:
- return "TYPE_VIEW_SCROLLED";
- case TYPE_ANNOUNCEMENT:
- return "TYPE_ANNOUNCEMENT";
- case TYPE_VIEW_ACCESSIBILITY_FOCUSED:
- return "TYPE_VIEW_ACCESSIBILITY_FOCUSED";
- case TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED:
- return "TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED";
- case TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY:
- return "TYPE_CURRENT_AT_GRANULARITY_MOVEMENT_CHANGED";
- case TYPE_GESTURE_DETECTION_START:
- return "TYPE_GESTURE_DETECTION_START";
- case TYPE_GESTURE_DETECTION_END:
- return "TYPE_GESTURE_DETECTION_END";
- case TYPE_TOUCH_INTERACTION_START:
- return "TYPE_TOUCH_INTERACTION_START";
- case TYPE_TOUCH_INTERACTION_END:
- return "TYPE_TOUCH_INTERACTION_END";
- default:
- return null;
+ if (eventType == TYPES_ALL_MASK) {
+ return "TYPES_ALL_MASK";
}
+ StringBuilder builder = new StringBuilder();
+ int eventTypeCount = 0;
+ while (eventType != 0) {
+ final int eventTypeFlag = 1 << Integer.numberOfTrailingZeros(eventType);
+ eventType &= ~eventTypeFlag;
+ switch (eventTypeFlag) {
+ case TYPE_VIEW_CLICKED: {
+ if (eventTypeCount > 0) {
+ builder.append(", ");
+ }
+ builder.append("TYPE_VIEW_CLICKED");
+ eventTypeCount++;
+ } break;
+ case TYPE_VIEW_LONG_CLICKED: {
+ if (eventTypeCount > 0) {
+ builder.append(", ");
+ }
+ builder.append("TYPE_VIEW_LONG_CLICKED");
+ eventTypeCount++;
+ } break;
+ case TYPE_VIEW_SELECTED: {
+ if (eventTypeCount > 0) {
+ builder.append(", ");
+ }
+ builder.append("TYPE_VIEW_SELECTED");
+ eventTypeCount++;
+ } break;
+ case TYPE_VIEW_FOCUSED: {
+ if (eventTypeCount > 0) {
+ builder.append(", ");
+ }
+ builder.append("TYPE_VIEW_FOCUSED");
+ eventTypeCount++;
+ } break;
+ case TYPE_VIEW_TEXT_CHANGED: {
+ if (eventTypeCount > 0) {
+ builder.append(", ");
+ }
+ builder.append("TYPE_VIEW_TEXT_CHANGED");
+ eventTypeCount++;
+ } break;
+ case TYPE_WINDOW_STATE_CHANGED: {
+ if (eventTypeCount > 0) {
+ builder.append(", ");
+ }
+ builder.append("TYPE_WINDOW_STATE_CHANGED");
+ eventTypeCount++;
+ } break;
+ case TYPE_VIEW_HOVER_ENTER: {
+ if (eventTypeCount > 0) {
+ builder.append(", ");
+ }
+ builder.append("TYPE_VIEW_HOVER_ENTER");
+ eventTypeCount++;
+ } break;
+ case TYPE_VIEW_HOVER_EXIT: {
+ if (eventTypeCount > 0) {
+ builder.append(", ");
+ }
+ builder.append("TYPE_VIEW_HOVER_EXIT");
+ eventTypeCount++;
+ } break;
+ case TYPE_NOTIFICATION_STATE_CHANGED: {
+ if (eventTypeCount > 0) {
+ builder.append(", ");
+ }
+ builder.append("TYPE_NOTIFICATION_STATE_CHANGED");
+ eventTypeCount++;
+ } break;
+ case TYPE_TOUCH_EXPLORATION_GESTURE_START: {
+ if (eventTypeCount > 0) {
+ builder.append(", ");
+ }
+ builder.append("TYPE_TOUCH_EXPLORATION_GESTURE_START");
+ eventTypeCount++;
+ } break;
+ case TYPE_TOUCH_EXPLORATION_GESTURE_END: {
+ if (eventTypeCount > 0) {
+ builder.append(", ");
+ }
+ builder.append("TYPE_TOUCH_EXPLORATION_GESTURE_END");
+ eventTypeCount++;
+ } break;
+ case TYPE_WINDOW_CONTENT_CHANGED: {
+ if (eventTypeCount > 0) {
+ builder.append(", ");
+ }
+ builder.append("TYPE_WINDOW_CONTENT_CHANGED");
+ eventTypeCount++;
+ } break;
+ case TYPE_VIEW_TEXT_SELECTION_CHANGED: {
+ if (eventTypeCount > 0) {
+ builder.append(", ");
+ }
+ builder.append("TYPE_VIEW_TEXT_SELECTION_CHANGED");
+ eventTypeCount++;
+ } break;
+ case TYPE_VIEW_SCROLLED: {
+ if (eventTypeCount > 0) {
+ builder.append(", ");
+ }
+ builder.append("TYPE_VIEW_SCROLLED");
+ eventTypeCount++;
+ } break;
+ case TYPE_ANNOUNCEMENT: {
+ if (eventTypeCount > 0) {
+ builder.append(", ");
+ }
+ builder.append("TYPE_ANNOUNCEMENT");
+ eventTypeCount++;
+ } break;
+ case TYPE_VIEW_ACCESSIBILITY_FOCUSED: {
+ if (eventTypeCount > 0) {
+ builder.append(", ");
+ }
+ builder.append("TYPE_VIEW_ACCESSIBILITY_FOCUSED");
+ eventTypeCount++;
+ } break;
+ case TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED: {
+ if (eventTypeCount > 0) {
+ builder.append(", ");
+ }
+ builder.append("TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED");
+ eventTypeCount++;
+ } break;
+ case TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY: {
+ if (eventTypeCount > 0) {
+ builder.append(", ");
+ }
+ builder.append("TYPE_CURRENT_AT_GRANULARITY_MOVEMENT_CHANGED");
+ eventTypeCount++;
+ } break;
+ case TYPE_GESTURE_DETECTION_START: {
+ if (eventTypeCount > 0) {
+ builder.append(", ");
+ }
+ builder.append("TYPE_GESTURE_DETECTION_START");
+ eventTypeCount++;
+ } break;
+ case TYPE_GESTURE_DETECTION_END: {
+ if (eventTypeCount > 0) {
+ builder.append(", ");
+ }
+ builder.append("TYPE_GESTURE_DETECTION_END");
+ eventTypeCount++;
+ } break;
+ case TYPE_TOUCH_INTERACTION_START: {
+ if (eventTypeCount > 0) {
+ builder.append(", ");
+ }
+ builder.append("TYPE_TOUCH_INTERACTION_START");
+ eventTypeCount++;
+ } break;
+ case TYPE_TOUCH_INTERACTION_END: {
+ if (eventTypeCount > 0) {
+ builder.append(", ");
+ }
+ builder.append("TYPE_TOUCH_INTERACTION_END");
+ eventTypeCount++;
+ } break;
+ }
+ }
+ if (eventTypeCount > 1) {
+ builder.insert(0, '[');
+ builder.append(']');
+ }
+ return builder.toString();
}
/**
diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
index 20b5f17..67df684 100644
--- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java
+++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
@@ -17,7 +17,6 @@
package android.view.accessibility;
import android.accessibilityservice.IAccessibilityServiceConnection;
-import android.graphics.Rect;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
@@ -102,8 +101,6 @@ public final class AccessibilityInteractionClient
private Message mSameThreadMessage;
- private final Rect mTempBounds = new Rect();
-
// The connection cache is shared between all interrogating threads.
private static final SparseArray<IAccessibilityServiceConnection> sConnectionCache =
new SparseArray<IAccessibilityServiceConnection>();
@@ -194,14 +191,14 @@ public final class AccessibilityInteractionClient
return cachedInfo;
}
final int interactionId = mInteractionIdCounter.getAndIncrement();
- final float windowScale = connection.findAccessibilityNodeInfoByAccessibilityId(
+ final boolean success = connection.findAccessibilityNodeInfoByAccessibilityId(
accessibilityWindowId, accessibilityNodeId, interactionId, this,
prefetchFlags, Thread.currentThread().getId());
// If the scale is zero the call has failed.
- if (windowScale > 0) {
+ if (success) {
List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear(
interactionId);
- finalizeAndCacheAccessibilityNodeInfos(infos, connectionId, windowScale);
+ finalizeAndCacheAccessibilityNodeInfos(infos, connectionId);
if (infos != null && !infos.isEmpty()) {
return infos.get(0);
}
@@ -242,15 +239,13 @@ public final class AccessibilityInteractionClient
IAccessibilityServiceConnection connection = getConnection(connectionId);
if (connection != null) {
final int interactionId = mInteractionIdCounter.getAndIncrement();
- final float windowScale =
- connection.findAccessibilityNodeInfoByViewId(accessibilityWindowId,
- accessibilityNodeId, viewId, interactionId, this,
- Thread.currentThread().getId());
- // If the scale is zero the call has failed.
- if (windowScale > 0) {
+ final boolean success =connection.findAccessibilityNodeInfoByViewId(
+ accessibilityWindowId, accessibilityNodeId, viewId, interactionId, this,
+ Thread.currentThread().getId());
+ if (success) {
AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear(
interactionId);
- finalizeAndCacheAccessibilityNodeInfo(info, connectionId, windowScale);
+ finalizeAndCacheAccessibilityNodeInfo(info, connectionId);
return info;
}
} else {
@@ -290,14 +285,13 @@ public final class AccessibilityInteractionClient
IAccessibilityServiceConnection connection = getConnection(connectionId);
if (connection != null) {
final int interactionId = mInteractionIdCounter.getAndIncrement();
- final float windowScale = connection.findAccessibilityNodeInfosByText(
+ final boolean success = connection.findAccessibilityNodeInfosByText(
accessibilityWindowId, accessibilityNodeId, text, interactionId, this,
Thread.currentThread().getId());
- // If the scale is zero the call has failed.
- if (windowScale > 0) {
+ if (success) {
List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear(
interactionId);
- finalizeAndCacheAccessibilityNodeInfos(infos, connectionId, windowScale);
+ finalizeAndCacheAccessibilityNodeInfos(infos, connectionId);
return infos;
}
} else {
@@ -336,14 +330,13 @@ public final class AccessibilityInteractionClient
IAccessibilityServiceConnection connection = getConnection(connectionId);
if (connection != null) {
final int interactionId = mInteractionIdCounter.getAndIncrement();
- final float windowScale = connection.findFocus(accessibilityWindowId,
+ final boolean success = connection.findFocus(accessibilityWindowId,
accessibilityNodeId, focusType, interactionId, this,
Thread.currentThread().getId());
- // If the scale is zero the call has failed.
- if (windowScale > 0) {
+ if (success) {
AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear(
interactionId);
- finalizeAndCacheAccessibilityNodeInfo(info, connectionId, windowScale);
+ finalizeAndCacheAccessibilityNodeInfo(info, connectionId);
return info;
}
} else {
@@ -381,14 +374,13 @@ public final class AccessibilityInteractionClient
IAccessibilityServiceConnection connection = getConnection(connectionId);
if (connection != null) {
final int interactionId = mInteractionIdCounter.getAndIncrement();
- final float windowScale = connection.focusSearch(accessibilityWindowId,
+ final boolean success = connection.focusSearch(accessibilityWindowId,
accessibilityNodeId, direction, interactionId, this,
Thread.currentThread().getId());
- // If the scale is zero the call has failed.
- if (windowScale > 0) {
+ if (success) {
AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear(
interactionId);
- finalizeAndCacheAccessibilityNodeInfo(info, connectionId, windowScale);
+ finalizeAndCacheAccessibilityNodeInfo(info, connectionId);
return info;
}
} else {
@@ -604,36 +596,14 @@ public final class AccessibilityInteractionClient
}
/**
- * Applies compatibility scale to the info bounds if it is not equal to one.
- *
- * @param info The info whose bounds to scale.
- * @param scale The scale to apply.
- */
- private void applyCompatibilityScaleIfNeeded(AccessibilityNodeInfo info, float scale) {
- if (scale == 1.0f) {
- return;
- }
- Rect bounds = mTempBounds;
- info.getBoundsInParent(bounds);
- bounds.scale(scale);
- info.setBoundsInParent(bounds);
-
- info.getBoundsInScreen(bounds);
- bounds.scale(scale);
- info.setBoundsInScreen(bounds);
- }
-
- /**
* Finalize an {@link AccessibilityNodeInfo} before passing it to the client.
*
* @param info The info.
* @param connectionId The id of the connection to the system.
- * @param windowScale The source window compatibility scale.
*/
- private void finalizeAndCacheAccessibilityNodeInfo(AccessibilityNodeInfo info, int connectionId,
- float windowScale) {
+ private void finalizeAndCacheAccessibilityNodeInfo(AccessibilityNodeInfo info,
+ int connectionId) {
if (info != null) {
- applyCompatibilityScaleIfNeeded(info, windowScale);
info.setConnectionId(connectionId);
info.setSealed(true);
sAccessibilityNodeInfoCache.add(info);
@@ -645,15 +615,14 @@ public final class AccessibilityInteractionClient
*
* @param infos The {@link AccessibilityNodeInfo}s.
* @param connectionId The id of the connection to the system.
- * @param windowScale The source window compatibility scale.
*/
private void finalizeAndCacheAccessibilityNodeInfos(List<AccessibilityNodeInfo> infos,
- int connectionId, float windowScale) {
+ int connectionId) {
if (infos != null) {
final int infosCount = infos.size();
for (int i = 0; i < infosCount; i++) {
AccessibilityNodeInfo info = infos.get(i);
- finalizeAndCacheAccessibilityNodeInfo(info, connectionId, windowScale);
+ finalizeAndCacheAccessibilityNodeInfo(info, connectionId);
}
}
}
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 1dc2487..6c03280 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -20,6 +20,7 @@ import android.graphics.Rect;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
+import android.util.Pools.SynchronizedPool;
import android.util.SparseLongArray;
import android.view.View;
@@ -354,11 +355,9 @@ public class AccessibilityNodeInfo implements Parcelable {
// Housekeeping.
private static final int MAX_POOL_SIZE = 50;
- private static final Object sPoolLock = new Object();
- private static AccessibilityNodeInfo sPool;
- private static int sPoolSize;
- private AccessibilityNodeInfo mNext;
- private boolean mIsInPool;
+ private static final SynchronizedPool<AccessibilityNodeInfo> sPool =
+ new SynchronizedPool<AccessibilityNodeInfo>(MAX_POOL_SIZE);
+
private boolean mSealed;
// Data.
@@ -487,6 +486,31 @@ public class AccessibilityNodeInfo implements Parcelable {
}
/**
+ * Refreshes this info with the latest state of the view it represents.
+ * <p>
+ * <strong>Note:</strong> If this method returns false this info is obsolete
+ * since it represents a view that is no longer in the view tree and should
+ * be recycled.
+ * </p>
+ * @return Whether the refresh succeeded.
+ */
+ public boolean refresh() {
+ enforceSealed();
+ if (!canPerformRequestOverConnection(mSourceNodeId)) {
+ return false;
+ }
+ AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
+ AccessibilityNodeInfo refreshedInfo = client.findAccessibilityNodeInfoByAccessibilityId(
+ mConnectionId, mWindowId, mSourceNodeId, 0);
+ if (refreshedInfo == null) {
+ return false;
+ }
+ init(refreshedInfo);
+ refreshedInfo.recycle();
+ return true;
+ }
+
+ /**
* @return The ids of the children.
*
* @hide
@@ -1517,17 +1541,8 @@ public class AccessibilityNodeInfo implements Parcelable {
* @return An instance.
*/
public static AccessibilityNodeInfo obtain() {
- synchronized (sPoolLock) {
- if (sPool != null) {
- AccessibilityNodeInfo info = sPool;
- sPool = sPool.mNext;
- sPoolSize--;
- info.mNext = null;
- info.mIsInPool = false;
- return info;
- }
- return new AccessibilityNodeInfo();
- }
+ AccessibilityNodeInfo info = sPool.acquire();
+ return (info != null) ? info : new AccessibilityNodeInfo();
}
/**
@@ -1552,18 +1567,8 @@ public class AccessibilityNodeInfo implements Parcelable {
* @throws IllegalStateException If the info is already recycled.
*/
public void recycle() {
- if (mIsInPool) {
- throw new IllegalStateException("Info already recycled!");
- }
clear();
- synchronized (sPoolLock) {
- if (sPoolSize <= MAX_POOL_SIZE) {
- mNext = sPool;
- sPool = this;
- mIsInPool = true;
- sPoolSize++;
- }
- }
+ sPool.release(this);
}
/**
@@ -1620,7 +1625,6 @@ public class AccessibilityNodeInfo implements Parcelable {
*
* @param other The other instance.
*/
- @SuppressWarnings("unchecked")
private void init(AccessibilityNodeInfo other) {
mSealed = other.mSealed;
mSourceNodeId = other.mSourceNodeId;
diff --git a/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl b/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl
index 9b39300..c313b07 100644
--- a/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl
@@ -17,6 +17,7 @@
package android.view.accessibility;
import android.os.Bundle;
+import android.view.MagnificationSpec;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
@@ -30,23 +31,23 @@ oneway interface IAccessibilityInteractionConnection {
void findAccessibilityNodeInfoByAccessibilityId(long accessibilityNodeId, int interactionId,
IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
- long interrogatingTid);
+ long interrogatingTid, in MagnificationSpec spec);
void findAccessibilityNodeInfoByViewId(long accessibilityNodeId, int viewId, int interactionId,
IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
- long interrogatingTid);
+ long interrogatingTid, in MagnificationSpec spec);
void findAccessibilityNodeInfosByText(long accessibilityNodeId, String text, int interactionId,
IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
- long interrogatingTid);
+ long interrogatingTid, in MagnificationSpec spec);
void findFocus(long accessibilityNodeId, int focusType, int interactionId,
IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
- long interrogatingTid);
+ long interrogatingTid, in MagnificationSpec spec);
void focusSearch(long accessibilityNodeId, int direction, int interactionId,
IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
- long interrogatingTid);
+ long interrogatingTid, in MagnificationSpec spec);
void performAccessibilityAction(long accessibilityNodeId, int action, in Bundle arguments,
int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags,
diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java
index 4dbca23..6fb8bdf 100644
--- a/core/java/android/webkit/BrowserFrame.java
+++ b/core/java/android/webkit/BrowserFrame.java
@@ -226,8 +226,6 @@ class BrowserFrame extends Handler {
} else {
sJavaBridge.setCacheSize(4 * 1024 * 1024);
}
- // initialize CacheManager
- CacheManager.init(appContext);
// create CookieSyncManager with current Context
CookieSyncManager.createInstance(appContext);
// create PluginManager with current Context
diff --git a/core/java/android/webkit/CacheManager.java b/core/java/android/webkit/CacheManager.java
index 52f41e6..bbd3f2b 100644
--- a/core/java/android/webkit/CacheManager.java
+++ b/core/java/android/webkit/CacheManager.java
@@ -45,14 +45,6 @@ import java.util.Map;
// CacheManager may only be used if your activity contains a WebView.
@Deprecated
public final class CacheManager {
-
- private static final String LOGTAG = "cache";
-
- static final String HEADER_KEY_IFMODIFIEDSINCE = "if-modified-since";
- static final String HEADER_KEY_IFNONEMATCH = "if-none-match";
-
- private static File mBaseDir;
-
/**
* Represents a resource stored in the HTTP cache. Instances of this class
* can be obtained by calling
@@ -239,39 +231,23 @@ public final class CacheManager {
}
/**
- * Initializes the HTTP cache. This method must be called before any
- * CacheManager methods are used. Note that this is called automatically
- * when a {@link WebView} is created.
- *
- * @param context the application context
- */
- static void init(Context context) {
- // This isn't actually where the real cache lives, but where we put files for the
- // purpose of getCacheFile().
- mBaseDir = new File(context.getCacheDir(), "webviewCacheChromiumStaging");
- if (!mBaseDir.exists()) {
- mBaseDir.mkdirs();
- }
- }
-
- /**
* Gets the base directory in which the files used to store the contents of
* cache entries are placed. See
* {@link CacheManager.CacheResult#getLocalPath CacheManager.CacheResult.getLocalPath()}.
*
* @return the base directory of the cache
- * @deprecated Access to the HTTP cache will be removed in a future release.
+ * @deprecated This method no longer has any effect and always returns null.
*/
@Deprecated
public static File getCacheFileBaseDir() {
- return mBaseDir;
+ return null;
}
/**
* Gets whether the HTTP cache is disabled.
*
* @return true if the HTTP cache is disabled
- * @deprecated Access to the HTTP cache will be removed in a future release.
+ * @deprecated This method no longer has any effect and always returns false.
*/
@Deprecated
public static boolean cacheDisabled() {
@@ -314,73 +290,11 @@ public final class CacheManager {
* @param headers a map from HTTP header name to value, to be populated
* for the returned cache entry
* @return the cache entry for the specified URL
- * @deprecated Access to the HTTP cache will be removed in a future release.
+ * @deprecated This method no longer has any effect and always returns null.
*/
@Deprecated
public static CacheResult getCacheFile(String url,
Map<String, String> headers) {
- return getCacheFile(url, 0, headers);
- }
-
- static CacheResult getCacheFile(String url, long postIdentifier,
- Map<String, String> headers) {
- CacheResult result = nativeGetCacheResult(url);
- if (result == null) {
- return null;
- }
- // A temporary local file will have been created native side and localPath set
- // appropriately.
- File src = new File(mBaseDir, result.localPath);
- try {
- // Open the file here so that even if it is deleted, the content
- // is still readable by the caller until close() is called.
- result.inStream = new FileInputStream(src);
- } catch (FileNotFoundException e) {
- Log.v(LOGTAG, "getCacheFile(): Failed to open file: " + e);
- // TODO: The files in the cache directory can be removed by the
- // system. If it is gone, what should we do?
- return null;
- }
-
- // A null value for headers is used by CACHE_MODE_CACHE_ONLY to imply
- // that we should provide the cache result even if it is expired.
- // Note that a negative expires value means a time in the far future.
- if (headers != null && result.expires >= 0
- && result.expires <= System.currentTimeMillis()) {
- if (result.lastModified == null && result.etag == null) {
- return null;
- }
- // Return HEADER_KEY_IFNONEMATCH or HEADER_KEY_IFMODIFIEDSINCE
- // for requesting validation.
- if (result.etag != null) {
- headers.put(HEADER_KEY_IFNONEMATCH, result.etag);
- }
- if (result.lastModified != null) {
- headers.put(HEADER_KEY_IFMODIFIEDSINCE, result.lastModified);
- }
- }
-
- if (DebugFlags.CACHE_MANAGER) {
- Log.v(LOGTAG, "getCacheFile for url " + url);
- }
-
- return result;
- }
-
- /**
- * Given a URL and its full headers, gets a CacheResult if a local cache
- * can be stored. Otherwise returns null. The mimetype is passed in so that
- * the function can use the mimetype that will be passed to WebCore which
- * could be different from the mimetype defined in the headers.
- * forceCache is for out-of-package callers to force creation of a
- * CacheResult, and is used to supply surrogate responses for URL
- * interception.
- *
- * @return a CacheResult for a given URL
- */
- static CacheResult createCacheFile(String url, int statusCode,
- Headers headers, String mimeType, boolean forceCache) {
- // This method is public but hidden. We break functionality.
return null;
}
@@ -424,36 +338,4 @@ public final class CacheManager {
// use, we should already have thrown an exception above.
assert false;
}
-
- /**
- * Removes all cache files.
- *
- * @return whether the removal succeeded
- */
- static boolean removeAllCacheFiles() {
- // delete cache files in a separate thread to not block UI.
- final Runnable clearCache = new Runnable() {
- public void run() {
- // delete all cache files
- try {
- String[] files = mBaseDir.list();
- // if mBaseDir doesn't exist, files can be null.
- if (files != null) {
- for (int i = 0; i < files.length; i++) {
- File f = new File(mBaseDir, files[i]);
- if (!f.delete()) {
- Log.e(LOGTAG, f.getPath() + " delete failed.");
- }
- }
- }
- } catch (SecurityException e) {
- // Ignore SecurityExceptions.
- }
- }
- };
- new Thread(clearCache).start();
- return true;
- }
-
- private static native CacheResult nativeGetCacheResult(String url);
}
diff --git a/core/java/android/webkit/EventLogTags.logtags b/core/java/android/webkit/EventLogTags.logtags
index 082a437..b0b5493 100644
--- a/core/java/android/webkit/EventLogTags.logtags
+++ b/core/java/android/webkit/EventLogTags.logtags
@@ -8,4 +8,3 @@ option java_package android.webkit;
# 70103- used by the browser app itself
70150 browser_snap_center
-70151 browser_text_size_change (oldSize|1|5), (newSize|1|5)
diff --git a/core/java/android/webkit/HttpAuthHandler.java b/core/java/android/webkit/HttpAuthHandler.java
index 296d960..ee3b369 100644
--- a/core/java/android/webkit/HttpAuthHandler.java
+++ b/core/java/android/webkit/HttpAuthHandler.java
@@ -40,7 +40,7 @@ public class HttpAuthHandler extends Handler {
* previously been rejected by the server for the current request.
*
* @return whether the credentials are suitable for use
- * @see Webview#getHttpAuthUsernamePassword
+ * @see WebView#getHttpAuthUsernamePassword
*/
public boolean useHttpAuthUsernamePassword() {
return false;
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index aa68904..83f7990 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -89,6 +89,14 @@ public abstract class WebSettings {
ZoomDensity(int size) {
value = size;
}
+
+ /**
+ * @hide Only for use by WebViewProvider implementations
+ */
+ public int getValue() {
+ return value;
+ }
+
int value;
}
@@ -936,6 +944,9 @@ public abstract class WebSettings {
* access to content from other file scheme URLs. See
* {@link #setAllowFileAccessFromFileURLs}. To enable the most restrictive,
* and therefore secure policy, this setting should be disabled.
+ * Note that this setting affects only JavaScript access to file scheme
+ * resources. Other access to such resources, for example, from image HTML
+ * elements, is unaffected.
* <p>
* The default value is true for API level
* {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH_MR1} and below,
@@ -953,6 +964,9 @@ public abstract class WebSettings {
* enable the most restrictive, and therefore secure policy, this setting
* should be disabled. Note that the value of this setting is ignored if
* the value of {@link #getAllowUniversalAccessFromFileURLs} is true.
+ * Note too, that this setting affects only JavaScript access to file scheme
+ * resources. Other access to such resources, for example, from image HTML
+ * elements, is unaffected.
* <p>
* The default value is true for API level
* {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH_MR1} and below,
@@ -1052,7 +1066,7 @@ public abstract class WebSettings {
*
* @param appCachePath a String path to the directory containing
* Application Caches files.
- * @see setAppCacheEnabled
+ * @see #setAppCacheEnabled
*/
public synchronized void setAppCachePath(String appCachePath) {
throw new MustOverrideException();
@@ -1121,9 +1135,22 @@ public abstract class WebSettings {
}
/**
- * Sets whether Geolocation is enabled. The default is true. See also
- * {@link #setGeolocationDatabasePath} for how to correctly set up
- * Geolocation.
+ * Sets whether Geolocation is enabled. The default is true.
+ * <p>
+ * Please note that in order for the Geolocation API to be usable
+ * by a page in the WebView, the following requirements must be met:
+ * <ul>
+ * <li>an application must have permission to access the device location,
+ * see {@link android.Manifest.permission#ACCESS_COARSE_LOCATION},
+ * {@link android.Manifest.permission#ACCESS_FINE_LOCATION};
+ * <li>an application must provide an implementation of the
+ * {@link WebChromeClient#onGeolocationPermissionsShowPrompt} callback
+ * to receive notifications that a page is requesting access to location
+ * via the JavaScript Geolocation API.
+ * </ul>
+ * <p>
+ * As an option, it is possible to store previous locations and web origin
+ * permissions in a database. See {@link #setGeolocationDatabasePath}.
*
* @param flag whether Geolocation should be enabled
*/
@@ -1295,7 +1322,7 @@ public abstract class WebSettings {
* and content is re-validated as needed. When navigating back, content is
* not revalidated, instead the content is just retrieved from the cache.
* This method allows the client to override this behavior by specifying
- * one of {@link #LOAD_DEFAULT}, {@link #LOAD_NORMAL},
+ * one of {@link #LOAD_DEFAULT},
* {@link #LOAD_CACHE_ELSE_NETWORK}, {@link #LOAD_NO_CACHE} or
* {@link #LOAD_CACHE_ONLY}. The default value is {@link #LOAD_DEFAULT}.
*
diff --git a/core/java/android/webkit/WebSettingsClassic.java b/core/java/android/webkit/WebSettingsClassic.java
index 1bbe7bb..e3d095f 100644
--- a/core/java/android/webkit/WebSettingsClassic.java
+++ b/core/java/android/webkit/WebSettingsClassic.java
@@ -647,10 +647,6 @@ public class WebSettingsClassic extends WebSettings {
@Override
public synchronized void setTextZoom(int textZoom) {
if (mTextSize != textZoom) {
- if (WebViewClassic.mLogEvent) {
- EventLog.writeEvent(EventLogTags.BROWSER_TEXT_SIZE_CHANGE,
- mTextSize, textZoom);
- }
mTextSize = textZoom;
postSync();
}
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 6df7820..441b7b0 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -615,7 +615,7 @@ public class WebView extends AbsoluteLayout
* @param realm the realm to which the credentials apply
* @param username the username
* @param password the password
- * @see getHttpAuthUsernamePassword
+ * @see #getHttpAuthUsernamePassword
* @see WebViewDatabase#hasHttpAuthUsernamePassword
* @see WebViewDatabase#clearHttpAuthUsernamePassword
*/
@@ -635,7 +635,7 @@ public class WebView extends AbsoluteLayout
* @return the credentials as a String array, if found. The first element
* is the username and the second element is the password. Null if
* no credentials are found.
- * @see setHttpAuthUsernamePassword
+ * @see #setHttpAuthUsernamePassword
* @see WebViewDatabase#hasHttpAuthUsernamePassword
* @see WebViewDatabase#clearHttpAuthUsernamePassword
*/
@@ -2137,4 +2137,10 @@ public class WebView extends AbsoluteLayout
super.setLayerType(layerType, paint);
mProvider.getViewDelegate().setLayerType(layerType, paint);
}
+
+ @Override
+ protected void dispatchDraw(Canvas canvas) {
+ mProvider.getViewDelegate().preDispatchDraw(canvas);
+ super.dispatchDraw(canvas);
+ }
}
diff --git a/core/java/android/webkit/WebViewClassic.java b/core/java/android/webkit/WebViewClassic.java
index ae56e6b..9a83964 100644
--- a/core/java/android/webkit/WebViewClassic.java
+++ b/core/java/android/webkit/WebViewClassic.java
@@ -8560,6 +8560,11 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
updateHwAccelerated();
}
+ @Override
+ public void preDispatchDraw(Canvas canvas) {
+ // no-op for WebViewClassic.
+ }
+
private void updateHwAccelerated() {
if (mNativeClass == 0) {
return;
diff --git a/core/java/android/webkit/WebViewClient.java b/core/java/android/webkit/WebViewClient.java
index 08a046a..e8974c6 100644
--- a/core/java/android/webkit/WebViewClient.java
+++ b/core/java/android/webkit/WebViewClient.java
@@ -31,6 +31,7 @@ public class WebViewClient {
* proper handler for the url. If WebViewClient is provided, return true
* means the host application handles the url, while return false means the
* current WebView handles the url.
+ * This method is not called for requests using the POST "method".
*
* @param view The WebView that is initiating the callback.
* @param url The url to be loaded.
@@ -82,9 +83,9 @@ public class WebViewClient {
* Notify the host application of a resource request and allow the
* application to return the data. If the return value is null, the WebView
* will continue to load the resource as usual. Otherwise, the return
- * response and data will be used. NOTE: This method is called by the
- * network thread so clients should exercise caution when accessing private
- * data.
+ * response and data will be used. NOTE: This method is called on a thread
+ * other than the UI thread so clients should exercise caution
+ * when accessing private data or the view system.
*
* @param view The {@link android.webkit.WebView} that is requesting the
* resource.
@@ -213,7 +214,7 @@ public class WebViewClient {
* @param handler the HttpAuthHandler used to set the WebView's response
* @param host the host requiring authentication
* @param realm the realm for which authentication is required
- * @see Webview#getHttpAuthUsernamePassword
+ * @see WebView#getHttpAuthUsernamePassword
*/
public void onReceivedHttpAuthRequest(WebView view,
HttpAuthHandler handler, String host, String realm) {
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index 3fb3ec6..9e5a326 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -2001,9 +2001,6 @@ public final class WebViewCore {
private void clearCache(boolean includeDiskFiles) {
mBrowserFrame.clearCache();
- if (includeDiskFiles) {
- CacheManager.removeAllCacheFiles();
- }
}
private void loadUrl(String url, Map<String, String> extraHeaders) {
diff --git a/core/java/android/webkit/WebViewDatabase.java b/core/java/android/webkit/WebViewDatabase.java
index 5597259..e08052a 100644
--- a/core/java/android/webkit/WebViewDatabase.java
+++ b/core/java/android/webkit/WebViewDatabase.java
@@ -50,7 +50,7 @@ public class WebViewDatabase {
*
* @return true if there are any saved username/password pairs
* @see WebView#savePassword
- * @see clearUsernamePassword
+ * @see #clearUsernamePassword
*/
public boolean hasUsernamePassword() {
throw new MustOverrideException();
@@ -61,7 +61,7 @@ public class WebViewDatabase {
* Note that these are unrelated to HTTP authentication credentials.
*
* @see WebView#savePassword
- * @see hasUsernamePassword
+ * @see #hasUsernamePassword
*/
public void clearUsernamePassword() {
throw new MustOverrideException();
@@ -71,9 +71,9 @@ public class WebViewDatabase {
* Gets whether there are any saved credentials for HTTP authentication.
*
* @return whether there are any saved credentials
- * @see Webview#getHttpAuthUsernamePassword
- * @see Webview#setHttpAuthUsernamePassword
- * @see clearHttpAuthUsernamePassword
+ * @see WebView#getHttpAuthUsernamePassword
+ * @see WebView#setHttpAuthUsernamePassword
+ * @see #clearHttpAuthUsernamePassword
*/
public boolean hasHttpAuthUsernamePassword() {
throw new MustOverrideException();
@@ -82,9 +82,9 @@ public class WebViewDatabase {
/**
* Clears any saved credentials for HTTP authentication.
*
- * @see Webview#getHttpAuthUsernamePassword
- * @see Webview#setHttpAuthUsernamePassword
- * @see hasHttpAuthUsernamePassword
+ * @see WebView#getHttpAuthUsernamePassword
+ * @see WebView#setHttpAuthUsernamePassword
+ * @see #hasHttpAuthUsernamePassword
*/
public void clearHttpAuthUsernamePassword() {
throw new MustOverrideException();
@@ -94,7 +94,7 @@ public class WebViewDatabase {
* Gets whether there is any saved data for web forms.
*
* @return whether there is any saved data for web forms
- * @see clearFormData
+ * @see #clearFormData
*/
public boolean hasFormData() {
throw new MustOverrideException();
@@ -103,7 +103,7 @@ public class WebViewDatabase {
/**
* Clears any saved data for web forms.
*
- * @see hasFormData
+ * @see #hasFormData
*/
public void clearFormData() {
throw new MustOverrideException();
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index b833a01..18df0b1 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -31,7 +31,7 @@ class WebViewFactory {
// TODO: When the Chromium powered WebView is ready, it should be the default factory class.
private static final String DEFAULT_WEBVIEW_FACTORY = "android.webkit.WebViewClassic$Factory";
private static final String CHROMIUM_WEBVIEW_FACTORY =
- "com.android.webviewchromium.WebViewChromiumFactoryProvider";
+ "com.android.webview.chromium.WebViewChromiumFactoryProvider";
private static final String CHROMIUM_WEBVIEW_JAR = "/system/framework/webviewchromium.jar";
private static final String LOGTAG = "WebViewFactory";
diff --git a/core/java/android/webkit/WebViewProvider.java b/core/java/android/webkit/WebViewProvider.java
index c9f9fbd..1020634 100644
--- a/core/java/android/webkit/WebViewProvider.java
+++ b/core/java/android/webkit/WebViewProvider.java
@@ -341,6 +341,8 @@ public interface WebViewProvider {
public void setBackgroundColor(int color);
public void setLayerType(int layerType, Paint paint);
+
+ public void preDispatchDraw(Canvas canvas);
}
interface ScrollDelegate {
diff --git a/core/java/android/widget/AbsSpinner.java b/core/java/android/widget/AbsSpinner.java
index f279f8e..a379157 100644
--- a/core/java/android/widget/AbsSpinner.java
+++ b/core/java/android/widget/AbsSpinner.java
@@ -375,7 +375,7 @@ public abstract class AbsSpinner extends AdapterView<SpinnerAdapter> {
/**
* Constructor called from {@link #CREATOR}
*/
- private SavedState(Parcel in) {
+ SavedState(Parcel in) {
super(in);
selectedId = in.readLong();
position = in.readInt();
diff --git a/core/java/android/widget/CheckBox.java b/core/java/android/widget/CheckBox.java
index f1804f8..41ab5f2 100644
--- a/core/java/android/widget/CheckBox.java
+++ b/core/java/android/widget/CheckBox.java
@@ -20,6 +20,7 @@ import android.content.Context;
import android.util.AttributeSet;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
+import android.util.ValueModel;
/**
@@ -55,7 +56,9 @@ import android.view.accessibility.AccessibilityNodeInfo;
* {@link android.R.styleable#View View Attributes}
* </p>
*/
-public class CheckBox extends CompoundButton {
+public class CheckBox extends CompoundButton implements ValueEditor<Boolean> {
+ private ValueModel<Boolean> mValueModel = ValueModel.EMPTY;
+
public CheckBox(Context context) {
this(context, null);
}
@@ -79,4 +82,22 @@ public class CheckBox extends CompoundButton {
super.onInitializeAccessibilityNodeInfo(info);
info.setClassName(CheckBox.class.getName());
}
+
+ @Override
+ public ValueModel<Boolean> getValueModel() {
+ return mValueModel;
+ }
+
+ @Override
+ public void setValueModel(ValueModel<Boolean> valueModel) {
+ mValueModel = valueModel;
+ setChecked(mValueModel.get());
+ }
+
+ @Override
+ public boolean performClick() {
+ boolean handled = super.performClick();
+ mValueModel.set(isChecked());
+ return handled;
+ }
}
diff --git a/core/java/android/widget/EditText.java b/core/java/android/widget/EditText.java
index 57e51c2..ec81214 100644
--- a/core/java/android/widget/EditText.java
+++ b/core/java/android/widget/EditText.java
@@ -17,6 +17,7 @@
package android.widget;
import android.content.Context;
+import android.graphics.Rect;
import android.text.Editable;
import android.text.Selection;
import android.text.Spannable;
@@ -24,6 +25,7 @@ import android.text.TextUtils;
import android.text.method.ArrowKeyMovementMethod;
import android.text.method.MovementMethod;
import android.util.AttributeSet;
+import android.util.ValueModel;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
@@ -47,7 +49,9 @@ import android.view.accessibility.AccessibilityNodeInfo;
* {@link android.R.styleable#TextView TextView Attributes},
* {@link android.R.styleable#View View Attributes}
*/
-public class EditText extends TextView {
+public class EditText extends TextView implements ValueEditor<CharSequence> {
+ private ValueModel<CharSequence> mValueModel = ValueModel.EMPTY;
+
public EditText(Context context) {
this(context, null);
}
@@ -128,4 +132,21 @@ public class EditText extends TextView {
super.onInitializeAccessibilityNodeInfo(info);
info.setClassName(EditText.class.getName());
}
+
+ @Override
+ public ValueModel<CharSequence> getValueModel() {
+ return mValueModel;
+ }
+
+ @Override
+ public void setValueModel(ValueModel<CharSequence> valueModel) {
+ mValueModel = valueModel;
+ setText(mValueModel.get());
+ }
+
+ @Override
+ void sendAfterTextChanged(Editable text) {
+ super.sendAfterTextChanged(text);
+ mValueModel.set(text);
+ }
}
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 85972c3..217bedb 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -2638,15 +2638,10 @@ public class Editor {
suggestionStart, suggestionEnd).toString();
mTextView.replaceText_internal(spanStart, spanEnd, suggestion);
- // Notify source IME of the suggestion pick. Do this before swaping texts.
- if (!TextUtils.isEmpty(
- suggestionInfo.suggestionSpan.getNotificationTargetClassName())) {
- InputMethodManager imm = InputMethodManager.peekInstance();
- if (imm != null) {
- imm.notifySuggestionPicked(suggestionInfo.suggestionSpan, originalText,
- suggestionInfo.suggestionIndex);
- }
- }
+ // Notify source IME of the suggestion pick. Do this before
+ // swaping texts.
+ suggestionInfo.suggestionSpan.notifySelection(
+ mTextView.getContext(), originalText, suggestionInfo.suggestionIndex);
// Swap text content between actual text and Suggestion span
String[] suggestions = suggestionInfo.suggestionSpan.getSuggestions();
diff --git a/core/java/android/widget/GridLayout.java b/core/java/android/widget/GridLayout.java
index 772d748..85ed8db 100644
--- a/core/java/android/widget/GridLayout.java
+++ b/core/java/android/widget/GridLayout.java
@@ -605,7 +605,7 @@ public class GridLayout extends ViewGroup {
}
private int getDefaultMargin(View c, boolean isAtEdge, boolean horizontal, boolean leading) {
- return isAtEdge ? DEFAULT_CONTAINER_MARGIN : getDefaultMargin(c, horizontal, leading);
+ return /*isAtEdge ? DEFAULT_CONTAINER_MARGIN :*/ getDefaultMargin(c, horizontal, leading);
}
private int getDefaultMargin(View c, LayoutParams p, boolean horizontal, boolean leading) {
@@ -824,13 +824,11 @@ public class GridLayout extends ViewGroup {
// Draw grid
private void drawLine(Canvas graphics, int x1, int y1, int x2, int y2, Paint paint) {
- int dx = getPaddingLeft();
- int dy = getPaddingTop();
if (isLayoutRtl()) {
int width = getWidth();
- graphics.drawLine(width - dx - x1, dy + y1, width - dx - x2, dy + y2, paint);
+ graphics.drawLine(width - x1, y1, width - x2, y2, paint);
} else {
- graphics.drawLine(dx + x1, dy + y1, dx + x2, dy + y2, paint);
+ graphics.drawLine(x1, y1, x2, y2, paint);
}
}
@@ -838,18 +836,17 @@ public class GridLayout extends ViewGroup {
* @hide
*/
@Override
- protected void onDebugDrawMargins(Canvas canvas) {
+ protected void onDebugDrawMargins(Canvas canvas, Paint paint) {
// Apply defaults, so as to remove UNDEFINED values
LayoutParams lp = new LayoutParams();
for (int i = 0; i < getChildCount(); i++) {
View c = getChildAt(i);
- Insets insets = getLayoutMode() == OPTICAL_BOUNDS ? c.getOpticalInsets() : Insets.NONE;
lp.setMargins(
- getMargin1(c, true, true) - insets.left,
- getMargin1(c, false, true) - insets.top,
- getMargin1(c, true, false) - insets.right,
- getMargin1(c, false, false) - insets.bottom);
- lp.onDebugDraw(c, canvas);
+ getMargin1(c, true, true),
+ getMargin1(c, false, true),
+ getMargin1(c, true, false),
+ getMargin1(c, false, false));
+ lp.onDebugDraw(c, canvas, paint);
}
}
@@ -858,26 +855,30 @@ public class GridLayout extends ViewGroup {
*/
@Override
protected void onDebugDraw(Canvas canvas) {
- int height = getHeight() - getPaddingTop() - getPaddingBottom();
- int width = getWidth() - getPaddingLeft() - getPaddingRight();
-
Paint paint = new Paint();
paint.setStyle(Paint.Style.STROKE);
paint.setColor(Color.argb(50, 255, 255, 255));
+ Insets insets = getOpticalInsets();
+
+ int top = getPaddingTop() + insets.top;
+ int left = getPaddingLeft() + insets.left;
+ int right = getWidth() - getPaddingRight() - insets.right;
+ int bottom = getHeight() - getPaddingBottom() - insets.bottom;
+
int[] xs = horizontalAxis.locations;
if (xs != null) {
for (int i = 0, length = xs.length; i < length; i++) {
- int x = xs[i];
- drawLine(canvas, x, 0, x, height - 1, paint);
+ int x = left + xs[i];
+ drawLine(canvas, x, top, x, bottom, paint);
}
}
int[] ys = verticalAxis.locations;
if (ys != null) {
for (int i = 0, length = ys.length; i < length; i++) {
- int y = ys[i];
- drawLine(canvas, 0, y, width - 1, y, paint);
+ int y = top + ys[i];
+ drawLine(canvas, left, y, right, y, paint);
}
}
@@ -1013,12 +1014,7 @@ public class GridLayout extends ViewGroup {
}
private int getMeasurement(View c, boolean horizontal) {
- int result = horizontal ? c.getMeasuredWidth() : c.getMeasuredHeight();
- if (getLayoutMode() == OPTICAL_BOUNDS) {
- Insets insets = c.getOpticalInsets();
- return result - (horizontal ? insets.left + insets.right : insets.top + insets.bottom);
- }
- return result;
+ return horizontal ? c.getMeasuredWidth() : c.getMeasuredHeight();
}
final int getMeasurementIncludingMargin(View c, boolean horizontal) {
@@ -1124,14 +1120,6 @@ public class GridLayout extends ViewGroup {
targetWidth - width - paddingRight - rightMargin - dx;
int cy = paddingTop + y1 + gravityOffsetY + alignmentOffsetY + topMargin;
- boolean useLayoutBounds = getLayoutMode() == OPTICAL_BOUNDS;
- if (useLayoutBounds) {
- Insets insets = c.getOpticalInsets();
- cx -= insets.left;
- cy -= insets.top;
- width += (insets.left + insets.right);
- height += (insets.top + insets.bottom);
- }
if (width != c.getMeasuredWidth() || height != c.getMeasuredHeight()) {
c.measure(makeMeasureSpec(width, EXACTLY), makeMeasureSpec(height, EXACTLY));
}
@@ -2418,6 +2406,8 @@ public class GridLayout extends ViewGroup {
* <li> {@code spec.span = [start, start + size]} </li>
* <li> {@code spec.alignment = alignment} </li>
* </ul>
+ * <p>
+ * To leave the start index undefined, use the value {@link #UNDEFINED}.
*
* @param start the start
* @param size the size
@@ -2433,9 +2423,13 @@ public class GridLayout extends ViewGroup {
* <li> {@code spec.span = [start, start + 1]} </li>
* <li> {@code spec.alignment = alignment} </li>
* </ul>
+ * <p>
+ * To leave the start index undefined, use the value {@link #UNDEFINED}.
*
* @param start the start index
* @param alignment the alignment
+ *
+ * @see #spec(int, int, Alignment)
*/
public static Spec spec(int start, Alignment alignment) {
return spec(start, 1, alignment);
@@ -2446,9 +2440,13 @@ public class GridLayout extends ViewGroup {
* <ul>
* <li> {@code spec.span = [start, start + size]} </li>
* </ul>
+ * <p>
+ * To leave the start index undefined, use the value {@link #UNDEFINED}.
*
* @param start the start
* @param size the size
+ *
+ * @see #spec(int, Alignment)
*/
public static Spec spec(int start, int size) {
return spec(start, size, UNDEFINED_ALIGNMENT);
@@ -2459,8 +2457,12 @@ public class GridLayout extends ViewGroup {
* <ul>
* <li> {@code spec.span = [start, start + 1]} </li>
* </ul>
+ * <p>
+ * To leave the start index undefined, use the value {@link #UNDEFINED}.
*
* @param start the start index
+ *
+ * @see #spec(int, int)
*/
public static Spec spec(int start) {
return spec(start, 1);
@@ -2654,14 +2656,7 @@ public class GridLayout extends ViewGroup {
@Override
public int getAlignmentValue(View view, int viewSize, int mode) {
int baseline = view.getBaseline();
- if (baseline == -1) {
- return UNDEFINED;
- } else {
- if (mode == OPTICAL_BOUNDS) {
- return baseline - view.getOpticalInsets().top;
- }
- return baseline;
- }
+ return baseline == -1 ? UNDEFINED : baseline;
}
@Override
diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java
index 87396fb..b40260c 100644
--- a/core/java/android/widget/ImageView.java
+++ b/core/java/android/widget/ImageView.java
@@ -30,6 +30,7 @@ import android.graphics.RectF;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
+import android.os.Build;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
@@ -40,6 +41,9 @@ import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.RemoteViews.RemoteView;
+import java.io.IOException;
+import java.io.InputStream;
+
/**
* Displays an arbitrary image, such as an icon. The ImageView class
* can load images from various sources (such as resources or content
@@ -90,6 +94,9 @@ public class ImageView extends View {
private int mBaseline = -1;
private boolean mBaselineAlignBottom = false;
+ // AdjustViewBounds behavior will be in compatibility mode for older apps.
+ private boolean mAdjustViewBoundsCompat = false;
+
private static final ScaleType[] sScaleTypeArray = {
ScaleType.MATRIX,
ScaleType.FIT_XY,
@@ -164,6 +171,8 @@ public class ImageView extends View {
private void initImageView() {
mMatrix = new Matrix();
mScaleType = ScaleType.FIT_CENTER;
+ mAdjustViewBoundsCompat = mContext.getApplicationInfo().targetSdkVersion <=
+ Build.VERSION_CODES.JELLY_BEAN_MR1;
}
@Override
@@ -225,8 +234,15 @@ public class ImageView extends View {
/**
* Set this to true if you want the ImageView to adjust its bounds
* to preserve the aspect ratio of its drawable.
+ *
+ * <p><strong>Note:</strong> If the application targets API level 17 or lower,
+ * adjustViewBounds will allow the drawable to shrink the view bounds, but not grow
+ * to fill available measured space in all cases. This is for compatibility with
+ * legacy {@link android.view.View.MeasureSpec MeasureSpec} and
+ * {@link android.widget.RelativeLayout RelativeLayout} behavior.</p>
+ *
* @param adjustViewBounds Whether to adjust the bounds of this view
- * to presrve the original aspect ratio of the drawable
+ * to preserve the original aspect ratio of the drawable.
*
* @see #getAdjustViewBounds()
*
@@ -632,20 +648,27 @@ public class ImageView extends View {
}
} else if (ContentResolver.SCHEME_CONTENT.equals(scheme)
|| ContentResolver.SCHEME_FILE.equals(scheme)) {
+ InputStream stream = null;
try {
- d = Drawable.createFromStream(
- mContext.getContentResolver().openInputStream(mUri),
- null);
+ stream = mContext.getContentResolver().openInputStream(mUri);
+ d = Drawable.createFromStream(stream, null);
} catch (Exception e) {
Log.w("ImageView", "Unable to open content: " + mUri, e);
+ } finally {
+ if (stream != null) {
+ try {
+ stream.close();
+ } catch (IOException e) {
+ Log.w("ImageView", "Unable to close content: " + mUri, e);
+ }
+ }
}
- } else {
+ } else {
d = Drawable.createFromPath(mUri.toString());
}
if (d == null) {
- System.out.println("resolveUri failed on bad bitmap uri: "
- + mUri);
+ System.out.println("resolveUri failed on bad bitmap uri: " + mUri);
// Don't try again.
mUri = null;
}
@@ -789,6 +812,12 @@ public class ImageView extends View {
if (resizeWidth) {
int newWidth = (int)(desiredAspect * (heightSize - ptop - pbottom)) +
pleft + pright;
+
+ // Allow the width to outgrow its original estimate if height is fixed.
+ if (!resizeHeight && !mAdjustViewBoundsCompat) {
+ widthSize = resolveAdjustedSize(newWidth, mMaxWidth, widthMeasureSpec);
+ }
+
if (newWidth <= widthSize) {
widthSize = newWidth;
done = true;
@@ -799,6 +828,13 @@ public class ImageView extends View {
if (!done && resizeHeight) {
int newHeight = (int)((widthSize - pleft - pright) / desiredAspect) +
ptop + pbottom;
+
+ // Allow the height to outgrow its original estimate if width is fixed.
+ if (!resizeWidth && !mAdjustViewBoundsCompat) {
+ heightSize = resolveAdjustedSize(newHeight, mMaxHeight,
+ heightMeasureSpec);
+ }
+
if (newHeight <= heightSize) {
heightSize = newHeight;
}
diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java
index 36dd13c..bc57c36 100644
--- a/core/java/android/widget/LinearLayout.java
+++ b/core/java/android/widget/LinearLayout.java
@@ -1431,9 +1431,9 @@ public class LinearLayout extends ViewGroup {
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
if (mOrientation == VERTICAL) {
- layoutVertical();
+ layoutVertical(l, t, r, b);
} else {
- layoutHorizontal();
+ layoutHorizontal(l, t, r, b);
}
}
@@ -1444,15 +1444,19 @@ public class LinearLayout extends ViewGroup {
* @see #getOrientation()
* @see #setOrientation(int)
* @see #onLayout(boolean, int, int, int, int)
+ * @param left
+ * @param top
+ * @param right
+ * @param bottom
*/
- void layoutVertical() {
+ void layoutVertical(int left, int top, int right, int bottom) {
final int paddingLeft = mPaddingLeft;
int childTop;
int childLeft;
// Where right end of child should go
- final int width = mRight - mLeft;
+ final int width = right - left;
int childRight = width - mPaddingRight;
// Space available for child
@@ -1466,12 +1470,12 @@ public class LinearLayout extends ViewGroup {
switch (majorGravity) {
case Gravity.BOTTOM:
// mTotalLength contains the padding already
- childTop = mPaddingTop + mBottom - mTop - mTotalLength;
+ childTop = mPaddingTop + bottom - top - mTotalLength;
break;
// mTotalLength contains the padding already
case Gravity.CENTER_VERTICAL:
- childTop = mPaddingTop + (mBottom - mTop - mTotalLength) / 2;
+ childTop = mPaddingTop + (bottom - top - mTotalLength) / 2;
break;
case Gravity.TOP:
@@ -1534,8 +1538,12 @@ public class LinearLayout extends ViewGroup {
* @see #getOrientation()
* @see #setOrientation(int)
* @see #onLayout(boolean, int, int, int, int)
+ * @param left
+ * @param top
+ * @param right
+ * @param bottom
*/
- void layoutHorizontal() {
+ void layoutHorizontal(int left, int top, int right, int bottom) {
final boolean isLayoutRtl = isLayoutRtl();
final int paddingTop = mPaddingTop;
@@ -1543,7 +1551,7 @@ public class LinearLayout extends ViewGroup {
int childLeft;
// Where bottom of child should go
- final int height = mBottom - mTop;
+ final int height = bottom - top;
int childBottom = height - mPaddingBottom;
// Space available for child
@@ -1563,12 +1571,12 @@ public class LinearLayout extends ViewGroup {
switch (Gravity.getAbsoluteGravity(majorGravity, layoutDirection)) {
case Gravity.RIGHT:
// mTotalLength contains the padding already
- childLeft = mPaddingLeft + mRight - mLeft - mTotalLength;
+ childLeft = mPaddingLeft + right - left - mTotalLength;
break;
case Gravity.CENTER_HORIZONTAL:
// mTotalLength contains the padding already
- childLeft = mPaddingLeft + (mRight - mLeft - mTotalLength) / 2;
+ childLeft = mPaddingLeft + (right - left - mTotalLength) / 2;
break;
case Gravity.LEFT:
diff --git a/core/java/android/widget/MediaController.java b/core/java/android/widget/MediaController.java
index f76ab2b..ee1bf18 100644
--- a/core/java/android/widget/MediaController.java
+++ b/core/java/android/widget/MediaController.java
@@ -149,7 +149,7 @@ public class MediaController extends FrameLayout {
private void initFloatingWindowLayout() {
mDecorLayoutParams = new WindowManager.LayoutParams();
WindowManager.LayoutParams p = mDecorLayoutParams;
- p.gravity = Gravity.TOP;
+ p.gravity = Gravity.TOP | Gravity.LEFT;
p.height = LayoutParams.WRAP_CONTENT;
p.x = 0;
p.format = PixelFormat.TRANSLUCENT;
@@ -167,9 +167,15 @@ public class MediaController extends FrameLayout {
int [] anchorPos = new int[2];
mAnchor.getLocationOnScreen(anchorPos);
+ // we need to know the size of the controller so we can properly position it
+ // within its space
+ mDecor.measure(MeasureSpec.makeMeasureSpec(mAnchor.getWidth(), MeasureSpec.AT_MOST),
+ MeasureSpec.makeMeasureSpec(mAnchor.getHeight(), MeasureSpec.AT_MOST));
+
WindowManager.LayoutParams p = mDecorLayoutParams;
p.width = mAnchor.getWidth();
- p.y = anchorPos[1] + mAnchor.getHeight();
+ p.x = anchorPos[0] + (mAnchor.getWidth() - p.width) / 2;
+ p.y = anchorPos[1] + mAnchor.getHeight() - mDecor.getMeasuredHeight();
}
// This is called whenever mAnchor's layout bound changes
@@ -204,6 +210,8 @@ public class MediaController extends FrameLayout {
/**
* Set the view that acts as the anchor for the control view.
* This can for example be a VideoView, or your Activity's main view.
+ * When VideoView calls this method, it will use the VideoView's parent
+ * as the anchor.
* @param view The view to which to anchor the controller when it is visible.
*/
public void setAnchorView(View view) {
diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index ea50e2e..f2d2c65 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -38,10 +38,7 @@ import android.graphics.drawable.shapes.Shape;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.AttributeSet;
-import android.util.Pool;
-import android.util.Poolable;
-import android.util.PoolableManager;
-import android.util.Pools;
+import android.util.Pools.SynchronizedPool;
import android.view.Gravity;
import android.view.RemotableViewMethod;
import android.view.View;
@@ -604,33 +601,20 @@ public class ProgressBar extends View {
}
}
- private static class RefreshData implements Poolable<RefreshData> {
+ private static class RefreshData {
+ private static final int POOL_MAX = 24;
+ private static final SynchronizedPool<RefreshData> sPool =
+ new SynchronizedPool<RefreshData>(POOL_MAX);
+
public int id;
public int progress;
public boolean fromUser;
-
- private RefreshData mNext;
- private boolean mIsPooled;
-
- private static final int POOL_MAX = 24;
- private static final Pool<RefreshData> sPool = Pools.synchronizedPool(
- Pools.finitePool(new PoolableManager<RefreshData>() {
- @Override
- public RefreshData newInstance() {
- return new RefreshData();
- }
-
- @Override
- public void onAcquired(RefreshData element) {
- }
-
- @Override
- public void onReleased(RefreshData element) {
- }
- }, POOL_MAX));
public static RefreshData obtain(int id, int progress, boolean fromUser) {
RefreshData rd = sPool.acquire();
+ if (rd == null) {
+ rd = new RefreshData();
+ }
rd.id = id;
rd.progress = progress;
rd.fromUser = fromUser;
@@ -640,28 +624,8 @@ public class ProgressBar extends View {
public void recycle() {
sPool.release(this);
}
-
- @Override
- public void setNextPoolable(RefreshData element) {
- mNext = element;
- }
-
- @Override
- public RefreshData getNextPoolable() {
- return mNext;
- }
-
- @Override
- public boolean isPooled() {
- return mIsPooled;
- }
-
- @Override
- public void setPooled(boolean isPooled) {
- mIsPooled = isPooled;
- }
}
-
+
private synchronized void doRefreshProgress(int id, int progress, boolean fromUser,
boolean callBackToApp) {
float scale = mMax > 0 ? (float) progress / (float) mMax : 0;
diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java
index 27fda24..e749e63 100644
--- a/core/java/android/widget/RelativeLayout.java
+++ b/core/java/android/widget/RelativeLayout.java
@@ -29,11 +29,9 @@ import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Rect;
+import android.os.Build;
import android.util.AttributeSet;
-import android.util.Pool;
-import android.util.Poolable;
-import android.util.PoolableManager;
-import android.util.Pools;
+import android.util.Pools.SimplePool;
import android.util.SparseArray;
import android.view.Gravity;
import android.view.View;
@@ -56,6 +54,21 @@ import static android.util.Log.d;
* {@link #ALIGN_PARENT_BOTTOM}.
* </p>
*
+ * <p><strong>Note:</strong> In platform version 17 and lower, RelativeLayout was affected by
+ * a measurement bug that could cause child views to be measured with incorrect
+ * {@link android.view.View.MeasureSpec MeasureSpec} values. (See
+ * {@link android.view.View.MeasureSpec#makeMeasureSpec(int, int) MeasureSpec.makeMeasureSpec}
+ * for more details.) This was triggered when a RelativeLayout container was placed in
+ * a scrolling container, such as a ScrollView or HorizontalScrollView. If a custom view
+ * not equipped to properly measure with the MeasureSpec mode
+ * {@link android.view.View.MeasureSpec#UNSPECIFIED UNSPECIFIED} was placed in a RelativeLayout,
+ * this would silently work anyway as RelativeLayout would pass a very large
+ * {@link android.view.View.MeasureSpec#AT_MOST AT_MOST} MeasureSpec instead.</p>
+ *
+ * <p>This behavior has been preserved for apps that set <code>android:targetSdkVersion="17"</code>
+ * or older in their manifest's <code>uses-sdk</code> tag for compatibility. Apps targeting SDK
+ * version 18 or newer will receive the correct behavior</p>
+ *
* <p>See the <a href="{@docRoot}guide/topics/ui/layout/relative.html">Relative
* Layout</a> guide.</p>
*
@@ -202,18 +215,29 @@ public class RelativeLayout extends ViewGroup {
private View[] mSortedVerticalChildren = new View[0];
private final DependencyGraph mGraph = new DependencyGraph();
+ // Compatibility hack. Old versions of the platform had problems
+ // with MeasureSpec value overflow and RelativeLayout was one source of them.
+ // Some apps came to rely on them. :(
+ private boolean mAllowBrokenMeasureSpecs = false;
+
public RelativeLayout(Context context) {
super(context);
+ mAllowBrokenMeasureSpecs = context.getApplicationInfo().targetSdkVersion <=
+ Build.VERSION_CODES.JELLY_BEAN_MR1;
}
public RelativeLayout(Context context, AttributeSet attrs) {
super(context, attrs);
initFromAttributes(context, attrs);
+ mAllowBrokenMeasureSpecs = context.getApplicationInfo().targetSdkVersion <=
+ Build.VERSION_CODES.JELLY_BEAN_MR1;
}
public RelativeLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initFromAttributes(context, attrs);
+ mAllowBrokenMeasureSpecs = context.getApplicationInfo().targetSdkVersion <=
+ Build.VERSION_CODES.JELLY_BEAN_MR1;
}
private void initFromAttributes(Context context, AttributeSet attrs) {
@@ -673,7 +697,12 @@ public class RelativeLayout extends ViewGroup {
mPaddingLeft, mPaddingRight,
myWidth);
int childHeightMeasureSpec;
- if (params.width == LayoutParams.MATCH_PARENT) {
+ if (myHeight < 0 && !mAllowBrokenMeasureSpecs) {
+ // Negative values in a mySize/myWidth/myWidth value in RelativeLayout measurement
+ // is code for, "we got an unspecified mode in the RelativeLayout's measurespec."
+ // Carry it forward.
+ childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+ } else if (params.width == LayoutParams.MATCH_PARENT) {
childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(myHeight, MeasureSpec.EXACTLY);
} else {
childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(myHeight, MeasureSpec.AT_MOST);
@@ -700,6 +729,13 @@ public class RelativeLayout extends ViewGroup {
private int getChildMeasureSpec(int childStart, int childEnd,
int childSize, int startMargin, int endMargin, int startPadding,
int endPadding, int mySize) {
+ if (mySize < 0 && !mAllowBrokenMeasureSpecs) {
+ // Negative values in a mySize/myWidth/myWidth value in RelativeLayout measurement
+ // is code for, "we got an unspecified mode in the RelativeLayout's measurespec."
+ // Carry it forward.
+ return MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+ }
+
int childSpecMode = 0;
int childSpecSize = 0;
@@ -1655,7 +1691,7 @@ public class RelativeLayout extends ViewGroup {
*
* A node with no dependent is considered a root of the graph.
*/
- static class Node implements Poolable<Node> {
+ static class Node {
/**
* The view representing this node in the layout.
*/
@@ -1678,43 +1714,14 @@ public class RelativeLayout extends ViewGroup {
// The pool is static, so all nodes instances are shared across
// activities, that's why we give it a rather high limit
private static final int POOL_LIMIT = 100;
- private static final Pool<Node> sPool = Pools.synchronizedPool(
- Pools.finitePool(new PoolableManager<Node>() {
- public Node newInstance() {
- return new Node();
- }
-
- public void onAcquired(Node element) {
- }
-
- public void onReleased(Node element) {
- }
- }, POOL_LIMIT)
- );
-
- private Node mNext;
- private boolean mIsPooled;
-
- public void setNextPoolable(Node element) {
- mNext = element;
- }
-
- public Node getNextPoolable() {
- return mNext;
- }
-
- public boolean isPooled() {
- return mIsPooled;
- }
-
- public void setPooled(boolean isPooled) {
- mIsPooled = isPooled;
- }
+ private static final SimplePool<Node> sPool = new SimplePool<Node>(POOL_LIMIT);
static Node acquire(View view) {
- final Node node = sPool.acquire();
+ Node node = sPool.acquire();
+ if (node == null) {
+ node = new Node();
+ }
node.view = view;
-
return node;
}
diff --git a/core/java/android/widget/SearchView.java b/core/java/android/widget/SearchView.java
index cd8638d..0281602 100644
--- a/core/java/android/widget/SearchView.java
+++ b/core/java/android/widget/SearchView.java
@@ -1247,6 +1247,7 @@ public class SearchView extends LinearLayout implements CollapsibleActionView {
*/
@Override
public void onActionViewCollapsed() {
+ setQuery("", false);
clearFocus();
updateViewsVisibility(true);
mQueryTextView.setImeOptions(mCollapsedImeOptions);
diff --git a/core/java/android/widget/SeekBar.java b/core/java/android/widget/SeekBar.java
index 2737f94..a6486a8 100644
--- a/core/java/android/widget/SeekBar.java
+++ b/core/java/android/widget/SeekBar.java
@@ -18,6 +18,7 @@ package android.widget;
import android.content.Context;
import android.util.AttributeSet;
+import android.util.ValueModel;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
@@ -33,7 +34,7 @@ import android.view.accessibility.AccessibilityNodeInfo;
*
* @attr ref android.R.styleable#SeekBar_thumb
*/
-public class SeekBar extends AbsSeekBar {
+public class SeekBar extends AbsSeekBar implements ValueEditor<Integer> {
/**
* A callback that notifies clients when the progress level has been
@@ -69,8 +70,9 @@ public class SeekBar extends AbsSeekBar {
void onStopTrackingTouch(SeekBar seekBar);
}
+ private ValueModel<Integer> mValueModel = ValueModel.EMPTY;
private OnSeekBarChangeListener mOnSeekBarChangeListener;
-
+
public SeekBar(Context context) {
this(context, null);
}
@@ -89,9 +91,23 @@ public class SeekBar extends AbsSeekBar {
if (mOnSeekBarChangeListener != null) {
mOnSeekBarChangeListener.onProgressChanged(this, getProgress(), fromUser);
+ if (fromUser) {
+ mValueModel.set(getProgress());
+ }
}
}
+ @Override
+ public ValueModel<Integer> getValueModel() {
+ return mValueModel;
+ }
+
+ @Override
+ public void setValueModel(ValueModel<Integer> valueModel) {
+ mValueModel = valueModel;
+ setProgress(mValueModel.get());
+ }
+
/**
* Sets a listener to receive notifications of changes to the SeekBar's progress level. Also
* provides notifications of when the user starts and stops a touch gesture within the SeekBar.
diff --git a/core/java/android/widget/SpellChecker.java b/core/java/android/widget/SpellChecker.java
index 74ea038..9e7f97e 100644
--- a/core/java/android/widget/SpellChecker.java
+++ b/core/java/android/widget/SpellChecker.java
@@ -109,7 +109,7 @@ public class SpellChecker implements SpellCheckerSessionListener {
mIds = new int[size];
mSpellCheckSpans = new SpellCheckSpan[size];
- setLocale(mTextView.getTextServicesLocale());
+ setLocale(mTextView.getSpellCheckerLocale());
mCookie = hashCode();
}
@@ -120,7 +120,8 @@ public class SpellChecker implements SpellCheckerSessionListener {
mTextServicesManager = (TextServicesManager) mTextView.getContext().
getSystemService(Context.TEXT_SERVICES_MANAGER_SERVICE);
if (!mTextServicesManager.isSpellCheckerEnabled()
- || mTextServicesManager.getCurrentSpellCheckerSubtype(true) == null) {
+ || mCurrentLocale == null
+ || mTextServicesManager.getCurrentSpellCheckerSubtype(true) == null) {
mSpellCheckerSession = null;
} else {
mSpellCheckerSession = mTextServicesManager.newSpellCheckerSession(
@@ -146,8 +147,10 @@ public class SpellChecker implements SpellCheckerSessionListener {
resetSession();
- // Change SpellParsers' wordIterator locale
- mWordIterator = new WordIterator(locale);
+ if (locale != null) {
+ // Change SpellParsers' wordIterator locale
+ mWordIterator = new WordIterator(locale);
+ }
// This class is the listener for locale change: warn other locale-aware objects
mTextView.onLocaleChanged();
@@ -222,9 +225,9 @@ public class SpellChecker implements SpellCheckerSessionListener {
if (DBG) {
Log.d(TAG, "Start spell-checking: " + start + ", " + end);
}
- final Locale locale = mTextView.getTextServicesLocale();
+ final Locale locale = mTextView.getSpellCheckerLocale();
final boolean isSessionActive = isSessionActive();
- if (mCurrentLocale == null || (!(mCurrentLocale.equals(locale)))) {
+ if (locale == null || mCurrentLocale == null || (!(mCurrentLocale.equals(locale)))) {
setLocale(locale);
// Re-check the entire text
start = 0;
diff --git a/core/java/android/widget/Spinner.java b/core/java/android/widget/Spinner.java
index 925864c..fa64fd3 100644
--- a/core/java/android/widget/Spinner.java
+++ b/core/java/android/widget/Spinner.java
@@ -25,6 +25,8 @@ import android.content.res.TypedArray;
import android.database.DataSetObserver;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
@@ -697,6 +699,69 @@ public class Spinner extends AbsSpinner implements OnClickListener {
return width;
}
+ @Override
+ public Parcelable onSaveInstanceState() {
+ final SavedState ss = new SavedState(super.onSaveInstanceState());
+ ss.showDropdown = mPopup != null && mPopup.isShowing();
+ return ss;
+ }
+
+ @Override
+ public void onRestoreInstanceState(Parcelable state) {
+ SavedState ss = (SavedState) state;
+
+ super.onRestoreInstanceState(ss.getSuperState());
+
+ if (ss.showDropdown) {
+ ViewTreeObserver vto = getViewTreeObserver();
+ if (vto != null) {
+ final OnGlobalLayoutListener listener = new OnGlobalLayoutListener() {
+ @Override
+ public void onGlobalLayout() {
+ if (!mPopup.isShowing()) {
+ mPopup.show();
+ }
+ final ViewTreeObserver vto = getViewTreeObserver();
+ if (vto != null) {
+ vto.removeOnGlobalLayoutListener(this);
+ }
+ }
+ };
+ vto.addOnGlobalLayoutListener(listener);
+ }
+ }
+ }
+
+ static class SavedState extends AbsSpinner.SavedState {
+ boolean showDropdown;
+
+ SavedState(Parcelable superState) {
+ super(superState);
+ }
+
+ private SavedState(Parcel in) {
+ super(in);
+ showDropdown = in.readByte() != 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ super.writeToParcel(out, flags);
+ out.writeByte((byte) (showDropdown ? 1 : 0));
+ }
+
+ public static final Parcelable.Creator<SavedState> CREATOR =
+ new Parcelable.Creator<SavedState>() {
+ public SavedState createFromParcel(Parcel in) {
+ return new SavedState(in);
+ }
+
+ public SavedState[] newArray(int size) {
+ return new SavedState[size];
+ }
+ };
+ }
+
/**
* <p>Wrapper class for an Adapter. Transforms the embedded Adapter instance
* into a ListAdapter.</p>
@@ -941,8 +1006,7 @@ public class Spinner extends AbsSpinner implements OnClickListener {
mHintText = hintText;
}
- @Override
- public void show() {
+ void computeContentWidth() {
final Drawable background = getBackground();
int hOffset = 0;
if (background != null) {
@@ -955,6 +1019,7 @@ public class Spinner extends AbsSpinner implements OnClickListener {
final int spinnerPaddingLeft = Spinner.this.getPaddingLeft();
final int spinnerPaddingRight = Spinner.this.getPaddingRight();
final int spinnerWidth = Spinner.this.getWidth();
+
if (mDropDownWidth == WRAP_CONTENT) {
int contentWidth = measureContentWidth(
(SpinnerAdapter) mAdapter, getBackground());
@@ -977,11 +1042,25 @@ public class Spinner extends AbsSpinner implements OnClickListener {
hOffset += spinnerPaddingLeft;
}
setHorizontalOffset(hOffset);
+ }
+
+ @Override
+ public void show() {
+ final boolean wasShowing = isShowing();
+
+ computeContentWidth();
+
setInputMethodMode(ListPopupWindow.INPUT_METHOD_NOT_NEEDED);
super.show();
getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE);
setSelection(Spinner.this.getSelectedItemPosition());
+ if (wasShowing) {
+ // Skip setting up the layout/dismiss listener below. If we were previously
+ // showing it will still stick around.
+ return;
+ }
+
// Make sure we hide if our anchor goes away.
// TODO: This might be appropriate to push all the way down to PopupWindow,
// but it may have other side effects to investigate first. (Text editing handles, etc.)
@@ -992,6 +1071,12 @@ public class Spinner extends AbsSpinner implements OnClickListener {
public void onGlobalLayout() {
if (!Spinner.this.isVisibleToUser()) {
dismiss();
+ } else {
+ computeContentWidth();
+
+ // Use super.show here to update; we don't want to move the selected
+ // position or adjust other things that would be reset otherwise.
+ DropdownPopup.super.show();
}
}
};
diff --git a/core/java/android/widget/TableLayout.java b/core/java/android/widget/TableLayout.java
index 399b4fa..f4b2ce0 100644
--- a/core/java/android/widget/TableLayout.java
+++ b/core/java/android/widget/TableLayout.java
@@ -445,7 +445,7 @@ public class TableLayout extends LinearLayout {
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
// enforce vertical layout
- layoutVertical();
+ layoutVertical(l, t, r, b);
}
/**
diff --git a/core/java/android/widget/TableRow.java b/core/java/android/widget/TableRow.java
index 35927e0..fe3631a 100644
--- a/core/java/android/widget/TableRow.java
+++ b/core/java/android/widget/TableRow.java
@@ -120,7 +120,7 @@ public class TableRow extends LinearLayout {
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
// enforce horizontal layout
- layoutHorizontal();
+ layoutHorizontal(l, t, r, b);
}
/**
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 22bfadb..1f64c5b 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -26,6 +26,7 @@ import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.graphics.Canvas;
+import android.graphics.Insets;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
@@ -510,7 +511,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
private InputFilter[] mFilters = NO_FILTERS;
- private volatile Locale mCurrentTextServicesLocaleCache;
+ private volatile Locale mCurrentSpellCheckerLocaleCache;
private final ReentrantLock mCurrentTextServicesLocaleLock = new ReentrantLock();
// It is possible to have a selection even when mEditor is null (programmatically set, like when
@@ -4349,6 +4350,14 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
/////////////////////////////////////////////////////////////////////////
+ private int getBoxHeight(Layout l) {
+ Insets opticalInsets = isLayoutModeOptical(mParent) ? getOpticalInsets() : Insets.NONE;
+ int padding = (l == mHintLayout) ?
+ getCompoundPaddingTop() + getCompoundPaddingBottom() :
+ getExtendedPaddingTop() + getExtendedPaddingBottom();
+ return getMeasuredHeight() - padding + opticalInsets.top + opticalInsets.bottom;
+ }
+
int getVerticalOffset(boolean forceNormal) {
int voffset = 0;
final int gravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
@@ -4359,15 +4368,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
if (gravity != Gravity.TOP) {
- int boxht;
-
- if (l == mHintLayout) {
- boxht = getMeasuredHeight() - getCompoundPaddingTop() -
- getCompoundPaddingBottom();
- } else {
- boxht = getMeasuredHeight() - getExtendedPaddingTop() -
- getExtendedPaddingBottom();
- }
+ int boxht = getBoxHeight(l);
int textht = l.getHeight();
if (textht < boxht) {
@@ -4390,15 +4391,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
if (gravity != Gravity.BOTTOM) {
- int boxht;
-
- if (l == mHintLayout) {
- boxht = getMeasuredHeight() - getCompoundPaddingTop() -
- getCompoundPaddingBottom();
- } else {
- boxht = getMeasuredHeight() - getExtendedPaddingTop() -
- getExtendedPaddingBottom();
- }
+ int boxht = getBoxHeight(l);
int textht = l.getHeight();
if (textht < boxht) {
@@ -5144,6 +5137,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
voffset = getVerticalOffset(true);
}
+ if (isLayoutModeOptical(mParent)) {
+ voffset -= getOpticalInsets().top;
+ }
+
return getExtendedPaddingTop() + voffset + mLayout.getLineBaseline(0);
}
@@ -7828,27 +7825,46 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
(isTextSelectable() && mText instanceof Spannable && isEnabled());
}
+ private Locale getTextServicesLocale(boolean allowNullLocale) {
+ // Start fetching the text services locale asynchronously.
+ updateTextServicesLocaleAsync();
+ // If !allowNullLocale and there is no cached text services locale, just return the default
+ // locale.
+ return (mCurrentSpellCheckerLocaleCache == null && !allowNullLocale) ? Locale.getDefault()
+ : mCurrentSpellCheckerLocaleCache;
+ }
+
/**
* This is a temporary method. Future versions may support multi-locale text.
* Caveat: This method may not return the latest text services locale, but this should be
* acceptable and it's more important to make this method asynchronous.
*
- * @return The locale that should be used for a word iterator and a spell checker
+ * @return The locale that should be used for a word iterator
* in this TextView, based on the current spell checker settings,
* the current IME's locale, or the system default locale.
+ * Please note that a word iterator in this TextView is different from another word iterator
+ * used by SpellChecker.java of TextView. This method should be used for the former.
* @hide
*/
// TODO: Support multi-locale
// TODO: Update the text services locale immediately after the keyboard locale is switched
// by catching intent of keyboard switch event
public Locale getTextServicesLocale() {
- if (mCurrentTextServicesLocaleCache == null) {
- // If there is no cached text services locale, just return the default locale.
- mCurrentTextServicesLocaleCache = Locale.getDefault();
- }
- // Start fetching the text services locale asynchronously.
- updateTextServicesLocaleAsync();
- return mCurrentTextServicesLocaleCache;
+ return getTextServicesLocale(false /* allowNullLocale */);
+ }
+
+ /**
+ * This is a temporary method. Future versions may support multi-locale text.
+ * Caveat: This method may not return the latest spell checker locale, but this should be
+ * acceptable and it's more important to make this method asynchronous.
+ *
+ * @return The locale that should be used for a spell checker in this TextView,
+ * based on the current spell checker settings, the current IME's locale, or the system default
+ * locale.
+ * @hide
+ */
+ public Locale getSpellCheckerLocale() {
+ return getTextServicesLocale(true /* allowNullLocale */);
}
private void updateTextServicesLocaleAsync() {
@@ -7867,14 +7883,16 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
private void updateTextServicesLocaleLocked() {
- Locale locale = Locale.getDefault();
final TextServicesManager textServicesManager = (TextServicesManager)
mContext.getSystemService(Context.TEXT_SERVICES_MANAGER_SERVICE);
final SpellCheckerSubtype subtype = textServicesManager.getCurrentSpellCheckerSubtype(true);
+ final Locale locale;
if (subtype != null) {
locale = SpellCheckerSubtype.constructLocaleFromString(subtype.getLocale());
+ } else {
+ locale = null;
}
- mCurrentTextServicesLocaleCache = locale;
+ mCurrentSpellCheckerLocaleCache = locale;
}
void onLocaleChanged() {
diff --git a/core/java/android/widget/ValueEditor.java b/core/java/android/widget/ValueEditor.java
new file mode 100755
index 0000000..2b91abf
--- /dev/null
+++ b/core/java/android/widget/ValueEditor.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2012 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.widget;
+
+import android.util.ValueModel;
+
+/**
+ * An interface for editors of simple values. Classes implementing this interface are normally
+ * UI controls (subclasses of {@link android.view.View View}) that can provide a suitable
+ * user interface to display and edit values of the specified type. This interface is
+ * intended to describe editors for simple types, like {@code boolean}, {@code int} or
+ * {@code String}, where the values themselves are immutable.
+ * <p>
+ * For example, {@link android.widget.CheckBox CheckBox} implements
+ * this interface for the Boolean type as it is capable of providing an appropriate
+ * mechanism for displaying and changing the value of a Boolean property.
+ *
+ * @param <T> the value type that this editor supports
+ */
+public interface ValueEditor<T> {
+ /**
+ * Return the last value model that was set. If no value model has been set, the editor
+ * should return the value {@link android.util.ValueModel#EMPTY}.
+ *
+ * @return the value model
+ */
+ public ValueModel<T> getValueModel();
+
+ /**
+ * Sets the value model for this editor. When the value model is set, the editor should
+ * retrieve the value from the value model, using {@link android.util.ValueModel#get()},
+ * and set its internal state accordingly. Likewise, when the editor's internal state changes
+ * it should update the value model by calling {@link android.util.ValueModel#set(T)}
+ * with the appropriate value.
+ *
+ * @param valueModel the new value model for this editor.
+ */
+ public void setValueModel(ValueModel<T> valueModel);
+}
diff --git a/core/java/android/widget/VideoView.java b/core/java/android/widget/VideoView.java
index 329b0df..16b6a76 100644
--- a/core/java/android/widget/VideoView.java
+++ b/core/java/android/widget/VideoView.java
@@ -35,6 +35,7 @@ import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
+import android.view.View.MeasureSpec;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.MediaController.MediaPlayerControl;
@@ -107,23 +108,65 @@ public class VideoView extends SurfaceView implements MediaPlayerControl {
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- //Log.i("@@@@", "onMeasure");
+ //Log.i("@@@@", "onMeasure(" + MeasureSpec.toString(widthMeasureSpec) + ", "
+ // + MeasureSpec.toString(heightMeasureSpec) + ")");
+
int width = getDefaultSize(mVideoWidth, widthMeasureSpec);
int height = getDefaultSize(mVideoHeight, heightMeasureSpec);
if (mVideoWidth > 0 && mVideoHeight > 0) {
- if ( mVideoWidth * height > width * mVideoHeight ) {
- //Log.i("@@@", "image too tall, correcting");
+
+ int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
+ int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
+ int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
+ int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
+
+ if (widthSpecMode == MeasureSpec.EXACTLY && heightSpecMode == MeasureSpec.EXACTLY) {
+ // the size is fixed
+ width = widthSpecSize;
+ height = heightSpecSize;
+
+ // for compatibility, we adjust size based on aspect ratio
+ if ( mVideoWidth * height < width * mVideoHeight ) {
+ //Log.i("@@@", "image too wide, correcting");
+ width = height * mVideoWidth / mVideoHeight;
+ } else if ( mVideoWidth * height > width * mVideoHeight ) {
+ //Log.i("@@@", "image too tall, correcting");
+ height = width * mVideoHeight / mVideoWidth;
+ }
+ } else if (widthSpecMode == MeasureSpec.EXACTLY) {
+ // only the width is fixed, adjust the height to match aspect ratio if possible
+ width = widthSpecSize;
height = width * mVideoHeight / mVideoWidth;
- } else if ( mVideoWidth * height < width * mVideoHeight ) {
- //Log.i("@@@", "image too wide, correcting");
+ if (heightSpecMode == MeasureSpec.AT_MOST && height > heightSpecSize) {
+ // couldn't match aspect ratio within the constraints
+ height = heightSpecSize;
+ }
+ } else if (heightSpecMode == MeasureSpec.EXACTLY) {
+ // only the height is fixed, adjust the width to match aspect ratio if possible
+ height = heightSpecSize;
width = height * mVideoWidth / mVideoHeight;
+ if (widthSpecMode == MeasureSpec.AT_MOST && width > widthSpecSize) {
+ // couldn't match aspect ratio within the constraints
+ width = widthSpecSize;
+ }
} else {
- //Log.i("@@@", "aspect ratio is correct: " +
- //width+"/"+height+"="+
- //mVideoWidth+"/"+mVideoHeight);
+ // neither the width nor the height are fixed, try to use actual video size
+ width = mVideoWidth;
+ height = mVideoHeight;
+ if (heightSpecMode == MeasureSpec.AT_MOST && height > heightSpecSize) {
+ // too tall, decrease both width and height
+ height = heightSpecSize;
+ width = height * mVideoWidth / mVideoHeight;
+ }
+ if (widthSpecMode == MeasureSpec.AT_MOST && width > widthSpecSize) {
+ // too wide, decrease both width and height
+ width = widthSpecSize;
+ height = width * mVideoHeight / mVideoWidth;
+ }
}
+ } else {
+ // no size yet, just adopt the given spec sizes
}
- //Log.i("@@@@@@@@@@", "setting size: " + width + 'x' + height);
setMeasuredDimension(width, height);
}
@@ -140,33 +183,8 @@ public class VideoView extends SurfaceView implements MediaPlayerControl {
}
public int resolveAdjustedSize(int desiredSize, int measureSpec) {
- int result = desiredSize;
- int specMode = MeasureSpec.getMode(measureSpec);
- int specSize = MeasureSpec.getSize(measureSpec);
-
- switch (specMode) {
- case MeasureSpec.UNSPECIFIED:
- /* Parent says we can be as big as we want. Just don't be larger
- * than max size imposed on ourselves.
- */
- result = desiredSize;
- break;
-
- case MeasureSpec.AT_MOST:
- /* Parent says we can be as big as we want, up to specSize.
- * Don't be larger than specSize, and don't be larger than
- * the max size imposed on ourselves.
- */
- result = Math.min(desiredSize, specSize);
- break;
-
- case MeasureSpec.EXACTLY:
- // No choice. Do what we are told.
- result = specSize;
- break;
- }
- return result;
-}
+ return getDefaultSize(desiredSize, measureSpec);
+ }
private void initVideoView() {
mVideoWidth = 0;