diff options
Diffstat (limited to 'core/java/android')
50 files changed, 1340 insertions, 434 deletions
diff --git a/core/java/android/animation/AnimatorSet.java b/core/java/android/animation/AnimatorSet.java index c5a4171..f9fa444 100644 --- a/core/java/android/animation/AnimatorSet.java +++ b/core/java/android/animation/AnimatorSet.java @@ -420,11 +420,7 @@ public final class AnimatorSet extends Animator { if (duration < 0) { throw new IllegalArgumentException("duration must be a value of zero or greater"); } - for (Node node : mNodes) { - // TODO: don't set the duration of the timing-only nodes created by AnimatorSet to - // insert "play-after" delays - node.animation.setDuration(duration); - } + // Just record the value for now - it will be used later when the AnimatorSet starts mDuration = duration; return this; } @@ -456,6 +452,14 @@ public final class AnimatorSet extends Animator { mTerminated = false; mStarted = true; + if (mDuration >= 0) { + // If the duration was set on this AnimatorSet, pass it along to all child animations + for (Node node : mNodes) { + // TODO: don't set the duration of the timing-only nodes created by AnimatorSet to + // insert "play-after" delays + node.animation.setDuration(mDuration); + } + } // First, sort the nodes (if necessary). This will ensure that sortedNodes // contains the animation nodes in the correct order. sortNodes(); diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 227900e..1c820dc 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -4656,7 +4656,7 @@ public class Activity extends ContextThemeWrapper /** * Print the Activity's state into the given stream. This gets invoked if - * you run "adb shell dumpsys activity <activity_component_name>". + * you run "adb shell dumpsys activity <activity_component_name>". * * @param prefix Desired prefix to prepend at each line of output. * @param fd The raw file descriptor that the dump is being sent to. diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index a3fdf3e..7e1589f 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -1000,7 +1000,7 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM } return true; } - + case GOING_TO_SLEEP_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); goingToSleep(); @@ -1015,6 +1015,13 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM return true; } + case SET_LOCK_SCREEN_SHOWN_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + setLockScreenShown(data.readInt() != 0); + reply.writeNoException(); + return true; + } + case SET_DEBUG_APP_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); String pn = data.readString(); @@ -2912,6 +2919,17 @@ class ActivityManagerProxy implements IActivityManager data.recycle(); reply.recycle(); } + public void setLockScreenShown(boolean shown) throws RemoteException + { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + data.writeInt(shown ? 1 : 0); + mRemote.transact(SET_LOCK_SCREEN_SHOWN_TRANSACTION, data, reply, 0); + reply.readException(); + data.recycle(); + reply.recycle(); + } public void setDebugApp( String packageName, boolean waitForDebugger, boolean persistent) throws RemoteException diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java index c493f0f..d3ba497 100644 --- a/core/java/android/app/Fragment.java +++ b/core/java/android/app/Fragment.java @@ -203,7 +203,7 @@ final class FragmentState implements Parcelable { * <li> {@link #onCreateView} creates and returns the view hierarchy associated * with the fragment. * <li> {@link #onActivityCreated} tells the fragment that its activity has - * completed its own {@link Activity#onCreate Activity.onCreaate}. + * completed its own {@link Activity#onCreate Activity.onCreate()}. * <li> {@link #onStart} makes the fragment visible to the user (based on its * containing activity being started). * <li> {@link #onResume} makes the fragment interacting with the user (based on its diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index c71b186..3fc2280 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -205,7 +205,8 @@ public interface IActivityManager extends IInterface { // Note: probably don't want to allow applications access to these. public void goingToSleep() throws RemoteException; public void wakingUp() throws RemoteException; - + public void setLockScreenShown(boolean shown) throws RemoteException; + public void unhandledBack() throws RemoteException; public ParcelFileDescriptor openContentUri(Uri uri) throws RemoteException; public void setDebugApp( @@ -588,4 +589,5 @@ public interface IActivityManager extends IInterface { int GET_CURRENT_USER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+144; int TARGET_TASK_AFFINITY_MATCHES_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+145; int NAVIGATE_UP_TO_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+146; + int SET_LOCK_SCREEN_SHOWN_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+147; } diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 04c64a0..5cce25f 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -196,10 +196,9 @@ public class Notification implements Parcelable public RemoteViews intruderView; /** - * A larger version of {@link #contentView}, giving the Notification an + * A large-format version of {@link #contentView}, giving the Notification an * opportunity to show more detail. The system UI may choose to show this * instead of the normal content view at its discretion. - * @hide */ public RemoteViews bigContentView; @@ -987,8 +986,6 @@ public class Notification implements Parcelable } /** - * @hide - * * Show the {@link Notification#when} field as a countdown (or count-up) timer instead of a timestamp. * * @see Notification#when @@ -1609,23 +1606,21 @@ public class Notification implements Parcelable } /** - * @hide because this API is still very rough + * Helper class for generating large-format notifications that include a large image attachment. * - * This is a "rebuilder": It consumes a Builder object and modifies its output. - * - * This represents the "big picture" style notification, with a large Bitmap atop the usual notification. - * - * Usage: + * This class is a "rebuilder": It consumes a Builder object and modifies its behavior, like so: * <pre class="prettyprint"> * Notification noti = new Notification.BigPictureStyle( * new Notification.Builder() - * .setContentTitle("New mail from " + sender.toString()) + * .setContentTitle("New photo from " + sender.toString()) * .setContentText(subject) - * .setSmallIcon(R.drawable.new_mail) + * .setSmallIcon(R.drawable.new_post) * .setLargeIcon(aBitmap)) * .bigPicture(aBigBitmap) * .build(); * </pre> + * + * @see Notification#bigContentView */ public static class BigPictureStyle { private Builder mBuilder; @@ -1656,13 +1651,9 @@ public class Notification implements Parcelable } /** - * @hide because this API is still very rough - * - * This is a "rebuilder": It consumes a Builder object and modifies its output. + * Helper class for generating large-format notifications that include a lot of text. * - * This represents the "big text" style notification, with more area for the main content text to be read in its entirety. - * - * Usage: + * This class is a "rebuilder": It consumes a Builder object and modifies its behavior, like so: * <pre class="prettyprint"> * Notification noti = new Notification.BigPictureStyle( * new Notification.Builder() @@ -1673,6 +1664,8 @@ public class Notification implements Parcelable * .bigText(aVeryLongString) * .build(); * </pre> + * + * @see Notification#bigContentView */ public static class BigTextStyle { private Builder mBuilder; diff --git a/core/java/android/app/Service.java b/core/java/android/app/Service.java index 207ae76..cb43d4c 100644 --- a/core/java/android/app/Service.java +++ b/core/java/android/app/Service.java @@ -666,8 +666,8 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac /** * Print the Service's state into the given stream. This gets invoked if - * you run "adb shell dumpsys activity service <yourservicename>". - * This is distinct from "dumpsys <servicename>", which only works for + * you run "adb shell dumpsys activity service <yourservicename>". + * This is distinct from "dumpsys <servicename>", which only works for * named system services and which invokes the {@link IBinder#dump} method * on the {@link IBinder} interface registered with ServiceManager. * diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java index 05ef194..1206056 100644 --- a/core/java/android/content/ContentProvider.java +++ b/core/java/android/content/ContentProvider.java @@ -1127,7 +1127,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 { /** * Print the Provider's state into the given stream. This gets invoked if - * you run "adb shell dumpsys activity provider <provider_component_name>". + * you run "adb shell dumpsys activity provider <provider_component_name>". * * @param prefix Desired prefix to prepend at each line of output. * @param fd The raw file descriptor that the dump is being sent to. diff --git a/core/java/android/content/SyncManager.java b/core/java/android/content/SyncManager.java index 6219de7..34c40a0 100644 --- a/core/java/android/content/SyncManager.java +++ b/core/java/android/content/SyncManager.java @@ -205,6 +205,9 @@ public class SyncManager implements OnAccountsUpdateListener { private final PowerManager mPowerManager; + // Use this as a random offset to seed all periodic syncs + private int mSyncRandomOffsetMillis; + private static final long SYNC_ALARM_TIMEOUT_MIN = 30 * 1000; // 30 seconds private static final long SYNC_ALARM_TIMEOUT_MAX = 2 * 60 * 60 * 1000; // two hours @@ -438,6 +441,9 @@ public class SyncManager implements OnAccountsUpdateListener { // do this synchronously to ensure we have the accounts before this call returns onAccountsUpdated(null); } + + // Pick a random second in a day to seed all periodic syncs + mSyncRandomOffsetMillis = mSyncStorageEngine.getSyncRandomOffset() * 1000; } /** @@ -666,6 +672,7 @@ public class SyncManager implements OnAccountsUpdateListener { private void sendCheckAlarmsMessage() { if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "sending MESSAGE_CHECK_ALARMS"); + mSyncHandler.removeMessages(SyncHandler.MESSAGE_CHECK_ALARMS); mSyncHandler.sendEmptyMessage(SyncHandler.MESSAGE_CHECK_ALARMS); } @@ -714,6 +721,8 @@ public class SyncManager implements OnAccountsUpdateListener { } private void increaseBackoffSetting(SyncOperation op) { + // TODO: Use this function to align it to an already scheduled sync + // operation in the specified window final long now = SystemClock.elapsedRealtime(); final Pair<Long, Long> previousSettings = @@ -1060,6 +1069,8 @@ public class SyncManager implements OnAccountsUpdateListener { final long now = SystemClock.elapsedRealtime(); pw.print("now: "); pw.print(now); pw.println(" (" + formatTime(System.currentTimeMillis()) + ")"); + pw.print("offset: "); pw.print(DateUtils.formatElapsedTime(mSyncRandomOffsetMillis/1000)); + pw.println(" (HH:MM:SS)"); pw.print("uptime: "); pw.print(DateUtils.formatElapsedTime(now/1000)); pw.println(" (HH:MM:SS)"); pw.print("time spent syncing: "); @@ -1771,6 +1782,9 @@ public class SyncManager implements OnAccountsUpdateListener { AccountAndUser[] accounts = mAccounts; final long nowAbsolute = System.currentTimeMillis(); + final long shiftedNowAbsolute = (0 < nowAbsolute - mSyncRandomOffsetMillis) + ? (nowAbsolute - mSyncRandomOffsetMillis) : 0; + ArrayList<SyncStorageEngine.AuthorityInfo> infos = mSyncStorageEngine.getAuthorities(); for (SyncStorageEngine.AuthorityInfo info : infos) { // skip the sync if the account of this operation no longer exists @@ -1792,16 +1806,32 @@ public class SyncManager implements OnAccountsUpdateListener { SyncStatusInfo status = mSyncStorageEngine.getOrCreateSyncStatus(info); for (int i = 0, N = info.periodicSyncs.size(); i < N; i++) { final Bundle extras = info.periodicSyncs.get(i).first; - final Long periodInSeconds = info.periodicSyncs.get(i).second; + final Long periodInMillis = info.periodicSyncs.get(i).second * 1000; // find when this periodic sync was last scheduled to run final long lastPollTimeAbsolute = status.getPeriodicSyncTime(i); - // compute when this periodic sync should next run - this can be in the future - // for example if the user changed the time, synced and changed back. - final long nextPollTimeAbsolute = lastPollTimeAbsolute > nowAbsolute - ? nowAbsolute - : lastPollTimeAbsolute + periodInSeconds * 1000; - // if it is ready to run then schedule it and mark it as having been scheduled - if (nextPollTimeAbsolute <= nowAbsolute) { + + long remainingMillis + = periodInMillis - (shiftedNowAbsolute % periodInMillis); + + /* + * Sync scheduling strategy: + * Set the next periodic sync based on a random offset (in seconds). + * + * Also sync right now if any of the following cases hold + * and mark it as having been scheduled + * + * Case 1: This sync is ready to run now. + * Case 2: If the lastPollTimeAbsolute is in the future, + * sync now and reinitialize. This can happen for + * example if the user changed the time, synced and + * changed back. + * Case 3: If we failed to sync at the last scheduled time + */ + if (remainingMillis == periodInMillis // Case 1 + || lastPollTimeAbsolute > nowAbsolute // Case 2 + || (nowAbsolute - lastPollTimeAbsolute + >= periodInMillis)) { // Case 3 + // Sync now final Pair<Long, Long> backoff = mSyncStorageEngine.getBackoff( info.account, info.userId, info.authority); final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo = @@ -1819,12 +1849,13 @@ public class SyncManager implements OnAccountsUpdateListener { info.account, info.userId, info.authority), syncAdapterInfo.type.allowParallelSyncs())); status.setPeriodicSyncTime(i, nowAbsolute); - } else { - // it isn't ready to run, remember this time if it is earlier than - // earliestFuturePollTime - if (nextPollTimeAbsolute < earliestFuturePollTime) { - earliestFuturePollTime = nextPollTimeAbsolute; - } + } + // Compute when this periodic sync should next run + final long nextPollTimeAbsolute = nowAbsolute + remainingMillis; + + // remember this time if it is earlier than earliestFuturePollTime + if (nextPollTimeAbsolute < earliestFuturePollTime) { + earliestFuturePollTime = nextPollTimeAbsolute; } } } diff --git a/core/java/android/content/SyncStorageEngine.java b/core/java/android/content/SyncStorageEngine.java index d3baf70..d821918 100644 --- a/core/java/android/content/SyncStorageEngine.java +++ b/core/java/android/content/SyncStorageEngine.java @@ -37,6 +37,7 @@ import android.os.Message; import android.os.Parcel; import android.os.RemoteCallbackList; import android.os.RemoteException; +import android.os.SystemClock; import android.util.Log; import android.util.SparseArray; import android.util.Xml; @@ -49,6 +50,7 @@ import java.util.ArrayList; import java.util.Calendar; import java.util.HashMap; import java.util.Iterator; +import java.util.Random; import java.util.TimeZone; import java.util.List; @@ -65,6 +67,7 @@ public class SyncStorageEngine extends Handler { private static final String XML_ATTR_NEXT_AUTHORITY_ID = "nextAuthorityId"; private static final String XML_ATTR_LISTEN_FOR_TICKLES = "listen-for-tickles"; + private static final String XML_ATTR_SYNC_RANDOM_OFFSET = "offsetInSeconds"; private static final String XML_ATTR_ENABLED = "enabled"; private static final String XML_ATTR_USER = "user"; private static final String XML_TAG_LISTEN_FOR_TICKLES = "listenForTickles"; @@ -277,6 +280,8 @@ public class SyncStorageEngine extends Handler { private static volatile SyncStorageEngine sSyncStorageEngine = null; + private int mSyncRandomOffset; + /** * This file contains the core engine state: all accounts and the * settings for them. It must never be lost, and should be changed @@ -375,6 +380,10 @@ public class SyncStorageEngine extends Handler { } } + public int getSyncRandomOffset() { + return mSyncRandomOffset; + } + public void addStatusChangeListener(int mask, ISyncStatusObserver callback) { synchronized (mAuthorities) { mChangeListeners.register(callback, mask); @@ -1465,6 +1474,16 @@ public class SyncStorageEngine extends Handler { } catch (NumberFormatException e) { // don't care } + String offsetString = parser.getAttributeValue(null, XML_ATTR_SYNC_RANDOM_OFFSET); + try { + mSyncRandomOffset = (offsetString == null) ? 0 : Integer.parseInt(offsetString); + } catch (NumberFormatException e) { + mSyncRandomOffset = 0; + } + if (mSyncRandomOffset == 0) { + Random random = new Random(System.currentTimeMillis()); + mSyncRandomOffset = random.nextInt(86400); + } mMasterSyncAutomatically.put(0, listen == null || Boolean.parseBoolean(listen)); eventType = parser.next(); AuthorityInfo authority = null; @@ -1705,6 +1724,7 @@ public class SyncStorageEngine extends Handler { out.startTag(null, "accounts"); out.attribute(null, "version", Integer.toString(ACCOUNTS_VERSION)); out.attribute(null, XML_ATTR_NEXT_AUTHORITY_ID, Integer.toString(mNextAuthorityId)); + out.attribute(null, XML_ATTR_SYNC_RANDOM_OFFSET, Integer.toString(mSyncRandomOffset)); // Write the Sync Automatically flags for each user final int M = mMasterSyncAutomatically.size(); diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index 56fd5f8..9b8454a 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -373,6 +373,6 @@ interface IPackageManager { List<UserInfo> getUsers(); UserInfo getUser(int userId); - void setPermissionEnforcement(String permission, int enforcement); - int getPermissionEnforcement(String permission); + void setPermissionEnforced(String permission, boolean enforced); + boolean isPermissionEnforced(String permission); } diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index b06b4a5..675f77e 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -28,6 +28,7 @@ import android.content.res.Resources; import android.content.res.XmlResourceParser; import android.graphics.drawable.Drawable; import android.net.Uri; +import android.os.Build; import android.os.Environment; import android.util.AndroidException; import android.util.DisplayMetrics; @@ -1091,9 +1092,7 @@ public abstract class PackageManager { = "android.content.pm.extra.VERIFICATION_INSTALL_FLAGS"; /** {@hide} */ - public static final int ENFORCEMENT_DEFAULT = 0; - /** {@hide} */ - public static final int ENFORCEMENT_YES = 1; + public static final boolean DEFAULT_ENFORCE_READ_EXTERNAL_STORAGE = !"user".equals(Build.TYPE); /** * Retrieve overall information about an application package that is diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index 2af58be..c682852 100755 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -32,7 +32,6 @@ import android.util.AttributeSet; import android.util.DisplayMetrics; import android.util.Log; import android.util.Slog; -import android.util.SparseArray; import android.util.TypedValue; import android.util.LongSparseArray; @@ -86,8 +85,8 @@ public class Resources { // single-threaded, and after that these are immutable. private static final LongSparseArray<Drawable.ConstantState> sPreloadedDrawables = new LongSparseArray<Drawable.ConstantState>(); - private static final SparseArray<ColorStateList> mPreloadedColorStateLists - = new SparseArray<ColorStateList>(); + private static final LongSparseArray<ColorStateList> sPreloadedColorStateLists + = new LongSparseArray<ColorStateList>(); private static final LongSparseArray<Drawable.ConstantState> sPreloadedColorDrawables = new LongSparseArray<Drawable.ConstantState>(); private static boolean mPreloaded; @@ -98,8 +97,8 @@ public class Resources { // These are protected by the mTmpValue lock. private final LongSparseArray<WeakReference<Drawable.ConstantState> > mDrawableCache = new LongSparseArray<WeakReference<Drawable.ConstantState> >(); - private final SparseArray<WeakReference<ColorStateList> > mColorStateListCache - = new SparseArray<WeakReference<ColorStateList> >(); + private final LongSparseArray<WeakReference<ColorStateList> > mColorStateListCache + = new LongSparseArray<WeakReference<ColorStateList> >(); private final LongSparseArray<WeakReference<Drawable.ConstantState> > mColorDrawableCache = new LongSparseArray<WeakReference<Drawable.ConstantState> >(); private boolean mPreloading; @@ -118,22 +117,6 @@ public class Resources { private CompatibilityInfo mCompatibilityInfo; - private static final LongSparseArray<Object> EMPTY_ARRAY = new LongSparseArray<Object>(0) { - @Override - public void put(long k, Object o) { - throw new UnsupportedOperationException(); - } - @Override - public void append(long k, Object o) { - throw new UnsupportedOperationException(); - } - }; - - @SuppressWarnings("unchecked") - private static <T> LongSparseArray<T> emptySparseArray() { - return (LongSparseArray<T>) EMPTY_ARRAY; - } - /** @hide */ public static int selectDefaultTheme(int curTheme, int targetSdkVersion) { return selectSystemTheme(curTheme, targetSdkVersion, @@ -180,9 +163,8 @@ public class Resources { * @param config Desired device configuration to consider when * selecting/computing resource values (optional). */ - public Resources(AssetManager assets, DisplayMetrics metrics, - Configuration config) { - this(assets, metrics, config, (CompatibilityInfo) null); + public Resources(AssetManager assets, DisplayMetrics metrics, Configuration config) { + this(assets, metrics, config, null); } /** @@ -1883,7 +1865,8 @@ public class Resources { return dr; } - Drawable.ConstantState cs = isColorDrawable ? sPreloadedColorDrawables.get(key) : sPreloadedDrawables.get(key); + Drawable.ConstantState cs = isColorDrawable ? + sPreloadedColorDrawables.get(key) : sPreloadedDrawables.get(key); if (cs != null) { dr = cs.newDrawable(this); } else { @@ -2005,21 +1988,21 @@ public class Resources { } } - final int key = (value.assetCookie << 24) | value.data; + final long key = (((long) value.assetCookie) << 32) | value.data; ColorStateList csl; if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT && value.type <= TypedValue.TYPE_LAST_COLOR_INT) { - csl = mPreloadedColorStateLists.get(key); + csl = sPreloadedColorStateLists.get(key); if (csl != null) { return csl; } csl = ColorStateList.valueOf(value.data); if (mPreloading) { - mPreloadedColorStateLists.put(key, csl); + sPreloadedColorStateLists.put(key, csl); } return csl; @@ -2030,7 +2013,7 @@ public class Resources { return csl; } - csl = mPreloadedColorStateLists.get(key); + csl = sPreloadedColorStateLists.get(key); if (csl != null) { return csl; } @@ -2063,14 +2046,13 @@ public class Resources { if (csl != null) { if (mPreloading) { - mPreloadedColorStateLists.put(key, csl); + sPreloadedColorStateLists.put(key, csl); } else { synchronized (mTmpValue) { //Log.i(TAG, "Saving cached color state list @ #" + // Integer.toHexString(key.intValue()) // + " in " + this + ": " + csl); - mColorStateListCache.put( - key, new WeakReference<ColorStateList>(csl)); + mColorStateListCache.put(key, new WeakReference<ColorStateList>(csl)); } } } @@ -2078,7 +2060,7 @@ public class Resources { return csl; } - private ColorStateList getCachedColorStateList(int key) { + private ColorStateList getCachedColorStateList(long key) { synchronized (mTmpValue) { WeakReference<ColorStateList> wr = mColorStateListCache.get(key); if (wr != null) { // we have the key @@ -2088,8 +2070,7 @@ public class Resources { // Integer.toHexString(((Integer)key).intValue()) // + " in " + this + ": " + entry); return entry; - } - else { // our entry has been purged + } else { // our entry has been purged mColorStateListCache.delete(key); } } diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java index 83b6986..640b47b 100644 --- a/core/java/android/hardware/Camera.java +++ b/core/java/android/hardware/Camera.java @@ -950,11 +950,10 @@ public class Camera { /** * Callback interface used to notify on auto focus start and stop. * - * <p>This is useful for continuous autofocus -- {@link Parameters#FOCUS_MODE_CONTINUOUS_VIDEO} - * and {@link Parameters#FOCUS_MODE_CONTINUOUS_PICTURE}. Applications can - * show autofocus animation.</p> - * - * @hide + * <p>This is only supported in continuous autofocus modes -- {@link + * Parameters#FOCUS_MODE_CONTINUOUS_VIDEO} and {@link + * Parameters#FOCUS_MODE_CONTINUOUS_PICTURE}. Applications can show + * autofocus animation based on this.</p> */ public interface AutoFocusMoveCallback { @@ -962,7 +961,7 @@ public class Camera { * Called when the camera auto focus starts or stops. * * @param start true if focus starts to move, false if focus stops to move - * @param camera the Camera service object + * @param camera the Camera service object */ void onAutoFocusMoving(boolean start, Camera camera); } @@ -971,7 +970,6 @@ public class Camera { * Sets camera auto-focus move callback. * * @param cb the callback to run - * @hide */ public void setAutoFocusMoveCallback(AutoFocusMoveCallback cb) { mAutoFocusMoveCallback = cb; diff --git a/core/java/android/hardware/input/IInputDevicesChangedListener.aidl b/core/java/android/hardware/input/IInputDevicesChangedListener.aidl new file mode 100644 index 0000000..5d8ada1 --- /dev/null +++ b/core/java/android/hardware/input/IInputDevicesChangedListener.aidl @@ -0,0 +1,29 @@ +/* + * 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.hardware.input; + +/** @hide */ +interface IInputDevicesChangedListener { + /* Called when input devices changed, such as a device being added, + * removed or changing configuration. + * + * The parameter is an array of pairs (deviceId, generation) indicating the current + * device id and generation of all input devices. The client can determine what + * has happened by comparing the result to its prior observations. + */ + oneway void onInputDevicesChanged(in int[] deviceIdAndGeneration); +} diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl index 47e0d1e..ca8321f 100644 --- a/core/java/android/hardware/input/IInputManager.aidl +++ b/core/java/android/hardware/input/IInputManager.aidl @@ -17,6 +17,7 @@ package android.hardware.input; import android.hardware.input.KeyboardLayout; +import android.hardware.input.IInputDevicesChangedListener; import android.view.InputDevice; import android.view.InputEvent; @@ -42,4 +43,7 @@ interface IInputManager { String getKeyboardLayoutForInputDevice(String inputDeviceDescriptor); void setKeyboardLayoutForInputDevice(String inputDeviceDescriptor, String keyboardLayoutDescriptor); + + // Registers an input devices changed listener. + void registerInputDevicesChangedListener(IInputDevicesChangedListener listener); } diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java index 3b3c237..35c49a1 100755 --- a/core/java/android/hardware/input/InputManager.java +++ b/core/java/android/hardware/input/InputManager.java @@ -19,7 +19,10 @@ package android.hardware.input; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.content.Context; +import android.os.Handler; import android.os.IBinder; +import android.os.Looper; +import android.os.Message; import android.os.RemoteException; import android.os.ServiceManager; import android.provider.Settings; @@ -29,6 +32,8 @@ import android.util.SparseArray; import android.view.InputDevice; import android.view.InputEvent; +import java.util.ArrayList; + /** * Provides information about input devices and available key layouts. * <p> @@ -40,11 +45,22 @@ import android.view.InputEvent; */ public final class InputManager { private static final String TAG = "InputManager"; + private static final boolean DEBUG = false; + + private static final int MSG_DEVICE_ADDED = 1; + private static final int MSG_DEVICE_REMOVED = 2; + private static final int MSG_DEVICE_CHANGED = 3; private static InputManager sInstance; private final IInputManager mIm; - private final SparseArray<InputDevice> mInputDevices = new SparseArray<InputDevice>(); + + // Guarded by mInputDevicesLock + private final Object mInputDevicesLock = new Object(); + private SparseArray<InputDevice> mInputDevices; + private InputDevicesChangedListener mInputDevicesChangedListener; + private final ArrayList<InputDeviceListenerDelegate> mInputDeviceListeners = + new ArrayList<InputDeviceListenerDelegate>(); /** * Broadcast Action: Query available keyboard layouts. @@ -169,6 +185,103 @@ public final class InputManager { } /** + * Gets information about the input device with the specified id. + * @param id The device id. + * @return The input device or null if not found. + */ + public InputDevice getInputDevice(int id) { + synchronized (mInputDevicesLock) { + populateInputDevicesLocked(); + + int index = mInputDevices.indexOfKey(id); + if (index < 0) { + return null; + } + + InputDevice inputDevice = mInputDevices.valueAt(index); + if (inputDevice == null) { + try { + inputDevice = mIm.getInputDevice(id); + } catch (RemoteException ex) { + throw new RuntimeException("Could not get input device information.", ex); + } + } + mInputDevices.setValueAt(index, inputDevice); + return inputDevice; + } + } + + /** + * Gets the ids of all input devices in the system. + * @return The input device ids. + */ + public int[] getInputDeviceIds() { + synchronized (mInputDevicesLock) { + populateInputDevicesLocked(); + + final int count = mInputDevices.size(); + final int[] ids = new int[count]; + for (int i = 0; i < count; i++) { + ids[i] = mInputDevices.keyAt(i); + } + return ids; + } + } + + /** + * Registers an input device listener to receive notifications about when + * input devices are added, removed or changed. + * + * @param listener The listener to register. + * @param handler The handler on which the listener should be invoked, or null + * if the listener should be invoked on the calling thread's looper. + * + * @see #unregisterInputDeviceListener + */ + public void registerInputDeviceListener(InputDeviceListener listener, Handler handler) { + if (listener == null) { + throw new IllegalArgumentException("listener must not be null"); + } + + synchronized (mInputDevicesLock) { + int index = findInputDeviceListenerLocked(listener); + if (index < 0) { + mInputDeviceListeners.add(new InputDeviceListenerDelegate(listener, handler)); + } + } + } + + /** + * Unregisters an input device listener. + * + * @param listener The listener to unregister. + * + * @see #registerInputDeviceListener + */ + public void unregisterInputDeviceListener(InputDeviceListener listener) { + if (listener == null) { + throw new IllegalArgumentException("listener must not be null"); + } + + synchronized (mInputDevicesLock) { + int index = findInputDeviceListenerLocked(listener); + if (index >= 0) { + mInputDeviceListeners.remove(index); + } + } + } + + private int findInputDeviceListenerLocked(InputDeviceListener listener) { + final int numListeners = mInputDeviceListeners.size(); + for (int i = 0; i < numListeners; i++) { + if (mInputDeviceListeners.get(i).mListener == listener) { + return i; + } + } + return -1; + } + + /** * Gets information about all supported keyboard layouts. * <p> * The input manager consults the built-in keyboard layouts as well @@ -327,50 +440,6 @@ public final class InputManager { } /** - * Gets information about the input device with the specified id. - * @param id The device id. - * @return The input device or null if not found. - * - * @hide - */ - public InputDevice getInputDevice(int id) { - synchronized (mInputDevices) { - InputDevice inputDevice = mInputDevices.get(id); - if (inputDevice != null) { - return inputDevice; - } - } - final InputDevice newInputDevice; - try { - newInputDevice = mIm.getInputDevice(id); - } catch (RemoteException ex) { - throw new RuntimeException("Could not get input device information.", ex); - } - synchronized (mInputDevices) { - InputDevice inputDevice = mInputDevices.get(id); - if (inputDevice != null) { - return inputDevice; - } - mInputDevices.put(id, newInputDevice); - return newInputDevice; - } - } - - /** - * Gets the ids of all input devices in the system. - * @return The input device ids. - * - * @hide - */ - public int[] getInputDeviceIds() { - try { - return mIm.getInputDeviceIds(); - } catch (RemoteException ex) { - throw new RuntimeException("Could not get input device ids.", ex); - } - } - - /** * Queries the framework about whether any physical keys exist on the * any keyboard attached to the device that are capable of producing the given * array of key codes. @@ -429,4 +498,151 @@ public final class InputManager { return false; } } + + private void populateInputDevicesLocked() { + if (mInputDevicesChangedListener == null) { + final InputDevicesChangedListener listener = new InputDevicesChangedListener(); + try { + mIm.registerInputDevicesChangedListener(listener); + } catch (RemoteException ex) { + throw new RuntimeException( + "Could not get register input device changed listener", ex); + } + mInputDevicesChangedListener = listener; + } + + if (mInputDevices == null) { + final int[] ids; + try { + ids = mIm.getInputDeviceIds(); + } catch (RemoteException ex) { + throw new RuntimeException("Could not get input device ids.", ex); + } + + mInputDevices = new SparseArray<InputDevice>(); + for (int i = 0; i < ids.length; i++) { + mInputDevices.put(ids[i], null); + } + } + } + + private void onInputDevicesChanged(int[] deviceIdAndGeneration) { + if (DEBUG) { + Log.d(TAG, "Received input devices changed."); + } + + synchronized (mInputDevicesLock) { + for (int i = mInputDevices.size(); --i > 0; ) { + final int deviceId = mInputDevices.keyAt(i); + if (!containsDeviceId(deviceIdAndGeneration, deviceId)) { + if (DEBUG) { + Log.d(TAG, "Device removed: " + deviceId); + } + mInputDevices.removeAt(i); + sendMessageToInputDeviceListenersLocked(MSG_DEVICE_REMOVED, deviceId); + } + } + + for (int i = 0; i < deviceIdAndGeneration.length; i += 2) { + final int deviceId = deviceIdAndGeneration[i]; + int index = mInputDevices.indexOfKey(deviceId); + if (index >= 0) { + final InputDevice device = mInputDevices.valueAt(index); + if (device != null) { + final int generation = deviceIdAndGeneration[i + 1]; + if (device.getGeneration() != generation) { + if (DEBUG) { + Log.d(TAG, "Device changed: " + deviceId); + } + mInputDevices.setValueAt(index, null); + sendMessageToInputDeviceListenersLocked(MSG_DEVICE_CHANGED, deviceId); + } + } + } else { + if (DEBUG) { + Log.d(TAG, "Device added: " + deviceId); + } + mInputDevices.put(deviceId, null); + sendMessageToInputDeviceListenersLocked(MSG_DEVICE_ADDED, deviceId); + } + } + } + } + + private void sendMessageToInputDeviceListenersLocked(int what, int deviceId) { + final int numListeners = mInputDeviceListeners.size(); + for (int i = 0; i < numListeners; i++) { + InputDeviceListenerDelegate listener = mInputDeviceListeners.get(i); + listener.sendMessage(listener.obtainMessage(what, deviceId, 0)); + } + } + + private static boolean containsDeviceId(int[] deviceIdAndGeneration, int deviceId) { + for (int i = 0; i < deviceIdAndGeneration.length; i += 2) { + if (deviceIdAndGeneration[i] == deviceId) { + return true; + } + } + return false; + } + + /** + * Listens for changes in input devices. + */ + public interface InputDeviceListener { + /** + * Called whenever an input device has been added to the system. + * Use {@link InputManager#getInputDevice} to get more information about the device. + * + * @param deviceId The id of the input device that was added. + */ + void onInputDeviceAdded(int deviceId); + + /** + * Called whenever an input device has been removed from the system. + * + * @param deviceId The id of the input device that was removed. + */ + void onInputDeviceRemoved(int deviceId); + + /** + * Called whenever the properties of an input device have changed since they + * were last queried. Use {@link InputManager#getInputDevice} to get + * a fresh {@link InputDevice} object with the new properties. + * + * @param deviceId The id of the input device that changed. + */ + void onInputDeviceChanged(int deviceId); + } + + private final class InputDevicesChangedListener extends IInputDevicesChangedListener.Stub { + @Override + public void onInputDevicesChanged(int[] deviceIdAndGeneration) throws RemoteException { + InputManager.this.onInputDevicesChanged(deviceIdAndGeneration); + } + } + + private static final class InputDeviceListenerDelegate extends Handler { + public final InputDeviceListener mListener; + + public InputDeviceListenerDelegate(InputDeviceListener listener, Handler handler) { + super(handler != null ? handler.getLooper() : Looper.myLooper()); + mListener = listener; + } + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_DEVICE_ADDED: + mListener.onInputDeviceAdded(msg.arg1); + break; + case MSG_DEVICE_REMOVED: + mListener.onInputDeviceRemoved(msg.arg1); + break; + case MSG_DEVICE_CHANGED: + mListener.onInputDeviceChanged(msg.arg1); + break; + } + } + } } diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index ba7dc4a..332f40a 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -1712,8 +1712,8 @@ public class InputMethodService extends AbstractInputMethodService { /** * Override this to intercept key down events before they are processed by the - * application. If you return true, the application will not itself - * process the event. If you return true, the normal application processing + * application. If you return true, the application will not + * process the event itself. If you return false, the normal application processing * will occur as if the IME had not seen the event at all. * * <p>The default implementation intercepts {@link KeyEvent#KEYCODE_BACK diff --git a/core/java/android/net/nsd/DnsSdServiceInfo.java b/core/java/android/net/nsd/DnsSdServiceInfo.java index 47d6ec6..33c3eb9 100644 --- a/core/java/android/net/nsd/DnsSdServiceInfo.java +++ b/core/java/android/net/nsd/DnsSdServiceInfo.java @@ -19,6 +19,8 @@ package android.net.nsd; import android.os.Parcelable; import android.os.Parcel; +import java.net.InetAddress; + /** * Defines a service based on DNS service discovery * {@hide} @@ -27,20 +29,20 @@ public class DnsSdServiceInfo implements NetworkServiceInfo, Parcelable { private String mServiceName; - private String mRegistrationType; + private String mServiceType; private DnsSdTxtRecord mTxtRecord; - private String mHostname; + private InetAddress mHost; private int mPort; - DnsSdServiceInfo() { + public DnsSdServiceInfo() { } - DnsSdServiceInfo(String sn, String rt, DnsSdTxtRecord tr) { + public DnsSdServiceInfo(String sn, String rt, DnsSdTxtRecord tr) { mServiceName = sn; - mRegistrationType = rt; + mServiceType = rt; mTxtRecord = tr; } @@ -59,13 +61,13 @@ public class DnsSdServiceInfo implements NetworkServiceInfo, Parcelable { @Override /** @hide */ public String getServiceType() { - return mRegistrationType; + return mServiceType; } @Override /** @hide */ public void setServiceType(String s) { - mRegistrationType = s; + mServiceType = s; } public DnsSdTxtRecord getTxtRecord() { @@ -76,12 +78,12 @@ public class DnsSdServiceInfo implements NetworkServiceInfo, Parcelable { mTxtRecord = new DnsSdTxtRecord(t); } - public String getHostName() { - return mHostname; + public InetAddress getHost() { + return mHost; } - public void setHostName(String s) { - mHostname = s; + public void setHost(InetAddress s) { + mHost = s; } public int getPort() { @@ -96,7 +98,9 @@ public class DnsSdServiceInfo implements NetworkServiceInfo, Parcelable { StringBuffer sb = new StringBuffer(); sb.append("name: ").append(mServiceName). - append("type: ").append(mRegistrationType). + append("type: ").append(mServiceType). + append("host: ").append(mHost). + append("port: ").append(mPort). append("txtRecord: ").append(mTxtRecord); return sb.toString(); } @@ -109,9 +113,14 @@ public class DnsSdServiceInfo implements NetworkServiceInfo, Parcelable { /** Implement the Parcelable interface */ public void writeToParcel(Parcel dest, int flags) { dest.writeString(mServiceName); - dest.writeString(mRegistrationType); + dest.writeString(mServiceType); dest.writeParcelable(mTxtRecord, flags); - dest.writeString(mHostname); + if (mHost != null) { + dest.writeByte((byte)1); + dest.writeByteArray(mHost.getAddress()); + } else { + dest.writeByte((byte)0); + } dest.writeInt(mPort); } @@ -121,9 +130,15 @@ public class DnsSdServiceInfo implements NetworkServiceInfo, Parcelable { public DnsSdServiceInfo createFromParcel(Parcel in) { DnsSdServiceInfo info = new DnsSdServiceInfo(); info.mServiceName = in.readString(); - info.mRegistrationType = in.readString(); + info.mServiceType = in.readString(); info.mTxtRecord = in.readParcelable(null); - info.mHostname = in.readString(); + + if (in.readByte() == 1) { + try { + info.mHost = InetAddress.getByAddress(in.createByteArray()); + } catch (java.net.UnknownHostException e) {} + } + info.mPort = in.readInt(); return info; } diff --git a/core/java/android/net/nsd/DnsSdTxtRecord.java b/core/java/android/net/nsd/DnsSdTxtRecord.java index 6d4342c..952e02f 100644 --- a/core/java/android/net/nsd/DnsSdTxtRecord.java +++ b/core/java/android/net/nsd/DnsSdTxtRecord.java @@ -24,6 +24,8 @@ package android.net.nsd; import android.os.Parcelable; import android.os.Parcel; +import java.util.Arrays; + /** * This class handles TXT record data for DNS based service discovery as specified at * http://tools.ietf.org/html/draft-cheshire-dnsext-dns-sd-11 @@ -160,7 +162,7 @@ public class DnsSdTxtRecord implements Parcelable { /* Gets the raw data in bytes */ public byte[] getRawData() { - return mData; + return (byte[]) mData.clone(); } private void insert(byte[] keyBytes, byte[] value, int index) { @@ -279,6 +281,24 @@ public class DnsSdTxtRecord implements Parcelable { return result != null ? result : ""; } + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + if (!(o instanceof DnsSdTxtRecord)) { + return false; + } + + DnsSdTxtRecord record = (DnsSdTxtRecord)o; + return Arrays.equals(record.mData, mData); + } + + @Override + public int hashCode() { + return Arrays.hashCode(mData); + } + /** Implement the Parcelable interface */ public int describeContents() { return 0; diff --git a/core/java/android/net/nsd/NsdManager.java b/core/java/android/net/nsd/NsdManager.java index a109a98..505f11b 100644 --- a/core/java/android/net/nsd/NsdManager.java +++ b/core/java/android/net/nsd/NsdManager.java @@ -93,6 +93,15 @@ public class NsdManager { /** @hide */ public static final int RESOLVE_SERVICE_SUCCEEDED = BASE + 17; + /** @hide */ + public static final int STOP_RESOLVE = BASE + 18; + /** @hide */ + public static final int STOP_RESOLVE_FAILED = BASE + 19; + /** @hide */ + public static final int STOP_RESOLVE_SUCCEEDED = BASE + 20; + + + /** * Create a new Nsd instance. Applications use * {@link android.content.Context#getSystemService Context.getSystemService()} to retrieve @@ -117,10 +126,23 @@ public class NsdManager { /** * Indicates that the operation failed because the framework is busy and - * unable to service the request + * unable to service the request. */ public static final int BUSY = 2; + /** + * Indicates that the operation failed because it is already active. + */ + public static final int ALREADY_ACTIVE = 3; + + /** + * Indicates that the operation failed because maximum limit on + * service registrations has reached. + */ + public static final int MAX_REGS_REACHED = 4; + + + /** Interface for callback invocation when framework channel is connected or lost */ public interface ChannelListener { public void onChannelConnected(Channel c); @@ -188,6 +210,7 @@ public class NsdManager { private DnsSdRegisterListener mDnsSdRegisterListener; private DnsSdUpdateRegistrationListener mDnsSdUpdateListener; private DnsSdResolveListener mDnsSdResolveListener; + private ActionListener mDnsSdStopResolveListener; AsyncChannel mAsyncChannel; ServiceHandler mHandler; @@ -278,6 +301,16 @@ public class NsdManager { (DnsSdServiceInfo) message.obj); } break; + case STOP_RESOLVE_FAILED: + if (mDnsSdStopResolveListener!= null) { + mDnsSdStopResolveListener.onFailure(message.arg1); + } + break; + case STOP_RESOLVE_SUCCEEDED: + if (mDnsSdStopResolveListener != null) { + mDnsSdStopResolveListener.onSuccess(); + } + break; default: Log.d(TAG, "Ignored " + message); break; @@ -345,6 +378,14 @@ public class NsdManager { c.mDnsSdResolveListener = b; } + /** + * Set the listener for stopping service resolution. Can be null. + */ + public void setStopResolveListener(Channel c, ActionListener b) { + if (c == null) throw new IllegalArgumentException("Channel needs to be initialized"); + c.mDnsSdStopResolveListener = b; + } + public void registerService(Channel c, DnsSdServiceInfo serviceInfo) { if (c == null) throw new IllegalArgumentException("Channel needs to be initialized"); if (serviceInfo == null) throw new IllegalArgumentException("Null serviceInfo"); @@ -378,6 +419,13 @@ public class NsdManager { c.mAsyncChannel.sendMessage(RESOLVE_SERVICE, serviceInfo); } + public void stopServiceResolve(Channel c) { + if (c == null) throw new IllegalArgumentException("Channel needs to be initialized"); + if (c.mDnsSdResolveListener == null) throw new + IllegalStateException("Resolve listener needs to be set first"); + c.mAsyncChannel.sendMessage(STOP_RESOLVE); + } + /** * Get a reference to NetworkService handler. This is used to establish * an AsyncChannel communication with the service diff --git a/core/java/android/provider/BrowserContract.java b/core/java/android/provider/BrowserContract.java index d678205..118b5eb 100644 --- a/core/java/android/provider/BrowserContract.java +++ b/core/java/android/provider/BrowserContract.java @@ -30,6 +30,15 @@ import android.os.RemoteException; import android.util.Pair; /** + * <p> + * The contract between the browser provider and applications. Contains the definition + * for the supported URIS and columns. + * </p> + * <h3>Overview</h3> + * <p> + * BrowserContract defines an database of browser-related information which are bookmarks, + * history, images and the mapping between the image and URL. + * </p> * @hide */ public class BrowserContract { @@ -45,12 +54,14 @@ public class BrowserContract { * the dirty flag is not automatically set and the "syncToNetwork" parameter * is set to false when calling * {@link ContentResolver#notifyChange(android.net.Uri, android.database.ContentObserver, boolean)}. + * @hide */ public static final String CALLER_IS_SYNCADAPTER = "caller_is_syncadapter"; /** * A parameter for use when querying any table that allows specifying a limit on the number * of rows returned. + * @hide */ public static final String PARAM_LIMIT = "limit"; @@ -58,6 +69,8 @@ public class BrowserContract { * Generic columns for use by sync adapters. The specific functions of * these columns are private to the sync adapter. Other clients of the API * should not attempt to either read or write these columns. + * + * @hide */ interface BaseSyncColumns { /** Generic column for use by sync adapters. */ @@ -74,6 +87,7 @@ public class BrowserContract { /** * Convenience definitions for use in implementing chrome bookmarks sync in the Bookmarks table. + * @hide */ public static final class ChromeSyncColumns { private ChromeSyncColumns() {} @@ -93,6 +107,7 @@ public class BrowserContract { /** * Columns that appear when each row of a table belongs to a specific * account, including sync information that an account may need. + * @hide */ interface SyncColumns extends BaseSyncColumns { /** @@ -144,13 +159,14 @@ public class BrowserContract { public static final String _ID = "_id"; /** - * The URL of the bookmark. + * This column is valid when the row is a URL. The history table's URL + * can not be updated. * <P>Type: TEXT (URL)</P> */ public static final String URL = "url"; /** - * The user visible title of the bookmark. + * The user visible title. * <P>Type: TEXT</P> */ public static final String TITLE = "title"; @@ -159,10 +175,14 @@ public class BrowserContract { * The time that this row was created on its originating client (msecs * since the epoch). * <P>Type: INTEGER</P> + * @hide */ public static final String DATE_CREATED = "created"; } + /** + * @hide + */ interface ImageColumns { /** * The favicon of the bookmark, may be NULL. @@ -182,7 +202,6 @@ public class BrowserContract { * The touch icon for the web page, may be NULL. * Must decode via {@link BitmapFactory#decodeByteArray}. * <p>Type: BLOB (image)</p> - * @hide */ public static final String TOUCH_ICON = "touch_icon"; } @@ -200,9 +219,26 @@ public class BrowserContract { */ public static final String VISITS = "visits"; + /** + * @hide + */ public static final String USER_ENTERED = "user_entered"; } + interface ImageMappingColumns { + /** + * The ID of the image in Images. One image can map onto the multiple URLs. + * <P>Type: INTEGER (long)</P> + */ + public static final String IMAGE_ID = "image_id"; + + /** + * The URL. The URL can map onto the different type of images. + * <P>Type: TEXT (URL)</P> + */ + public static final String URL = "url"; + } + /** * The bookmarks table, which holds the user's browser bookmarks. */ @@ -218,24 +254,71 @@ public class BrowserContract { public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "bookmarks"); /** + * Used in {@link Bookmarks#TYPE} column and indicats the row is a bookmark. + */ + public static final int BOOKMARK_TYPE_BOOKMARK = 1; + + /** + * Used in {@link Bookmarks#TYPE} column and indicats the row is a folder. + */ + public static final int BOOKMARK_TYPE_FOLDER = 2; + + /** + * Used in {@link Bookmarks#TYPE} column and indicats the row is the bookmark bar folder. + */ + public static final int BOOKMARK_TYPE_BOOKMARK_BAR_FOLDER = 3; + + /** + * Used in {@link Bookmarks#TYPE} column and indicats the row is other folder and + */ + public static final int BOOKMARK_TYPE_OTHER_FOLDER = 4; + + /** + * Used in {@link Bookmarks#TYPE} column and indicats the row is other folder, . + */ + public static final int BOOKMARK_TYPE_MOBILE_FOLDER = 5; + + /** + * The type of the item. + * <P>Type: INTEGER</P> + * <p>Allowed values are:</p> + * <p> + * <ul> + * <li>{@link #BOOKMARK_TYPE_BOOKMARK}</li> + * <li>{@link #BOOKMARK_TYPE_FOLDER}</li> + * <li>{@link #BOOKMARK_TYPE_BOOKMARK_BAR_FOLDER}</li> + * <li>{@link #BOOKMARK_TYPE_OTHER_FOLDER}</li> + * <li>{@link #BOOKMARK_TYPE_MOBILE_FOLDER}</li> + * </ul> + * </p> + * <p> The TYPE_BOOKMARK_BAR_FOLDER, TYPE_OTHER_FOLDER and TYPE_MOBILE_FOLDER + * can not be updated or deleted.</p> + */ + public static final String TYPE = "type"; + + /** * The content:// style URI for the default folder + * @hide */ public static final Uri CONTENT_URI_DEFAULT_FOLDER = Uri.withAppendedPath(CONTENT_URI, "folder"); /** * Query parameter used to specify an account name + * @hide */ public static final String PARAM_ACCOUNT_NAME = "acct_name"; /** * Query parameter used to specify an account type + * @hide */ public static final String PARAM_ACCOUNT_TYPE = "acct_type"; /** * Builds a URI that points to a specific folder. * @param folderId the ID of the folder to point to + * @hide */ public static final Uri buildFolderUri(long folderId) { return ContentUris.withAppendedId(CONTENT_URI_DEFAULT_FOLDER, folderId); @@ -255,6 +338,7 @@ public class BrowserContract { * Query parameter to use if you want to see deleted bookmarks that are still * around on the device and haven't been synced yet. * @see #IS_DELETED + * @hide */ public static final String QUERY_PARAMETER_SHOW_DELETED = "show_deleted"; @@ -262,6 +346,7 @@ public class BrowserContract { * Flag indicating if an item is a folder or bookmark. Non-zero values indicate * a folder and zero indicates a bookmark. * <P>Type: INTEGER (boolean)</P> + * @hide */ public static final String IS_FOLDER = "folder"; @@ -274,6 +359,7 @@ public class BrowserContract { /** * The source ID for an item's parent. Read-only. * @see #PARENT + * @hide */ public static final String PARENT_SOURCE_ID = "parent_source"; @@ -281,6 +367,7 @@ public class BrowserContract { * The position of the bookmark in relation to it's siblings that share the same * {@link #PARENT}. May be negative. * <P>Type: INTEGER</P> + * @hide */ public static final String POSITION = "position"; @@ -288,6 +375,7 @@ public class BrowserContract { * The item that the bookmark should be inserted after. * May be negative. * <P>Type: INTEGER</P> + * @hide */ public static final String INSERT_AFTER = "insert_after"; @@ -296,6 +384,7 @@ public class BrowserContract { * May be negative. * <P>Type: INTEGER</P> * @see #INSERT_AFTER + * @hide */ public static final String INSERT_AFTER_SOURCE_ID = "insert_after_source"; @@ -305,12 +394,14 @@ public class BrowserContract { * to the URI when performing your query. * <p>Type: INTEGER (non-zero if the item has been deleted, zero if it hasn't) * @see #QUERY_PARAMETER_SHOW_DELETED + * @hide */ public static final String IS_DELETED = "deleted"; } /** * Read-only table that lists all the accounts that are used to provide bookmarks. + * @hide */ public static final class Accounts { /** @@ -410,6 +501,7 @@ public class BrowserContract { * A table provided for sync adapters to use for storing private sync state data. * * @see SyncStateContract + * @hide */ public static final class SyncState implements SyncStateContract.Columns { /** @@ -459,8 +551,18 @@ public class BrowserContract { } /** - * Stores images for URLs. Only support query() and update(). - * @hide + * <p> + * Stores images for URLs. + * </p> + * <p> + * The rows in this table can not be updated since there might have multiple URLs mapping onto + * the same image. If you want to update a URL's image, you need to add the new image in this + * table, then update the mapping onto the added image. + * </p> + * <p> + * Every image should be at least associated with one URL, otherwise it will be removed after a + * while. + * </p> */ public static final class Images implements ImageColumns { /** @@ -474,15 +576,93 @@ public class BrowserContract { public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "images"); /** + * The MIME type of {@link #CONTENT_URI} providing a directory of images. + */ + public static final String CONTENT_TYPE = "vnd.android.cursor.dir/images"; + + /** + * The MIME type of a {@link #CONTENT_URI} of a single image. + */ + public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/images"; + + /** + * Used in {@link Images#TYPE} column and indicats the row is a favicon. + */ + public static final int IMAGE_TYPE_FAVICON = 1; + + /** + * Used in {@link Images#TYPE} column and indicats the row is a precomposed touch icon. + */ + public static final int IMAGE_TYPE_PRECOMPOSED_TOUCH_ICON = 2; + + /** + * Used in {@link Images#TYPE} column and indicats the row is a touch icon. + */ + public static final int IMAGE_TYPE_TOUCH_ICON = 4; + + /** + * The type of item in the table. + * <P>Type: INTEGER</P> + * <p>Allowed values are:</p> + * <p> + * <ul> + * <li>{@link #IMAGE_TYPE_FAVICON}</li> + * <li>{@link #IMAGE_TYPE_PRECOMPOSED_TOUCH_ICON}</li> + * <li>{@link #IMAGE_TYPE_TOUCH_ICON}</li> + * </ul> + * </p> + */ + public static final String TYPE = "type"; + + /** + * The image data. + * <p>Type: BLOB (image)</p> + */ + public static final String DATA = "data"; + + /** * The URL the images came from. * <P>Type: TEXT (URL)</P> + * @hide */ public static final String URL = "url_key"; } /** + * <p> + * A table that stores the mappings between the image and the URL. + * </p> + * <p> + * Deleting or Updating a mapping might also deletes the mapped image if there is no other URL + * maps onto it. + * </p> + */ + public static final class ImageMappings implements ImageMappingColumns { + /** + * This utility class cannot be instantiated + */ + private ImageMappings() {} + + /** + * The content:// style URI for this table + */ + public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "image_mappings"); + + /** + * The MIME type of {@link #CONTENT_URI} providing a directory of image mappings. + */ + public static final String CONTENT_TYPE = "vnd.android.cursor.dir/image_mappings"; + + /** + * The MIME type of a {@link #CONTENT_URI} of a single image mapping. + */ + public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/image_mappings"; + } + + /** * A combined view of bookmarks and history. All bookmarks in all folders are included and * no folders are included. + * @hide */ public static final class Combined implements CommonColumns, HistoryColumns, ImageColumns { /** @@ -505,6 +685,7 @@ public class BrowserContract { /** * A table that stores settings specific to the browser. Only support query and insert. + * @hide */ public static final class Settings { /** diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java index 0e9306b..035d8c4 100644 --- a/core/java/android/provider/ContactsContract.java +++ b/core/java/android/provider/ContactsContract.java @@ -1536,7 +1536,11 @@ public final class ContactsContract { * * @param resolver the ContentResolver to use * @param contactId the person who was contacted + * + * @deprecated The class DataUsageStatUpdater of the Android support library should + * be used instead. */ + @Deprecated public static void markAsContacted(ContentResolver resolver, long contactId) { Uri uri = ContentUris.withAppendedId(CONTENT_URI, contactId); ContentValues values = new ContentValues(); @@ -7452,7 +7456,7 @@ public final class ContactsContract { /** * <p> * API allowing applications to send usage information for each {@link Data} row to the - * Contacts Provider. + * Contacts Provider. Applications can also clear all usage information. * </p> * <p> * With the feedback, Contacts Provider may return more contextually appropriate results for @@ -7497,6 +7501,12 @@ public final class ContactsContract { * boolean successful = resolver.update(uri, new ContentValues(), null, null) > 0; * </pre> * </p> + * <p> + * Applications can also clear all usage information with: + * <pre> + * boolean successful = resolver.delete(DataUsageFeedback.DELETE_USAGE_URI, null, null) > 0; + * </pre> + * </p> */ public static final class DataUsageFeedback { @@ -7508,6 +7518,14 @@ public final class ContactsContract { Uri.withAppendedPath(Data.CONTENT_URI, "usagefeedback"); /** + * The content:// style URI for deleting all usage information. + * Must be used with {@link ContentResolver#delete(Uri, String, String[])}. + * The {@code where} and {@code selectionArgs} parameters are ignored. + */ + public static final Uri DELETE_USAGE_URI = + Uri.withAppendedPath(Contacts.CONTENT_URI, "delete_usage"); + + /** * <p> * Name for query parameter specifying the type of data usage. * </p> diff --git a/core/java/android/provider/Downloads.java b/core/java/android/provider/Downloads.java index bd6170b..cd8d51f 100644 --- a/core/java/android/provider/Downloads.java +++ b/core/java/android/provider/Downloads.java @@ -704,6 +704,37 @@ public final class Downloads { */ public static final int STATUS_BLOCKED = 498; + /** {@hide} */ + public static String statusToString(int status) { + switch (status) { + case STATUS_PENDING: return "PENDING"; + case STATUS_RUNNING: return "RUNNING"; + case STATUS_PAUSED_BY_APP: return "PAUSED_BY_APP"; + case STATUS_WAITING_TO_RETRY: return "WAITING_TO_RETRY"; + case STATUS_WAITING_FOR_NETWORK: return "WAITING_FOR_NETWORK"; + case STATUS_QUEUED_FOR_WIFI: return "QUEUED_FOR_WIFI"; + case STATUS_INSUFFICIENT_SPACE_ERROR: return "INSUFFICIENT_SPACE_ERROR"; + case STATUS_DEVICE_NOT_FOUND_ERROR: return "DEVICE_NOT_FOUND_ERROR"; + case STATUS_SUCCESS: return "SUCCESS"; + case STATUS_BAD_REQUEST: return "BAD_REQUEST"; + case STATUS_NOT_ACCEPTABLE: return "NOT_ACCEPTABLE"; + case STATUS_LENGTH_REQUIRED: return "LENGTH_REQUIRED"; + case STATUS_PRECONDITION_FAILED: return "PRECONDITION_FAILED"; + case STATUS_FILE_ALREADY_EXISTS_ERROR: return "FILE_ALREADY_EXISTS_ERROR"; + case STATUS_CANNOT_RESUME: return "CANNOT_RESUME"; + case STATUS_CANCELED: return "CANCELED"; + case STATUS_UNKNOWN_ERROR: return "UNKNOWN_ERROR"; + case STATUS_FILE_ERROR: return "FILE_ERROR"; + case STATUS_UNHANDLED_REDIRECT: return "UNHANDLED_REDIRECT"; + case STATUS_UNHANDLED_HTTP_CODE: return "UNHANDLED_HTTP_CODE"; + case STATUS_HTTP_DATA_ERROR: return "HTTP_DATA_ERROR"; + case STATUS_HTTP_EXCEPTION: return "HTTP_EXCEPTION"; + case STATUS_TOO_MANY_REDIRECTS: return "TOO_MANY_REDIRECTS"; + case STATUS_BLOCKED: return "BLOCKED"; + default: return Integer.toString(status); + } + } + /** * This download is visible but only shows in the notifications * while it's in progress. diff --git a/core/java/android/text/SpannableStringBuilder.java b/core/java/android/text/SpannableStringBuilder.java index 6056c75..11c169e 100644 --- a/core/java/android/text/SpannableStringBuilder.java +++ b/core/java/android/text/SpannableStringBuilder.java @@ -75,7 +75,7 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable if (spans[i] instanceof NoCopySpan) { continue; } - + int st = sp.getSpanStart(spans[i]) - start; int en = sp.getSpanEnd(spans[i]) - start; int fl = sp.getSpanFlags(spans[i]); @@ -212,7 +212,7 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable if (mGapLength > 2 * length()) resizeFor(length()); - + return ret; // == this } @@ -220,7 +220,7 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable public void clear() { replace(0, length(), "", 0, 0); } - + // Documentation from interface public void clearSpans() { for (int i = mSpanCount - 1; i >= 0; i--) { @@ -257,45 +257,50 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable return append(String.valueOf(text)); } - private void change(int start, int end, CharSequence tb, int tbstart, int tbend) { - checkRange("replace", start, end); + private void change(int start, int end, CharSequence cs, int csStart, int csEnd) { + // Can be negative + final int nbNewChars = (csEnd - csStart) - (end - start); for (int i = mSpanCount - 1; i >= 0; i--) { - if ((mSpanFlags[i] & SPAN_PARAGRAPH) == SPAN_PARAGRAPH) { - int st = mSpanStarts[i]; - if (st > mGapStart) - st -= mGapLength; + int spanStart = mSpanStarts[i]; + if (spanStart > mGapStart) + spanStart -= mGapLength; - int en = mSpanEnds[i]; - if (en > mGapStart) - en -= mGapLength; + int spanEnd = mSpanEnds[i]; + if (spanEnd > mGapStart) + spanEnd -= mGapLength; - int ost = st; - int oen = en; + if ((mSpanFlags[i] & SPAN_PARAGRAPH) == SPAN_PARAGRAPH) { + int ost = spanStart; + int oen = spanEnd; int clen = length(); - if (st > start && st <= end) { - for (st = end; st < clen; st++) - if (st > end && charAt(st - 1) == '\n') + if (spanStart > start && spanStart <= end) { + for (spanStart = end; spanStart < clen; spanStart++) + if (spanStart > end && charAt(spanStart - 1) == '\n') break; } - if (en > start && en <= end) { - for (en = end; en < clen; en++) - if (en > end && charAt(en - 1) == '\n') + if (spanEnd > start && spanEnd <= end) { + for (spanEnd = end; spanEnd < clen; spanEnd++) + if (spanEnd > end && charAt(spanEnd - 1) == '\n') break; } - if (st != ost || en != oen) - setSpan(false, mSpans[i], st, en, mSpanFlags[i]); + if (spanStart != ost || spanEnd != oen) + setSpan(false, mSpans[i], spanStart, spanEnd, mSpanFlags[i]); } + + int flags = 0; + if (spanStart == start) flags |= SPAN_START_AT_START; + else if (spanStart == end + nbNewChars) flags |= SPAN_START_AT_END; + if (spanEnd == start) flags |= SPAN_END_AT_START; + else if (spanEnd == end + nbNewChars) flags |= SPAN_END_AT_END; + mSpanFlags[i] |= flags; } moveGapTo(end); - // Can be negative - final int nbNewChars = (tbend - tbstart) - (end - start); - if (nbNewChars >= mGapLength) { resizeFor(mText.length + nbNewChars - mGapLength); } @@ -306,7 +311,7 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable if (mGapLength < 1) new Exception("mGapLength < 1").printStackTrace(); - TextUtils.getChars(tb, tbstart, tbend, mText, start); + TextUtils.getChars(cs, csStart, csEnd, mText, start); if (end > start) { // no need for span fixup on pure insertion @@ -340,21 +345,23 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable } } - if (tb instanceof Spanned) { - Spanned sp = (Spanned) tb; - Object[] spans = sp.getSpans(tbstart, tbend, Object.class); + mSpanCountBeforeAdd = mSpanCount; + + if (cs instanceof Spanned) { + Spanned sp = (Spanned) cs; + Object[] spans = sp.getSpans(csStart, csEnd, Object.class); for (int i = 0; i < spans.length; i++) { int st = sp.getSpanStart(spans[i]); int en = sp.getSpanEnd(spans[i]); - if (st < tbstart) st = tbstart; - if (en > tbend) en = tbend; + if (st < csStart) st = csStart; + if (en > csEnd) en = csEnd; // Add span only if this object is not yet used as a span in this string - if (getSpanStart(spans[i]) < 0) { - setSpan(false, spans[i], st - tbstart + start, en - tbstart + start, - sp.getSpanFlags(spans[i])); + if (getSpanStart(spans[i]) < 0 && !(spans[i] instanceof SpanWatcher)) { + setSpan(false, spans[i], st - csStart + start, en - csStart + start, + sp.getSpanFlags(spans[i])); } } } @@ -390,6 +397,8 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable // Documentation from interface public SpannableStringBuilder replace(final int start, final int end, CharSequence tb, int tbstart, int tbend) { + checkRange("replace", start, end); + int filtercount = mFilters.length; for (int i = 0; i < filtercount; i++) { CharSequence repl = mFilters[i].filter(tb, tbstart, tbend, this, start, end); @@ -404,10 +413,6 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable final int origLen = end - start; final int newLen = tbend - tbstart; - if (origLen == 0 && newLen == 0) { - return this; - } - TextWatcher[] textWatchers = getSpans(start, start + origLen, TextWatcher.class); sendBeforeTextChanged(textWatchers, start, origLen, newLen); @@ -415,43 +420,101 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable // a text replacement. If replaced or replacement text length is zero, this // is already taken care of. boolean adjustSelection = origLen != 0 && newLen != 0; - int selstart = 0; - int selend = 0; + int selectionStart = 0; + int selectionEnd = 0; if (adjustSelection) { - selstart = Selection.getSelectionStart(this); - selend = Selection.getSelectionEnd(this); + selectionStart = Selection.getSelectionStart(this); + selectionEnd = Selection.getSelectionEnd(this); } - checkRange("replace", start, end); - change(start, end, tb, tbstart, tbend); if (adjustSelection) { - if (selstart > start && selstart < end) { - long off = selstart - start; - - off = off * newLen / origLen; - selstart = (int) off + start; + if (selectionStart > start && selectionStart < end) { + final int offset = (selectionStart - start) * newLen / origLen; + selectionStart = start + offset; - setSpan(false, Selection.SELECTION_START, selstart, selstart, + setSpan(false, Selection.SELECTION_START, selectionStart, selectionStart, Spanned.SPAN_POINT_POINT); } - if (selend > start && selend < end) { - long off = selend - start; + if (selectionEnd > start && selectionEnd < end) { + final int offset = (selectionEnd - start) * newLen / origLen; + selectionEnd = start + offset; - off = off * newLen / origLen; - selend = (int) off + start; - - setSpan(false, Selection.SELECTION_END, selend, selend, Spanned.SPAN_POINT_POINT); + setSpan(false, Selection.SELECTION_END, selectionEnd, selectionEnd, + Spanned.SPAN_POINT_POINT); } } sendTextChanged(textWatchers, start, origLen, newLen); sendAfterTextChanged(textWatchers); + // Span watchers need to be called after text watchers, which may update the layout + sendToSpanWatchers(start, end, newLen - origLen); + return this; } + private void sendToSpanWatchers(int replaceStart, int replaceEnd, int nbNewChars) { + for (int i = 0; i < mSpanCountBeforeAdd; i++) { + int spanStart = mSpanStarts[i]; + int spanEnd = mSpanEnds[i]; + if (spanStart > mGapStart) spanStart -= mGapLength; + if (spanEnd > mGapStart) spanEnd -= mGapLength; + int spanFlags = mSpanFlags[i]; + + int newReplaceEnd = replaceEnd + nbNewChars; + boolean spanChanged = false; + int previousSpanStart = spanStart; + if (spanStart > newReplaceEnd) { + if (nbNewChars != 0) { + previousSpanStart -= nbNewChars; + spanChanged = true; + } + } else if (spanStart >= replaceStart) { + // No change if span start was already at replace interval boundaries before replace + if ((spanStart != replaceStart || + ((spanFlags & SPAN_START_AT_START) != SPAN_START_AT_START)) && + (spanStart != newReplaceEnd || + ((spanFlags & SPAN_START_AT_END) != SPAN_START_AT_END))) { + // TODO previousSpanStart is incorrect, but we would need to save all the + // previous spans' positions before replace to provide it + spanChanged = true; + } + } + int previousSpanEnd = spanEnd; + if (spanEnd > newReplaceEnd) { + if (nbNewChars != 0) { + previousSpanEnd -= nbNewChars; + spanChanged = true; + } + } else if (spanEnd >= replaceStart) { + // No change if span start was already at replace interval boundaries before replace + if ((spanEnd != replaceStart || + ((spanFlags & SPAN_END_AT_START) != SPAN_END_AT_START)) && + (spanEnd != newReplaceEnd || + ((spanFlags & SPAN_END_AT_END) != SPAN_END_AT_END))) { + // TODO same as above for previousSpanEnd + spanChanged = true; + } + } + + if (spanChanged) { + sendSpanChanged(mSpans[i], previousSpanStart, previousSpanEnd, spanStart, spanEnd); + } + mSpanFlags[i] &= ~SPAN_START_END_MASK; + } + + // The spans starting at mIntermediateSpanCount were added from the replacement text + for (int i = mSpanCountBeforeAdd; i < mSpanCount; i++) { + int spanStart = mSpanStarts[i]; + int spanEnd = mSpanEnds[i]; + if (spanStart > mGapStart) spanStart -= mGapLength; + if (spanEnd > mGapStart) spanEnd -= mGapLength; + sendSpanAdded(mSpans[i], spanStart, spanEnd); + } + } + /** * Mark the specified range of text with the specified object. * The flags determine how the span will behave when text is @@ -788,13 +851,12 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable if (end <= mGapStart) { System.arraycopy(mText, start, dest, destoff, end - start); } else if (start >= mGapStart) { - System.arraycopy(mText, start + mGapLength, - dest, destoff, end - start); + System.arraycopy(mText, start + mGapLength, dest, destoff, end - start); } else { System.arraycopy(mText, start, dest, destoff, mGapStart - start); System.arraycopy(mText, mGapStart + mGapLength, - dest, destoff + (mGapStart - start), - end - mGapStart); + dest, destoff + (mGapStart - start), + end - mGapStart); } } @@ -863,12 +925,14 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable } } - private void sendSpanChanged(Object what, int s, int e, int st, int en) { - SpanWatcher[] recip = getSpans(Math.min(s, st), Math.max(e, en), SpanWatcher.class); - int n = recip.length; - + private void sendSpanChanged(Object what, int oldStart, int oldEnd, int start, int end) { + // The bounds of a possible SpanWatcher are guaranteed to be set before this method is + // called, so that the order of the span does not affect this broadcast. + SpanWatcher[] spanWatchers = getSpans(Math.min(oldStart, start), + Math.min(Math.max(oldEnd, end), length()), SpanWatcher.class); + int n = spanWatchers.length; for (int i = 0; i < n; i++) { - recip[i].onSpanChanged(this, what, s, e, st, en); + spanWatchers[i].onSpanChanged(this, what, oldStart, oldEnd, start, end); } } @@ -879,26 +943,23 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable private void checkRange(final String operation, int start, int end) { if (end < start) { throw new IndexOutOfBoundsException(operation + " " + - region(start, end) + - " has end before start"); + region(start, end) + " has end before start"); } int len = length(); if (start > len || end > len) { throw new IndexOutOfBoundsException(operation + " " + - region(start, end) + - " ends beyond length " + len); + region(start, end) + " ends beyond length " + len); } if (start < 0 || end < 0) { throw new IndexOutOfBoundsException(operation + " " + - region(start, end) + - " starts before 0"); + region(start, end) + " starts before 0"); } } -/* + /* private boolean isprint(char c) { // XXX if (c >= ' ' && c <= '~') return true; @@ -977,7 +1038,7 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable System.out.print("\n"); } -*/ + */ /** * Don't call this yourself -- exists for Canvas to use internally. @@ -1023,7 +1084,7 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable } } - /** + /** * Don't call this yourself -- exists for Paint to use internally. * {@hide} */ @@ -1059,8 +1120,7 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable if (end <= mGapStart) { ret = p.getTextWidths(mText, start, end - start, widths); } else if (start >= mGapStart) { - ret = p.getTextWidths(mText, start + mGapLength, end - start, - widths); + ret = p.getTextWidths(mText, start + mGapLength, end - start, widths); } else { char[] buf = TextUtils.obtain(end - start); @@ -1205,6 +1265,7 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable private int[] mSpanEnds; private int[] mSpanFlags; private int mSpanCount; + private int mSpanCountBeforeAdd; // TODO These value are tightly related to the public SPAN_MARK/POINT values in {@link Spanned} private static final int MARK = 1; @@ -1214,4 +1275,11 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable private static final int START_MASK = 0xF0; private static final int END_MASK = 0x0F; private static final int START_SHIFT = 4; + + // These bits are not (currently) used by SPANNED flags + private static final int SPAN_START_AT_START = 0x1000; + private static final int SPAN_START_AT_END = 0x2000; + private static final int SPAN_END_AT_START = 0x4000; + private static final int SPAN_END_AT_END = 0x8000; + private static final int SPAN_START_END_MASK = 0xF000; } diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java index aa0ac74..8bc36b7 100644 --- a/core/java/android/view/HardwareRenderer.java +++ b/core/java/android/view/HardwareRenderer.java @@ -237,6 +237,17 @@ public abstract class HardwareRenderer { abstract boolean validate(); /** + * This method ensures the hardware renderer is in a valid state + * before executing the specified action. + * + * This method will attempt to set a valid state even if the window + * the renderer is attached to was destroyed. + * + * @return true if the action was run + */ + abstract boolean safelyRun(Runnable action); + + /** * Setup the hardware renderer for drawing. This is called whenever the * size of the target surface changes or when the surface is first created. * @@ -1380,26 +1391,40 @@ public abstract class HardwareRenderer { } @Override - void destroyHardwareResources(View view) { - if (view != null) { - boolean needsContext = true; - if (isEnabled() && checkCurrent() != SURFACE_STATE_ERROR) needsContext = false; - - if (needsContext) { - Gl20RendererEglContext managedContext = - (Gl20RendererEglContext) sEglContextStorage.get(); - if (managedContext == null) return; - usePbufferSurface(managedContext.getContext()); - } - - destroyResources(view); - GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_LAYERS); + boolean safelyRun(Runnable action) { + boolean needsContext = true; + if (isEnabled() && checkCurrent() != SURFACE_STATE_ERROR) needsContext = false; + + if (needsContext) { + Gl20RendererEglContext managedContext = + (Gl20RendererEglContext) sEglContextStorage.get(); + if (managedContext == null) return false; + usePbufferSurface(managedContext.getContext()); + } + try { + action.run(); + } finally { if (needsContext) { sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); } } + + return true; + } + + @Override + void destroyHardwareResources(final View view) { + if (view != null) { + safelyRun(new Runnable() { + @Override + public void run() { + destroyResources(view); + GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_LAYERS); + } + }); + } } private static void destroyResources(View view) { diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java index 75b2c746..4ebb679 100755 --- a/core/java/android/view/InputDevice.java +++ b/core/java/android/view/InputDevice.java @@ -40,6 +40,7 @@ import java.util.List; */ public final class InputDevice implements Parcelable { private final int mId; + private final int mGeneration; private final String mName; private final String mDescriptor; private final int mSources; @@ -302,9 +303,10 @@ public final class InputDevice implements Parcelable { }; // Called by native code. - private InputDevice(int id, String name, String descriptor, int sources, + private InputDevice(int id, int generation, String name, String descriptor, int sources, int keyboardType, KeyCharacterMap keyCharacterMap) { mId = id; + mGeneration = generation; mName = name; mDescriptor = descriptor; mSources = sources; @@ -314,6 +316,7 @@ public final class InputDevice implements Parcelable { private InputDevice(Parcel in) { mId = in.readInt(); + mGeneration = in.readInt(); mName = in.readString(); mDescriptor = in.readString(); mSources = in.readInt(); @@ -364,6 +367,19 @@ public final class InputDevice implements Parcelable { } /** + * Gets a generation number for this input device. + * The generation number is incremented whenever the device is reconfigured and its + * properties may have changed. + * + * @return The generation number. + * + * @hide + */ + public int getGeneration() { + return mGeneration; + } + + /** * Gets the input device descriptor, which is a stable identifier for an input device. * <p> * An input device descriptor uniquely identifies an input device. Its value @@ -595,6 +611,7 @@ public final class InputDevice implements Parcelable { @Override public void writeToParcel(Parcel out, int flags) { out.writeInt(mId); + out.writeInt(mGeneration); out.writeString(mName); out.writeString(mDescriptor); out.writeInt(mSources); @@ -624,6 +641,7 @@ public final class InputDevice implements Parcelable { StringBuilder description = new StringBuilder(); description.append("Input Device ").append(mId).append(": ").append(mName).append("\n"); description.append(" Descriptor: ").append(mDescriptor).append("\n"); + description.append(" Generation: ").append(mGeneration).append("\n"); description.append(" Keyboard Type: "); switch (mKeyboardType) { diff --git a/core/java/android/view/KeyCharacterMap.java b/core/java/android/view/KeyCharacterMap.java index 3d165ea..12d7b12 100644 --- a/core/java/android/view/KeyCharacterMap.java +++ b/core/java/android/view/KeyCharacterMap.java @@ -22,8 +22,6 @@ import android.text.method.MetaKeyKeyListener; import android.util.AndroidRuntimeException; import android.util.SparseIntArray; import android.hardware.input.InputManager; -import android.util.SparseArray; -import android.view.InputDevice.MotionRange; import java.lang.Character; diff --git a/core/java/android/view/TextureView.java b/core/java/android/view/TextureView.java index 3cd8b71..32029ba 100644 --- a/core/java/android/view/TextureView.java +++ b/core/java/android/view/TextureView.java @@ -187,7 +187,9 @@ public class TextureView extends View { public void setOpaque(boolean opaque) { if (opaque != mOpaque) { mOpaque = opaque; - updateLayer(); + if (mLayer != null) { + updateLayer(); + } } } @@ -204,7 +206,18 @@ public class TextureView extends View { @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); - destroySurface(); + if (mLayer != null && mAttachInfo != null && mAttachInfo.mHardwareRenderer != null) { + boolean success = mAttachInfo.mHardwareRenderer.safelyRun(new Runnable() { + @Override + public void run() { + destroySurface(); + } + }); + + if (!success) { + Log.w(LOG_TAG, "TextureView was not able to destroy its surface: " + this); + } + } } private void destroySurface() { diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 0be7a87..1fa19d1 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -4483,10 +4483,8 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal getDrawingRect(bounds); info.setBoundsInParent(bounds); - int[] locationOnScreen = mAttachInfo.mInvalidateChildLocation; - getLocationOnScreen(locationOnScreen); - bounds.offsetTo(0, 0); - bounds.offset(locationOnScreen[0], locationOnScreen[1]); + getGlobalVisibleRect(bounds); + bounds.offset(mAttachInfo.mWindowLeft, mAttachInfo.mWindowTop); info.setBoundsInScreen(bounds); if ((mPrivateFlags & IS_ROOT_NAMESPACE) == 0) { @@ -9792,7 +9790,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * @attr ref android.R.styleable#View_scrollbarSize */ public int getScrollBarSize() { - return mScrollCache == null ? ViewConfiguration.getScrollBarSize() : + return mScrollCache == null ? ViewConfiguration.get(mContext).getScaledScrollBarSize() : mScrollCache.scrollBarSize; } @@ -12971,6 +12969,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * background */ public void setBackground(Drawable background) { + //noinspection deprecation setBackgroundDrawable(background); } @@ -14296,7 +14295,15 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal */ public void setAnimation(Animation animation) { mCurrentAnimation = animation; + if (animation != null) { + // If the screen is off assume the animation start time is now instead of + // the next frame we draw. Keeping the START_ON_FIRST_FRAME start time + // would cause the animation to start when the screen turns back on + if (mAttachInfo != null && !mAttachInfo.mScreenOn && + animation.getStartTime() == Animation.START_ON_FIRST_FRAME) { + animation.setStartTime(AnimationUtils.currentAnimationTimeMillis()); + } animation.reset(); } } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 899fb32..7a43cf1 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -5065,6 +5065,23 @@ public final class ViewRootImpl implements ViewParent, } /** + * Computes whether a view is visible on the screen. + * + * @param view The view to check. + * @return Whether the view is visible on the screen. + */ + private boolean isDisplayedOnScreen(View view) { + // The first two checks are made also made by isShown() which + // however traverses the tree up to the parent to catch that. + // Therefore, we do some fail fast check to minimize the up + // tree traversal. + return (view.mAttachInfo != null + && view.mAttachInfo.mWindowVisibility == View.VISIBLE + && view.isShown() + && view.getGlobalVisibleRect(mTempRect)); + } + + /** * Class for managing accessibility interactions initiated from the system * and targeting the view hierarchy. A *ClientThread method is to be * called from the interaction connection this ViewAncestor gives the @@ -5175,7 +5192,7 @@ public final class ViewRootImpl implements ViewParent, } else { target = findViewByAccessibilityId(accessibilityViewId); } - if (target != null && target.getVisibility() == View.VISIBLE) { + if (target != null && isDisplayedOnScreen(target)) { getAccessibilityNodePrefetcher().prefetchAccessibilityNodeInfos(target, virtualDescendantId, prefetchFlags, infos); } @@ -5231,7 +5248,7 @@ public final class ViewRootImpl implements ViewParent, } if (root != null) { View target = root.findViewById(viewId); - if (target != null && target.getVisibility() == View.VISIBLE) { + if (target != null && isDisplayedOnScreen(target)) { info = target.createAccessibilityNodeInfo(); } } @@ -5287,7 +5304,7 @@ public final class ViewRootImpl implements ViewParent, } else { target = ViewRootImpl.this.mView; } - if (target != null && target.getVisibility() == View.VISIBLE) { + if (target != null && isDisplayedOnScreen(target)) { AccessibilityNodeProvider provider = target.getAccessibilityNodeProvider(); if (provider != null) { infos = provider.findAccessibilityNodeInfosByText(text, @@ -5304,7 +5321,7 @@ public final class ViewRootImpl implements ViewParent, final int viewCount = foundViews.size(); for (int i = 0; i < viewCount; i++) { View foundView = foundViews.get(i); - if (foundView.getVisibility() == View.VISIBLE) { + if (isDisplayedOnScreen(foundView)) { provider = foundView.getAccessibilityNodeProvider(); if (provider != null) { List<AccessibilityNodeInfo> infosFromProvider = @@ -5367,7 +5384,7 @@ public final class ViewRootImpl implements ViewParent, boolean succeeded = false; try { View target = findViewByAccessibilityId(accessibilityViewId); - if (target != null && target.getVisibility() == View.VISIBLE) { + if (target != null && isDisplayedOnScreen(target)) { AccessibilityNodeProvider provider = target.getAccessibilityNodeProvider(); if (provider != null) { succeeded = provider.performAccessibilityAction(action, @@ -5505,7 +5522,7 @@ public final class ViewRootImpl implements ViewParent, View child = parentGroup.getChildAt(i); if (outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE && child.getAccessibilityViewId() != current.getAccessibilityViewId() - && child.getVisibility() == View.VISIBLE) { + && isDisplayedOnScreen(child)) { final long childNodeId = AccessibilityNodeInfo.makeNodeId( child.getAccessibilityViewId(), AccessibilityNodeInfo.UNDEFINED); AccessibilityNodeInfo info = null; @@ -5533,7 +5550,7 @@ public final class ViewRootImpl implements ViewParent, final int childCount = rootGroup.getChildCount(); for (int i = 0; i < childCount; i++) { View child = rootGroup.getChildAt(i); - if (child.getVisibility() == View.VISIBLE + if (isDisplayedOnScreen(child) && outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) { final long childNodeId = AccessibilityNodeInfo.makeNodeId( child.getAccessibilityViewId(), AccessibilityNodeInfo.UNDEFINED); diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java index 66bdc5d..27baaea 100644 --- a/core/java/android/view/WindowManagerPolicy.java +++ b/core/java/android/view/WindowManagerPolicy.java @@ -667,7 +667,7 @@ public interface WindowManagerPolicy { /** * Create and return an animation to re-display a force hidden window. */ - public Animation createForceHideEnterAnimation(); + public Animation createForceHideEnterAnimation(boolean onWallpaper); /** * Called from the input reader thread before a key is enqueued. diff --git a/core/java/android/webkit/CallbackProxy.java b/core/java/android/webkit/CallbackProxy.java index 800ebc8..79bd5d3 100644 --- a/core/java/android/webkit/CallbackProxy.java +++ b/core/java/android/webkit/CallbackProxy.java @@ -147,6 +147,40 @@ class CallbackProxy extends Handler { } } + private class JsResultReceiver implements JsResult.ResultReceiver { + // This prevents a user from interacting with the result before WebCore is + // ready to handle it. + private boolean mReady; + // Tells us if the user tried to confirm or cancel the result before WebCore + // is ready. + private boolean mTriedToNotifyBeforeReady; + + public JsPromptResult mJsResult = new JsPromptResult(this); + + final void setReady() { + mReady = true; + if (mTriedToNotifyBeforeReady) { + notifyCallbackProxy(); + } + } + + /* Wake up the WebCore thread. */ + @Override + public void onJsResultComplete(JsResult result) { + if (mReady) { + notifyCallbackProxy(); + } else { + mTriedToNotifyBeforeReady = true; + } + } + + private void notifyCallbackProxy() { + synchronized (CallbackProxy.this) { + CallbackProxy.this.notify(); + } + } +} + /** * Construct a new CallbackProxy. */ @@ -531,14 +565,15 @@ class CallbackProxy extends Handler { case JS_ALERT: if (mWebChromeClient != null) { - final JsResult res = (JsResult) msg.obj; + final JsResultReceiver receiver = (JsResultReceiver) msg.obj; + final JsResult res = receiver.mJsResult; String message = msg.getData().getString("message"); String url = msg.getData().getString("url"); if (!mWebChromeClient.onJsAlert(mWebView.getWebView(), url, message, res)) { if (!canShowAlertDialog()) { res.cancel(); - res.setReady(); + receiver.setReady(); break; } new AlertDialog.Builder(mContext) @@ -561,20 +596,21 @@ class CallbackProxy extends Handler { }) .show(); } - res.setReady(); + receiver.setReady(); } break; case JS_CONFIRM: if (mWebChromeClient != null) { - final JsResult res = (JsResult) msg.obj; + final JsResultReceiver receiver = (JsResultReceiver) msg.obj; + final JsResult res = receiver.mJsResult; String message = msg.getData().getString("message"); String url = msg.getData().getString("url"); if (!mWebChromeClient.onJsConfirm(mWebView.getWebView(), url, message, res)) { if (!canShowAlertDialog()) { res.cancel(); - res.setReady(); + receiver.setReady(); break; } new AlertDialog.Builder(mContext) @@ -605,13 +641,14 @@ class CallbackProxy extends Handler { } // Tell the JsResult that it is ready for client // interaction. - res.setReady(); + receiver.setReady(); } break; case JS_PROMPT: if (mWebChromeClient != null) { - final JsPromptResult res = (JsPromptResult) msg.obj; + final JsResultReceiver receiver = (JsResultReceiver) msg.obj; + final JsPromptResult res = receiver.mJsResult; String message = msg.getData().getString("message"); String defaultVal = msg.getData().getString("default"); String url = msg.getData().getString("url"); @@ -619,7 +656,7 @@ class CallbackProxy extends Handler { defaultVal, res)) { if (!canShowAlertDialog()) { res.cancel(); - res.setReady(); + receiver.setReady(); break; } final LayoutInflater factory = LayoutInflater @@ -662,20 +699,21 @@ class CallbackProxy extends Handler { } // Tell the JsResult that it is ready for client // interaction. - res.setReady(); + receiver.setReady(); } break; case JS_UNLOAD: if (mWebChromeClient != null) { - final JsResult res = (JsResult) msg.obj; + final JsResultReceiver receiver = (JsResultReceiver) msg.obj; + final JsResult res = receiver.mJsResult; String message = msg.getData().getString("message"); String url = msg.getData().getString("url"); if (!mWebChromeClient.onJsBeforeUnload(mWebView.getWebView(), url, message, res)) { if (!canShowAlertDialog()) { res.cancel(); - res.setReady(); + receiver.setReady(); break; } final String m = mContext.getString( @@ -700,19 +738,20 @@ class CallbackProxy extends Handler { }) .show(); } - res.setReady(); + receiver.setReady(); } break; case JS_TIMEOUT: if(mWebChromeClient != null) { - final JsResult res = (JsResult) msg.obj; + final JsResultReceiver receiver = (JsResultReceiver) msg.obj; + final JsResult res = receiver.mJsResult; if(mWebChromeClient.onJsTimeout()) { res.confirm(); } else { res.cancel(); } - res.setReady(); + receiver.setReady(); } break; @@ -791,7 +830,8 @@ class CallbackProxy extends Handler { case OPEN_FILE_CHOOSER: if (mWebChromeClient != null) { UploadFileMessageData data = (UploadFileMessageData)msg.obj; - mWebChromeClient.openFileChooser(data.getUploadFile(), data.getAcceptType()); + mWebChromeClient.openFileChooser(data.getUploadFile(), data.getAcceptType(), + data.getCapture()); } break; @@ -1331,7 +1371,7 @@ class CallbackProxy extends Handler { if (mWebChromeClient == null) { return; } - JsResult result = new JsResult(this, false); + JsResultReceiver result = new JsResultReceiver(); Message alert = obtainMessage(JS_ALERT, result); alert.getData().putString("message", message); alert.getData().putString("url", url); @@ -1352,7 +1392,7 @@ class CallbackProxy extends Handler { if (mWebChromeClient == null) { return false; } - JsResult result = new JsResult(this, false); + JsResultReceiver result = new JsResultReceiver(); Message confirm = obtainMessage(JS_CONFIRM, result); confirm.getData().putString("message", message); confirm.getData().putString("url", url); @@ -1365,7 +1405,7 @@ class CallbackProxy extends Handler { Log.e(LOGTAG, Log.getStackTraceString(e)); } } - return result.getResult(); + return result.mJsResult.getResult(); } public String onJsPrompt(String url, String message, String defaultValue) { @@ -1374,7 +1414,7 @@ class CallbackProxy extends Handler { if (mWebChromeClient == null) { return null; } - JsPromptResult result = new JsPromptResult(this); + JsResultReceiver result = new JsResultReceiver(); Message prompt = obtainMessage(JS_PROMPT, result); prompt.getData().putString("message", message); prompt.getData().putString("default", defaultValue); @@ -1388,7 +1428,7 @@ class CallbackProxy extends Handler { Log.e(LOGTAG, Log.getStackTraceString(e)); } } - return result.getStringResult(); + return result.mJsResult.getStringResult(); } public boolean onJsBeforeUnload(String url, String message) { @@ -1397,7 +1437,7 @@ class CallbackProxy extends Handler { if (mWebChromeClient == null) { return true; } - JsResult result = new JsResult(this, true); + JsResultReceiver result = new JsResultReceiver(); Message confirm = obtainMessage(JS_UNLOAD, result); confirm.getData().putString("message", message); confirm.getData().putString("url", url); @@ -1410,7 +1450,7 @@ class CallbackProxy extends Handler { Log.e(LOGTAG, Log.getStackTraceString(e)); } } - return result.getResult(); + return result.mJsResult.getResult(); } /** @@ -1540,7 +1580,7 @@ class CallbackProxy extends Handler { if (mWebChromeClient == null) { return true; } - JsResult result = new JsResult(this, true); + JsResultReceiver result = new JsResultReceiver(); Message timeout = obtainMessage(JS_TIMEOUT, result); synchronized (this) { sendMessage(timeout); @@ -1551,7 +1591,7 @@ class CallbackProxy extends Handler { Log.e(LOGTAG, Log.getStackTraceString(e)); } } - return result.getResult(); + return result.mJsResult.getResult(); } public void getVisitedHistory(ValueCallback<String[]> callback) { @@ -1566,10 +1606,12 @@ class CallbackProxy extends Handler { private static class UploadFileMessageData { private UploadFile mCallback; private String mAcceptType; + private String mCapture; - public UploadFileMessageData(UploadFile uploadFile, String acceptType) { + public UploadFileMessageData(UploadFile uploadFile, String acceptType, String capture) { mCallback = uploadFile; mAcceptType = acceptType; + mCapture = capture; } public UploadFile getUploadFile() { @@ -1579,6 +1621,10 @@ class CallbackProxy extends Handler { public String getAcceptType() { return mAcceptType; } + + public String getCapture() { + return mCapture; + } } private class UploadFile implements ValueCallback<Uri> { @@ -1597,13 +1643,13 @@ class CallbackProxy extends Handler { /** * Called by WebViewCore to open a file chooser. */ - /* package */ Uri openFileChooser(String acceptType) { + /* package */ Uri openFileChooser(String acceptType, String capture) { if (mWebChromeClient == null) { return null; } Message myMessage = obtainMessage(OPEN_FILE_CHOOSER); UploadFile uploadFile = new UploadFile(); - UploadFileMessageData data = new UploadFileMessageData(uploadFile, acceptType); + UploadFileMessageData data = new UploadFileMessageData(uploadFile, acceptType, capture); myMessage.obj = data; synchronized (this) { sendMessage(myMessage); diff --git a/core/java/android/webkit/DeviceMotionAndOrientationManager.java b/core/java/android/webkit/DeviceMotionAndOrientationManager.java index 79b78d8..ea1e9ff 100644 --- a/core/java/android/webkit/DeviceMotionAndOrientationManager.java +++ b/core/java/android/webkit/DeviceMotionAndOrientationManager.java @@ -22,9 +22,8 @@ package android.webkit; * * This could be part of WebViewCore, but have moved it to its own class to * avoid bloat there. - * @hide */ -public final class DeviceMotionAndOrientationManager { +final class DeviceMotionAndOrientationManager { private WebViewCore mWebViewCore; public DeviceMotionAndOrientationManager(WebViewCore webViewCore) { @@ -32,12 +31,12 @@ public final class DeviceMotionAndOrientationManager { } /** - * Sets whether the Page for this WebViewCore should use a mock DeviceOrientation + * Sets that the Page for this WebViewCore should use a mock DeviceOrientation * client. */ - public void useMock() { + public void setUseMock() { assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName()); - nativeUseMock(mWebViewCore); + nativeSetUseMock(mWebViewCore); } /** @@ -66,7 +65,7 @@ public final class DeviceMotionAndOrientationManager { } // Native functions - private static native void nativeUseMock(WebViewCore webViewCore); + private static native void nativeSetUseMock(WebViewCore webViewCore); private static native void nativeSetMockOrientation(WebViewCore webViewCore, boolean canProvideAlpha, double alpha, boolean canProvideBeta, double beta, boolean canProvideGamma, double gamma); diff --git a/core/java/android/webkit/DeviceMotionService.java b/core/java/android/webkit/DeviceMotionService.java index b4d5759..9121429 100755 --- a/core/java/android/webkit/DeviceMotionService.java +++ b/core/java/android/webkit/DeviceMotionService.java @@ -153,6 +153,7 @@ final class DeviceMotionService implements SensorEventListener { * SensorEventListener implementation. * Callbacks happen on the thread on which we registered - the WebCore thread. */ + @Override public void onSensorChanged(SensorEvent event) { assert(event.values.length == 3); assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName()); @@ -170,6 +171,7 @@ final class DeviceMotionService implements SensorEventListener { } } + @Override public void onAccuracyChanged(Sensor sensor, int accuracy) { assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName()); } diff --git a/core/java/android/webkit/DeviceOrientationService.java b/core/java/android/webkit/DeviceOrientationService.java index 47c8ab7..2e8656c 100755 --- a/core/java/android/webkit/DeviceOrientationService.java +++ b/core/java/android/webkit/DeviceOrientationService.java @@ -184,6 +184,7 @@ final class DeviceOrientationService implements SensorEventListener { * SensorEventListener implementation. * Callbacks happen on the thread on which we registered - the WebCore thread. */ + @Override public void onSensorChanged(SensorEvent event) { assert(event.values.length == 3); assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName()); @@ -217,6 +218,7 @@ final class DeviceOrientationService implements SensorEventListener { } } + @Override public void onAccuracyChanged(Sensor sensor, int accuracy) { assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName()); } diff --git a/core/java/android/webkit/GeolocationPermissions.java b/core/java/android/webkit/GeolocationPermissions.java index 93eb082..1160d57 100755 --- a/core/java/android/webkit/GeolocationPermissions.java +++ b/core/java/android/webkit/GeolocationPermissions.java @@ -293,6 +293,16 @@ public final class GeolocationPermissions { postMessage(Message.obtain(null, CLEAR_ALL)); } + /** + * This class should not be instantiated directly, applications must only use + * {@link #getInstance()} to obtain the instance. + * Note this constructor was erroneously public and published in SDK levels prior to 16, but + * applications using it would receive a non-functional instance of this class (there was no + * way to call createHandler() and createUIHandler(), so it would not work). + * @hide + */ + public GeolocationPermissions() {} + // Native functions, run on the WebKit thread. private static native Set nativeGetOrigins(); private static native boolean nativeGetAllowed(String origin); diff --git a/core/java/android/webkit/JsPromptResult.java b/core/java/android/webkit/JsPromptResult.java index 9fcd1bc..a1bf124 100644 --- a/core/java/android/webkit/JsPromptResult.java +++ b/core/java/android/webkit/JsPromptResult.java @@ -18,11 +18,11 @@ package android.webkit; /** - * Public class for handling javascript prompt requests. A - * JsDialogHandlerInterface implentation will receive a jsPrompt call with a - * JsPromptResult parameter. This parameter is used to return a result to - * WebView. The client can call cancel() to cancel the dialog or confirm() with - * the user's input to confirm the dialog. + * Public class for handling JavaScript prompt requests. The WebChromeClient will receive a + * {@link WebChromeClient#onJsPrompt(WebView, String, String, String, JsPromptResult)} call with a + * JsPromptResult instance as a parameter. This parameter is used to return the result of this user + * dialog prompt back to the WebView instance. The client can call cancel() to cancel the dialog or + * confirm() with the user's input to confirm the dialog. */ public class JsPromptResult extends JsResult { // String result of the prompt @@ -36,17 +36,17 @@ public class JsPromptResult extends JsResult { confirm(); } - /*package*/ JsPromptResult(CallbackProxy proxy) { - super(proxy, /* unused */ false); + /** + * @hide Only for use by WebViewProvider implementations + */ + public JsPromptResult(ResultReceiver receiver) { + super(receiver); } - /*package*/ String getStringResult() { + /** + * @hide Only for use by WebViewProvider implementations + */ + public String getStringResult() { return mStringResult; } - - @Override - /*package*/ void handleDefault() { - mStringResult = null; - super.handleDefault(); - } } diff --git a/core/java/android/webkit/JsResult.java b/core/java/android/webkit/JsResult.java index e61ab21..07b686f 100644 --- a/core/java/android/webkit/JsResult.java +++ b/core/java/android/webkit/JsResult.java @@ -16,23 +16,27 @@ package android.webkit; - +/** + * An instance of this class is passed as a parameter in various {@link WebChromeClient} action + * notifications. The object is used as a handle onto the underlying JavaScript-originated request, + * and provides a means for the client to indicate whether this action should proceed. + */ public class JsResult { - // This prevents a user from interacting with the result before WebCore is - // ready to handle it. - private boolean mReady; - // Tells us if the user tried to confirm or cancel the result before WebCore - // is ready. - private boolean mTriedToNotifyBeforeReady; // This is a basic result of a confirm or prompt dialog. protected boolean mResult; /** - * This is the caller of the prompt and is the object that is waiting. - * @hide + * Callback interface, implemented by the WebViewProvider implementation to receive + * notifications when the JavaScript result represented by a JsResult instance has + * @hide Only for use by WebViewProvider implementations */ - protected final CallbackProxy mProxy; - // This is the default value of the result. - private final boolean mDefaultValue; + public interface ResultReceiver { + public void onJsResultComplete(JsResult result); + } + /** + * This is the caller of the prompt and is the object that is waiting. + * @hide + */ + protected final ResultReceiver mReceiver; /** * Handle the result if the user cancelled the dialog. @@ -50,36 +54,22 @@ public class JsResult { wakeUp(); } - /*package*/ JsResult(CallbackProxy proxy, boolean defaultVal) { - mProxy = proxy; - mDefaultValue = defaultVal; + /** + * @hide Only for use by WebViewProvider implementations + */ + public JsResult(ResultReceiver receiver) { + mReceiver = receiver; } - /*package*/ final boolean getResult() { + /** + * @hide Only for use by WebViewProvider implementations + */ + public final boolean getResult() { return mResult; } - /*package*/ final void setReady() { - mReady = true; - if (mTriedToNotifyBeforeReady) { - wakeUp(); - } - } - - /*package*/ void handleDefault() { - setReady(); - mResult = mDefaultValue; - wakeUp(); - } - - /* Wake up the WebCore thread. */ + /* Notify the caller that the JsResult has completed */ protected final void wakeUp() { - if (mReady) { - synchronized (mProxy) { - mProxy.notify(); - } - } else { - mTriedToNotifyBeforeReady = true; - } + mReceiver.onJsResultComplete(this); } } diff --git a/core/java/android/webkit/WebChromeClient.java b/core/java/android/webkit/WebChromeClient.java index a6ef0ce..5cb0d41 100644 --- a/core/java/android/webkit/WebChromeClient.java +++ b/core/java/android/webkit/WebChromeClient.java @@ -346,9 +346,11 @@ public class WebChromeClient { * onReceiveValue must be called to wake up the thread.a * @param acceptType The value of the 'accept' attribute of the input tag * associated with this file picker. + * @param capture The value of the 'capture' attribute of the input tag + * associated with this file picker. * @hide */ - public void openFileChooser(ValueCallback<Uri> uploadFile, String acceptType) { + public void openFileChooser(ValueCallback<Uri> uploadFile, String acceptType, String capture) { uploadFile.onReceiveValue(null); } diff --git a/core/java/android/webkit/WebStorage.java b/core/java/android/webkit/WebStorage.java index 2300c2e..3745258 100644 --- a/core/java/android/webkit/WebStorage.java +++ b/core/java/android/webkit/WebStorage.java @@ -418,6 +418,16 @@ public final class WebStorage { } } + /** + * This class should not be instantiated directly, applications must only use + * {@link #getInstance()} to obtain the instance. + * Note this constructor was erroneously public and published in SDK levels prior to 16, but + * applications using it would receive a non-functional instance of this class (there was no + * way to call createHandler() and createUIHandler(), so it would not work). + * @hide + */ + public WebStorage() {} + // Native functions private static native Set nativeGetOrigins(); private static native long nativeGetUsageForOrigin(String origin); diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 84632c6..5498622 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -1281,8 +1281,10 @@ public class WebView extends AbsoluteLayout * @deprecated {@link #findAllAsync} is preferred. * @see #setFindListener */ + @Deprecated public int findAll(String find) { checkThread(); + StrictMode.noteSlowCall("findAll blocks UI: prefer findAllAsync"); return mProvider.findAll(find); } @@ -1539,6 +1541,7 @@ public class WebView extends AbsoluteLayout * * @deprecated The built-in zoom mechanism is preferred, see * {@link WebSettings#setBuiltInZoomControls(boolean)}. + * @hide since API version 16. */ @Deprecated public View getZoomControls() { diff --git a/core/java/android/webkit/WebViewClassic.java b/core/java/android/webkit/WebViewClassic.java index 5dc2681..504788e 100644 --- a/core/java/android/webkit/WebViewClassic.java +++ b/core/java/android/webkit/WebViewClassic.java @@ -828,6 +828,13 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc // if AUTO_REDRAW_HACK is true, then the CALL key will toggle redrawing // the screen all-the-time. Good for profiling our drawing code static private final boolean AUTO_REDRAW_HACK = false; + + // The rate at which edit text is scrolled in content pixels per millisecond + static private final float TEXT_SCROLL_RATE = 0.01f; + + // The presumed scroll rate for the first scroll of edit text + static private final long TEXT_SCROLL_FIRST_SCROLL_MS = 16; + // true means redraw the screen all-the-time. Only with AUTO_REDRAW_HACK private boolean mAutoRedraw; @@ -853,6 +860,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc boolean mIsEditingText = false; ArrayList<Message> mBatchedTextChanges = new ArrayList<Message>(); boolean mIsBatchingTextChanges = false; + private long mLastEditScroll = 0; private static class OnTrimMemoryListener implements ComponentCallbacks2 { private static OnTrimMemoryListener sInstance = null; @@ -1037,9 +1045,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc // pages with the space bar, in pixels. private static final int PAGE_SCROLL_OVERLAP = 24; - // Time between successive calls to text scroll fling animation - private static final int TEXT_SCROLL_ANIMATION_DELAY_MS = 16; - /** * These prevent calling requestLayout if either dimension is fixed. This * depends on the layout parameters and the measure specs. @@ -1207,7 +1212,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc static final int RELOCATE_AUTO_COMPLETE_POPUP = 146; static final int FOCUS_NODE_CHANGED = 147; static final int AUTOFILL_FORM = 148; - static final int ANIMATE_TEXT_SCROLL = 149; + static final int SCROLL_EDIT_TEXT = 149; static final int EDIT_TEXT_SIZE_CHANGED = 150; static final int SHOW_CARET_HANDLE = 151; static final int UPDATE_CONTENT_BOUNDS = 152; @@ -5049,8 +5054,8 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc * * debug only */ - public void useMockDeviceOrientation() { - mWebViewCore.sendMessage(EventHub.USE_MOCK_DEVICE_ORIENTATION); + public void setUseMockDeviceOrientation() { + mWebViewCore.sendMessage(EventHub.SET_USE_MOCK_DEVICE_ORIENTATION); } /** @@ -6002,9 +6007,9 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc data.mNativeLayer = nativeScrollableLayer( contentX, contentY, data.mNativeLayerRect, null); data.mSlop = viewToContentDimension(mNavSlop); - mTouchHighlightRegion.setEmpty(); + removeTouchHighlight(); if (!mBlockWebkitViewMessages) { - mTouchHighlightRequested = System.currentTimeMillis(); + mTouchHighlightRequested = SystemClock.uptimeMillis(); mWebViewCore.sendMessageAtFrontOfQueue( EventHub.HIT_TEST, data); } @@ -6091,6 +6096,11 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc mSelectDraggingTextQuad.containsPoint(handleX, handleY); boolean inEditBounds = mEditTextContentBounds .contains(handleX, handleY); + if (mIsEditingText && !inEditBounds) { + beginScrollEdit(); + } else { + endScrollEdit(); + } if (inCursorText || (mIsEditingText && !inEditBounds)) { snapDraggingCursor(); } @@ -6240,6 +6250,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc break; } case MotionEvent.ACTION_UP: { + endScrollEdit(); if (!mConfirmMove && mIsEditingText && mSelectionStarted && mIsCaretSelection) { showPasteWindow(); @@ -6335,6 +6346,86 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc } } + /** + * Returns the text scroll speed in content pixels per millisecond based on + * the touch location. + * @param coordinate The x or y touch coordinate in content space + * @param min The minimum coordinate (x or y) of the edit content bounds + * @param max The maximum coordinate (x or y) of the edit content bounds + */ + private static float getTextScrollSpeed(int coordinate, int min, int max) { + if (coordinate < min) { + return (coordinate - min) * TEXT_SCROLL_RATE; + } else if (coordinate >= max) { + return (coordinate - max + 1) * TEXT_SCROLL_RATE; + } else { + return 0.0f; + } + } + + private void beginScrollEdit() { + if (mLastEditScroll == 0) { + mLastEditScroll = SystemClock.uptimeMillis() - + TEXT_SCROLL_FIRST_SCROLL_MS; + scrollEditWithCursor(); + } + } + + private void endScrollEdit() { + mLastEditScroll = 0; + } + + private static int getTextScrollDelta(float speed, long deltaT) { + float distance = speed * deltaT; + int intDistance = (int)Math.floor(distance); + float probability = distance - intDistance; + if (Math.random() < probability) { + intDistance++; + } + return intDistance; + } + /** + * Scrolls edit text a distance based on the last touch point, + * the last scroll time, and the edit text content bounds. + */ + private void scrollEditWithCursor() { + if (mLastEditScroll != 0) { + int x = viewToContentX(mLastTouchX + getScrollX() + mSelectDraggingOffset.x); + float scrollSpeedX = getTextScrollSpeed(x, mEditTextContentBounds.left, + mEditTextContentBounds.right); + int y = viewToContentY(mLastTouchY + getScrollY() + mSelectDraggingOffset.y); + float scrollSpeedY = getTextScrollSpeed(y, mEditTextContentBounds.top, + mEditTextContentBounds.bottom); + if (scrollSpeedX == 0.0f && scrollSpeedY == 0.0f) { + endScrollEdit(); + } else { + long currentTime = SystemClock.uptimeMillis(); + long timeSinceLastUpdate = currentTime - mLastEditScroll; + int deltaX = getTextScrollDelta(scrollSpeedX, timeSinceLastUpdate); + int deltaY = getTextScrollDelta(scrollSpeedY, timeSinceLastUpdate); + mLastEditScroll = currentTime; + if (deltaX == 0 && deltaY == 0) { + // By probability no text scroll this time. Try again later. + mPrivateHandler.sendEmptyMessageDelayed(SCROLL_EDIT_TEXT, + TEXT_SCROLL_FIRST_SCROLL_MS); + } else { + int scrollX = getTextScrollX() + deltaX; + scrollX = Math.min(getMaxTextScrollX(), scrollX); + scrollX = Math.max(0, scrollX); + int scrollY = getTextScrollY() + deltaY; + scrollY = Math.min(getMaxTextScrollY(), scrollY); + scrollY = Math.max(0, scrollY); + scrollEditText(scrollX, scrollY); + int cursorX = mSelectDraggingCursor.x; + int cursorY = mSelectDraggingCursor.y; + mSelectDraggingCursor.set(x - deltaX, y - deltaY); + updateWebkitSelection(); + mSelectDraggingCursor.set(cursorX, cursorY); + } + } + } + } + private void startTouch(float x, float y, long eventTime) { // Remember where the motion event started mStartTouchX = mLastTouchX = Math.round(x); @@ -7053,7 +7144,8 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc if (mFindIsUp) return false; boolean result = false; result = mWebViewPrivate.super_requestFocus(direction, previouslyFocusedRect); - if (mWebViewCore.getSettings().getNeedInitialFocus() && !mWebView.isInTouchMode()) { + if (mWebViewCore.getSettings().getNeedInitialFocus() + && !mWebView.isInTouchMode()) { // For cases such as GMail, where we gain focus from a direction, // we want to move to the first available link. // FIXME: If there are no visible links, we may not want to @@ -7074,7 +7166,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc default: return result; } - // TODO: Send initial focus request to webkit (b/6108927) + mWebViewCore.sendMessage(EventHub.SET_INITIAL_FOCUS, fakeKeyDirection); } return result; } @@ -7673,10 +7765,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc msg.arg1, /* unused */0); break; - case ANIMATE_TEXT_SCROLL: - computeEditTextScroll(); - break; - case EDIT_TEXT_SIZE_CHANGED: if (msg.arg1 == mFieldPointer) { mEditTextContent.set((Rect)msg.obj); @@ -7695,6 +7783,10 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc mEditTextContentBounds.set((Rect) msg.obj); break; + case SCROLL_EDIT_TEXT: + scrollEditWithCursor(); + break; + default: super.handleMessage(msg); break; @@ -7777,13 +7869,16 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc if (mFocusedNode.mHasFocus && mFocusedNode.mEditable) { return false; } - long delay = System.currentTimeMillis() - mTouchHighlightRequested; + long delay = SystemClock.uptimeMillis() - mTouchHighlightRequested; if (delay < ViewConfiguration.getTapTimeout()) { Rect r = mTouchHighlightRegion.getBounds(); mWebView.postInvalidateDelayed(delay, r.left, r.top, r.right, r.bottom); return false; } - return true; + if (mInputDispatcher == null) { + return false; + } + return mInputDispatcher.shouldShowTapHighlight(); } @@ -7893,8 +7988,8 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc if (viewRect.width() < getWidth() >> 1 || viewRect.height() < getHeight() >> 1) { mTouchHighlightRegion.union(viewRect); - } else { - Log.w(LOGTAG, "Skip the huge selection rect:" + } else if (DebugFlags.WEB_VIEW) { + Log.d(LOGTAG, "Skip the huge selection rect:" + viewRect); } } @@ -7995,16 +8090,17 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc mWebView.invalidate(); } - if (mPictureListener != null) { - mPictureListener.onNewPicture(getWebView(), capturePicture()); - } - // update the zoom information based on the new picture mZoomManager.onNewPicture(draw); if (isPictureAfterFirstLayout) { mViewManager.postReadyToDrawAll(); } + scrollEditWithCursor(); + + if (mPictureListener != null) { + mPictureListener.onNewPicture(getWebView(), capturePicture()); + } } /** @@ -8047,13 +8143,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc invalidate(); } - private void computeEditTextScroll() { - if (mEditTextScroller.computeScrollOffset()) { - scrollEditText(mEditTextScroller.getCurrX(), - mEditTextScroller.getCurrY()); - } - } - private void scrollEditText(int scrollX, int scrollY) { // Scrollable edit text. Scroll it. float maxScrollX = getMaxTextScrollX(); @@ -8061,8 +8150,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc mEditTextContent.offsetTo(-scrollX, -scrollY); mWebViewCore.sendMessageAtFrontOfQueue(EventHub.SCROLL_TEXT_INPUT, 0, scrollY, (Float)scrollPercentX); - mPrivateHandler.sendEmptyMessageDelayed(ANIMATE_TEXT_SCROLL, - TEXT_SCROLL_ANIMATION_DELAY_MS); } private void beginTextBatch() { diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java index 6390ffe..7652417 100644 --- a/core/java/android/webkit/WebViewCore.java +++ b/core/java/android/webkit/WebViewCore.java @@ -394,10 +394,12 @@ public final class WebViewCore { * Called by JNI. Open a file chooser to upload a file. * @param acceptType The value of the 'accept' attribute of the * input tag associated with this file picker. + * @param capture The value of the 'capture' attribute of the + * input tag associated with this file picker. * @return String version of the URI. */ - private String openFileChooser(String acceptType) { - Uri uri = mCallbackProxy.openFileChooser(acceptType); + private String openFileChooser(String acceptType, String capture) { + Uri uri = mCallbackProxy.openFileChooser(acceptType, capture); if (uri != null) { String filePath = ""; // Note - querying for MediaStore.Images.Media.DATA @@ -616,9 +618,6 @@ public final class WebViewCore { int unichar, int repeatCount, boolean isShift, boolean isAlt, boolean isSym, boolean isDown); - private native void nativeClick(int nativeClass, int framePtr, int nodePtr, - boolean fake); - private native void nativeSendListBoxChoices(int nativeClass, boolean[] choices, int size); @@ -661,8 +660,6 @@ public final class WebViewCore { int x, int y); private native String nativeRetrieveImageSource(int nativeClass, int x, int y); - private native void nativeTouchUp(int nativeClass, - int touchGeneration, int framePtr, int nodePtr, int x, int y); private native boolean nativeMouseClick(int nativeClass); private native boolean nativeHandleTouchEvent(int nativeClass, int action, @@ -1073,10 +1070,8 @@ public final class WebViewCore { static final int REPLACE_TEXT = 114; static final int PASS_TO_JS = 115; static final int SET_GLOBAL_BOUNDS = 116; - static final int CLICK = 118; static final int SET_NETWORK_STATE = 119; static final int DOC_HAS_IMAGES = 120; - static final int FAKE_CLICK = 121; static final int DELETE_SELECTION = 122; static final int LISTBOX_CHOICES = 123; static final int SINGLE_LISTBOX_CHOICE = 124; @@ -1145,7 +1140,7 @@ public final class WebViewCore { // accessibility support static final int MODIFY_SELECTION = 190; - static final int USE_MOCK_DEVICE_ORIENTATION = 191; + static final int SET_USE_MOCK_DEVICE_ORIENTATION = 191; static final int AUTOFILL_FORM = 192; @@ -1181,6 +1176,7 @@ public final class WebViewCore { // key was pressed (down and up) static final int KEY_PRESS = 223; + static final int SET_INITIAL_FOCUS = 224; // Private handler for WebCore messages. private Handler mHandler; @@ -1371,14 +1367,6 @@ public final class WebViewCore { keyPress(msg.arg1); break; - case FAKE_CLICK: - nativeClick(mNativeClass, msg.arg1, msg.arg2, true); - break; - - case CLICK: - nativeClick(mNativeClass, msg.arg1, msg.arg2, false); - break; - case VIEW_SIZE_CHANGED: { viewSizeChanged((WebViewClassic.ViewSizeData) msg.obj); break; @@ -1665,8 +1653,8 @@ public final class WebViewCore { .sendToTarget(); break; - case USE_MOCK_DEVICE_ORIENTATION: - useMockDeviceOrientation(); + case SET_USE_MOCK_DEVICE_ORIENTATION: + setUseMockDeviceOrientation(); break; case AUTOFILL_FORM: @@ -1761,6 +1749,9 @@ public final class WebViewCore { WebViewClassic.UPDATE_MATCH_COUNT, request).sendToTarget(); break; } + case SET_INITIAL_FOCUS: + nativeSetInitialFocus(mNativeClass, msg.arg1); + break; } } }; @@ -3008,8 +2999,8 @@ public final class WebViewCore { // TODO: Figure out what to do with this (b/6111818) } - private void useMockDeviceOrientation() { - mDeviceMotionAndOrientationManager.useMock(); + private void setUseMockDeviceOrientation() { + mDeviceMotionAndOrientationManager.setUseMock(); } public void setMockDeviceOrientation(boolean canProvideAlpha, double alpha, @@ -3084,6 +3075,7 @@ public final class WebViewCore { private native void nativeClearTextSelection(int nativeClass); private native boolean nativeSelectWordAt(int nativeClass, int x, int y); private native void nativeSelectAll(int nativeClass); + private native void nativeSetInitialFocus(int nativeClass, int keyDirection); private static native void nativeCertTrustChanged(); } diff --git a/core/java/android/webkit/WebViewInputDispatcher.java b/core/java/android/webkit/WebViewInputDispatcher.java index e7024d9..0a0afaa 100644 --- a/core/java/android/webkit/WebViewInputDispatcher.java +++ b/core/java/android/webkit/WebViewInputDispatcher.java @@ -269,9 +269,8 @@ final class WebViewInputDispatcher { */ public boolean postPointerEvent(MotionEvent event, int webKitXOffset, int webKitYOffset, float webKitScale) { - if (event == null - || (event.getSource() & InputDevice.SOURCE_CLASS_POINTER) == 0) { - throw new IllegalArgumentException("event must be a pointer event"); + if (event == null) { + throw new IllegalArgumentException("event cannot be null"); } if (DEBUG) { @@ -349,6 +348,12 @@ final class WebViewInputDispatcher { } } + public boolean shouldShowTapHighlight() { + synchronized (mLock) { + return mPostLongPressScheduled || mPostClickScheduled; + } + } + private void postLongPress() { synchronized (mLock) { if (!mPostLongPressScheduled) { diff --git a/core/java/android/widget/CheckBox.java b/core/java/android/widget/CheckBox.java index 0685eea..858c415 100644 --- a/core/java/android/widget/CheckBox.java +++ b/core/java/android/widget/CheckBox.java @@ -21,8 +21,6 @@ import android.util.AttributeSet; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; -import com.android.internal.R; - /** * <p> @@ -71,16 +69,6 @@ public class CheckBox extends CompoundButton { } @Override - public void onPopulateAccessibilityEvent(AccessibilityEvent event) { - super.onPopulateAccessibilityEvent(event); - if (isChecked()) { - event.getText().add(mContext.getString(R.string.checkbox_checked)); - } else { - event.getText().add(mContext.getString(R.string.checkbox_not_checked)); - } - } - - @Override public void onInitializeAccessibilityEvent(AccessibilityEvent event) { super.onInitializeAccessibilityEvent(event); event.setClassName(CheckBox.class.getName()); diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java index d897a39..b2321d9 100644 --- a/core/java/android/widget/NumberPicker.java +++ b/core/java/android/widget/NumberPicker.java @@ -579,7 +579,7 @@ public class NumberPicker extends LinearLayout { throw new IllegalArgumentException("minWidth > maxWidth"); } - mComputeMaxWidth = (mMaxWidth == Integer.MAX_VALUE); + mComputeMaxWidth = (mMaxWidth == SIZE_UNSPECIFIED); attributesArray.recycle(); @@ -771,6 +771,8 @@ public class NumberPicker extends LinearLayout { mLastDownEventTime = event.getEventTime(); mIngonreMoveEvents = false; mShowSoftInputOnTap = false; + // Make sure we wupport flinging inside scrollables. + getParent().requestDisallowInterceptTouchEvent(true); if (!mFlingScroller.isFinished()) { mFlingScroller.forceFinished(true); mAdjustScroller.forceFinished(true); @@ -1096,12 +1098,7 @@ public class NumberPicker extends LinearLayout { * @see #setMaxValue(int) */ public void setValue(int value) { - if (mValue == value) { - return; - } setValueInternal(value, false); - initializeSelectorWheelIndices(); - invalidate(); } /** @@ -1498,6 +1495,8 @@ public class NumberPicker extends LinearLayout { if (notifyChange) { notifyChange(previous, current); } + initializeSelectorWheelIndices(); + invalidate(); } /** diff --git a/core/java/android/widget/ShareActionProvider.java b/core/java/android/widget/ShareActionProvider.java index 22e9ef1..080b87d 100644 --- a/core/java/android/widget/ShareActionProvider.java +++ b/core/java/android/widget/ShareActionProvider.java @@ -80,16 +80,22 @@ public class ShareActionProvider extends ActionProvider { /** * Called when a share target has been selected. The client can - * decide whether to handle the intent or rely on the default - * behavior which is launching it. + * decide whether to perform some action before the sharing is + * actually performed. * <p> * <strong>Note:</strong> Modifying the intent is not permitted and * any changes to the latter will be ignored. * </p> + * <p> + * <strong>Note:</strong> You should <strong>not</strong> handle the + * intent here. This callback aims to notify the client that a + * sharing is being performed, so the client can update the UI + * if necessary. + * </p> * * @param source The source of the notification. * @param intent The intent for launching the chosen share target. - * @return Whether the client has handled the intent. + * @return The return result is ignored. Always return false for consistency. */ public boolean onShareTargetSelected(ShareActionProvider source, Intent intent); } @@ -308,7 +314,7 @@ public class ShareActionProvider extends ActionProvider { @Override public boolean onChooseActivity(ActivityChooserModel host, Intent intent) { if (mOnShareTargetSelectedListener != null) { - return mOnShareTargetSelectedListener.onShareTargetSelected( + mOnShareTargetSelectedListener.onShareTargetSelected( ShareActionProvider.this, intent); } return false; diff --git a/core/java/android/widget/Spinner.java b/core/java/android/widget/Spinner.java index aef8a34..36d1ee0 100644 --- a/core/java/android/widget/Spinner.java +++ b/core/java/android/widget/Spinner.java @@ -907,7 +907,7 @@ public class Spinner extends AbsSpinner implements OnClickListener { public void onItemClick(AdapterView parent, View v, int position, long id) { Spinner.this.setSelection(position); if (mOnItemClickListener != null) { - Spinner.this.performItemClick(null, position, mAdapter.getItemId(position)); + Spinner.this.performItemClick(v, position, mAdapter.getItemId(position)); } dismiss(); } diff --git a/core/java/android/widget/Switch.java b/core/java/android/widget/Switch.java index a897cc3..0786909 100644 --- a/core/java/android/widget/Switch.java +++ b/core/java/android/widget/Switch.java @@ -806,5 +806,16 @@ public class Switch extends CompoundButton { public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfo(info); info.setClassName(Switch.class.getName()); + CharSequence switchText = isChecked() ? mTextOn : mTextOff; + if (!TextUtils.isEmpty(switchText)) { + CharSequence oldText = info.getText(); + if (TextUtils.isEmpty(oldText)) { + info.setText(switchText); + } else { + StringBuilder newText = new StringBuilder(); + newText.append(oldText).append(' ').append(switchText); + info.setText(newText); + } + } } } |
