diff options
Diffstat (limited to 'core/java')
48 files changed, 896 insertions, 339 deletions
diff --git a/core/java/android/app/ActionBar.java b/core/java/android/app/ActionBar.java index 51c6f3a..24d3a6b 100644 --- a/core/java/android/app/ActionBar.java +++ b/core/java/android/app/ActionBar.java @@ -43,15 +43,18 @@ import android.widget.SpinnerAdapter; * modify various characteristics of the action bar or remove it completely.</p> * <p>From your activity, you can retrieve an instance of {@link ActionBar} by calling {@link * android.app.Activity#getActionBar getActionBar()}.</p> - * <p>For information about how to use the action bar, including how to add action items, navigation - * modes and more, read the <a href="{@docRoot}guide/topics/ui/actionbar.html">Action - * Bar</a> developer guide.</p> * <p>In some cases, the action bar may be overlayed by another bar that enables contextual actions, * using an {@link android.view.ActionMode}. For example, when the user selects one or more items in * your activity, you can enable an action mode that offers actions specific to the selected * items, with a UI that temporarily replaces the action bar. Although the UI may occupy the * same space, the {@link android.view.ActionMode} APIs are distinct and independent from those for * {@link ActionBar}. + * <div class="special reference"> + * <h3>Developer Guides</h3> + * <p>For information about how to use the action bar, including how to add action items, navigation + * modes and more, read the <a href="{@docRoot}guide/topics/ui/actionbar.html">Action + * Bar</a> developer guide.</p> + * </div> */ public abstract class ActionBar { /** diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 034e3c7..8e8d37d 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -111,18 +111,6 @@ import java.util.HashMap; * {@link android.R.styleable#AndroidManifestActivity <activity>} * declaration in their package's <code>AndroidManifest.xml</code>.</p> * - * <p>The Activity class is an important part of an application's overall lifecycle, - * and the way activities are launched and put together is a fundamental - * part of the platform's application model. For a detailed perspective on the structure of an - * Android application and how activities behave, please read the - * <a href="{@docRoot}guide/topics/fundamentals.html">Application Fundamentals</a> and - * <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back Stack</a> - * documents.</p> - * - * <p>You can also find a detailed discussion about how to create activities in the - * <a href="{@docRoot}guide/topics/fundamentals/activities.html">Activities</a> - * document.</p> - * * <p>Topics covered here: * <ol> * <li><a href="#Fragments">Fragments</a> @@ -133,7 +121,22 @@ import java.util.HashMap; * <li><a href="#Permissions">Permissions</a> * <li><a href="#ProcessLifecycle">Process Lifecycle</a> * </ol> - * + * + * <div class="special reference"> + * <h3>Developer Guides</h3> + * <p>The Activity class is an important part of an application's overall lifecycle, + * and the way activities are launched and put together is a fundamental + * part of the platform's application model. For a detailed perspective on the structure of an + * Android application and how activities behave, please read the + * <a href="{@docRoot}guide/topics/fundamentals.html">Application Fundamentals</a> and + * <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back Stack</a> + * developer guides.</p> + * + * <p>You can also find a detailed discussion about how to create activities in the + * <a href="{@docRoot}guide/topics/fundamentals/activities.html">Activities</a> + * developer guide.</p> + * </div> + * * <a name="Fragments"></a> * <h3>Fragments</h3> * @@ -3246,6 +3249,7 @@ public class Activity extends ContextThemeWrapper try { String resolvedType = null; if (fillInIntent != null) { + fillInIntent.setAllowFds(false); resolvedType = fillInIntent.resolveTypeIfNeeded(getContentResolver()); } int result = ActivityManagerNative.getDefault() @@ -3370,6 +3374,7 @@ public class Activity extends ContextThemeWrapper if (mParent == null) { int result = IActivityManager.START_RETURN_INTENT_TO_CALLER; try { + intent.setAllowFds(false); result = ActivityManagerNative.getDefault() .startActivity(mMainThread.getApplicationThread(), intent, intent.resolveTypeIfNeeded( @@ -3419,6 +3424,7 @@ public class Activity extends ContextThemeWrapper public boolean startNextMatchingActivity(Intent intent) { if (mParent == null) { try { + intent.setAllowFds(false); return ActivityManagerNative.getDefault() .startNextMatchingActivity(mToken, intent); } catch (RemoteException e) { @@ -3692,6 +3698,9 @@ public class Activity extends ContextThemeWrapper } if (false) Log.v(TAG, "Finishing self: token=" + mToken); try { + if (resultData != null) { + resultData.setAllowFds(false); + } if (ActivityManagerNative.getDefault() .finishActivity(mToken, resultCode, resultData)) { mFinished = true; @@ -3812,6 +3821,7 @@ public class Activity extends ContextThemeWrapper int flags) { String packageName = getPackageName(); try { + data.setAllowFds(false); IIntentSender target = ActivityManagerNative.getDefault().getIntentSender( IActivityManager.INTENT_SENDER_ACTIVITY_RESULT, packageName, diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 0776e10..99aae37 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -2674,6 +2674,7 @@ public final class ActivityThread { // Next have the activity save its current state and managed dialogs... if (!r.activity.mFinished && saveState) { state = new Bundle(); + state.setAllowFds(false); mInstrumentation.callActivityOnSaveInstanceState(r.activity, state); r.state = state; } @@ -2775,6 +2776,7 @@ public final class ActivityThread { if (!r.activity.mFinished && saveState) { if (r.state == null) { state = new Bundle(); + state.setAllowFds(false); mInstrumentation.callActivityOnSaveInstanceState(r.activity, state); r.state = state; } else { @@ -3306,6 +3308,7 @@ public final class ActivityThread { } if (r.state == null && !r.stopped && !r.isPreHoneycomb()) { r.state = new Bundle(); + r.state.setAllowFds(false); mInstrumentation.callActivityOnSaveInstanceState(r.activity, r.state); } diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 2139704..2bf1fb7 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -874,6 +874,7 @@ class ContextImpl extends Context { try { String resolvedType = null; if (fillInIntent != null) { + fillInIntent.setAllowFds(false); resolvedType = fillInIntent.resolveTypeIfNeeded(getContentResolver()); } int result = ActivityManagerNative.getDefault() @@ -892,6 +893,7 @@ class ContextImpl extends Context { public void sendBroadcast(Intent intent) { String resolvedType = intent.resolveTypeIfNeeded(getContentResolver()); try { + intent.setAllowFds(false); ActivityManagerNative.getDefault().broadcastIntent( mMainThread.getApplicationThread(), intent, resolvedType, null, Activity.RESULT_OK, null, null, null, false, false); @@ -903,6 +905,7 @@ class ContextImpl extends Context { public void sendBroadcast(Intent intent, String receiverPermission) { String resolvedType = intent.resolveTypeIfNeeded(getContentResolver()); try { + intent.setAllowFds(false); ActivityManagerNative.getDefault().broadcastIntent( mMainThread.getApplicationThread(), intent, resolvedType, null, Activity.RESULT_OK, null, null, receiverPermission, false, false); @@ -915,6 +918,7 @@ class ContextImpl extends Context { String receiverPermission) { String resolvedType = intent.resolveTypeIfNeeded(getContentResolver()); try { + intent.setAllowFds(false); ActivityManagerNative.getDefault().broadcastIntent( mMainThread.getApplicationThread(), intent, resolvedType, null, Activity.RESULT_OK, null, null, receiverPermission, true, false); @@ -946,6 +950,7 @@ class ContextImpl extends Context { } String resolvedType = intent.resolveTypeIfNeeded(getContentResolver()); try { + intent.setAllowFds(false); ActivityManagerNative.getDefault().broadcastIntent( mMainThread.getApplicationThread(), intent, resolvedType, rd, initialCode, initialData, initialExtras, receiverPermission, @@ -958,6 +963,7 @@ class ContextImpl extends Context { public void sendStickyBroadcast(Intent intent) { String resolvedType = intent.resolveTypeIfNeeded(getContentResolver()); try { + intent.setAllowFds(false); ActivityManagerNative.getDefault().broadcastIntent( mMainThread.getApplicationThread(), intent, resolvedType, null, Activity.RESULT_OK, null, null, null, false, true); @@ -989,6 +995,7 @@ class ContextImpl extends Context { } String resolvedType = intent.resolveTypeIfNeeded(getContentResolver()); try { + intent.setAllowFds(false); ActivityManagerNative.getDefault().broadcastIntent( mMainThread.getApplicationThread(), intent, resolvedType, rd, initialCode, initialData, initialExtras, null, @@ -1005,6 +1012,7 @@ class ContextImpl extends Context { intent.setDataAndType(intent.getData(), resolvedType); } try { + intent.setAllowFds(false); ActivityManagerNative.getDefault().unbroadcastIntent( mMainThread.getApplicationThread(), intent); } catch (RemoteException e) { @@ -1069,6 +1077,7 @@ class ContextImpl extends Context { @Override public ComponentName startService(Intent service) { try { + service.setAllowFds(false); ComponentName cn = ActivityManagerNative.getDefault().startService( mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(getContentResolver())); @@ -1086,6 +1095,7 @@ class ContextImpl extends Context { @Override public boolean stopService(Intent service) { try { + service.setAllowFds(false); int res = ActivityManagerNative.getDefault().stopService( mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(getContentResolver())); @@ -1116,6 +1126,7 @@ class ContextImpl extends Context { < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) { flags |= BIND_WAIVE_PRIORITY; } + service.setAllowFds(false); int res = ActivityManagerNative.getDefault().bindService( mMainThread.getApplicationThread(), getActivityToken(), service, service.resolveTypeIfNeeded(getContentResolver()), @@ -1148,6 +1159,9 @@ class ContextImpl extends Context { public boolean startInstrumentation(ComponentName className, String profileFile, Bundle arguments) { try { + if (arguments != null) { + arguments.setAllowFds(false); + } return ActivityManagerNative.getDefault().startInstrumentation( className, profileFile, 0, arguments, null); } catch (RemoteException e) { diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java index 3a08e6d..d423d98 100644 --- a/core/java/android/app/Fragment.java +++ b/core/java/android/app/Fragment.java @@ -162,9 +162,6 @@ final class FragmentState implements Parcelable { * constructor to instantiate it. If the empty constructor is not available, * a runtime exception will occur in some cases during state restore. * - * <p>For more documentation, also see the <a - * href="{@docRoot}guide/topics/fundamentals/fragments.html">Fragments</a> developer guide.</p> - * * <p>Topics covered here: * <ol> * <li><a href="#OlderPlatforms">Older Platforms</a> @@ -173,6 +170,12 @@ final class FragmentState implements Parcelable { * <li><a href="#BackStack">Back Stack</a> * </ol> * + * <div class="special reference"> + * <h3>Developer Guides</h3> + * <p>For more information about using fragments, read the + * <a href="{@docRoot}guide/topics/fundamentals/fragments.html">Fragments</a> developer guide.</p> + * </div> + * * <a name="OlderPlatforms"></a> * <h3>Older Platforms</h3> * diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java index 7a6759f..3da4f29 100644 --- a/core/java/android/app/FragmentManager.java +++ b/core/java/android/app/FragmentManager.java @@ -45,6 +45,12 @@ import java.util.Arrays; /** * Interface for interacting with {@link Fragment} objects inside of an * {@link Activity} + * + * <div class="special reference"> + * <h3>Developer Guides</h3> + * <p>For more information about using fragments, read the + * <a href="{@docRoot}guide/topics/fundamentals/fragments.html">Fragments</a> developer guide.</p> + * </div> */ public abstract class FragmentManager { /** diff --git a/core/java/android/app/FragmentTransaction.java b/core/java/android/app/FragmentTransaction.java index c1f3cd6..6e99899 100644 --- a/core/java/android/app/FragmentTransaction.java +++ b/core/java/android/app/FragmentTransaction.java @@ -2,6 +2,12 @@ package android.app; /** * API for performing a set of Fragment operations. + * + * <div class="special reference"> + * <h3>Developer Guides</h3> + * <p>For more information about using fragments, read the + * <a href="{@docRoot}guide/topics/fundamentals/fragments.html">Fragments</a> developer guide.</p> + * </div> */ public abstract class FragmentTransaction { /** diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java index f3bc495..d7f5c55 100644 --- a/core/java/android/app/Instrumentation.java +++ b/core/java/android/app/Instrumentation.java @@ -1375,6 +1375,7 @@ public class Instrumentation { } } try { + intent.setAllowFds(false); int result = ActivityManagerNative.getDefault() .startActivity(whoThread, intent, intent.resolveTypeIfNeeded(who.getContentResolver()), @@ -1415,6 +1416,7 @@ public class Instrumentation { try { String[] resolvedTypes = new String[intents.length]; for (int i=0; i<intents.length; i++) { + intents[i].setAllowFds(false); resolvedTypes[i] = intents[i].resolveTypeIfNeeded(who.getContentResolver()); } int result = ActivityManagerNative.getDefault() @@ -1471,6 +1473,7 @@ public class Instrumentation { } } try { + intent.setAllowFds(false); int result = ActivityManagerNative.getDefault() .startActivity(whoThread, intent, intent.resolveTypeIfNeeded(who.getContentResolver()), diff --git a/core/java/android/app/IntentService.java b/core/java/android/app/IntentService.java index 57a2695..96767ae 100644 --- a/core/java/android/app/IntentService.java +++ b/core/java/android/app/IntentService.java @@ -41,6 +41,12 @@ import android.os.Message; * long as necessary (and will not block the application's main loop), but * only one request will be processed at a time. * + * <div class="special reference"> + * <h3>Developer Guides</h3> + * <p>For a detailed discussion about how to create services, read the + * <a href="{@docRoot}guide/topics/fundamentals/services.html">Services</a> developer guide.</p> + * </div> + * * @see android.os.AsyncTask */ public abstract class IntentService extends Service { diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java index 2549c84..522f477 100644 --- a/core/java/android/app/LoadedApk.java +++ b/core/java/android/app/LoadedApk.java @@ -661,6 +661,9 @@ public final class LoadedApk { "Finishing broadcast to unregistered receiver"); IActivityManager mgr = ActivityManagerNative.getDefault(); try { + if (extras != null) { + extras.setAllowFds(false); + } mgr.finishReceiver(this, resultCode, data, extras, false); } catch (RemoteException e) { Slog.w(ActivityThread.TAG, "Couldn't finish broadcast to unregistered receiver"); diff --git a/core/java/android/app/LoaderManager.java b/core/java/android/app/LoaderManager.java index 46a008d..89e9ddd 100644 --- a/core/java/android/app/LoaderManager.java +++ b/core/java/android/app/LoaderManager.java @@ -47,6 +47,12 @@ import java.lang.reflect.Modifier; * * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/LoaderCursor.java * fragment_cursor} + * + * <div class="special reference"> + * <h3>Developer Guides</h3> + * <p>For more information about using loaders, read the + * <a href="{@docRoot}guide/topics/fundamentals/loaders.html">Loaders</a> developer guide.</p> + * </div> */ public abstract class LoaderManager { /** diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java index b4827cb..b0637a7 100644 --- a/core/java/android/app/PendingIntent.java +++ b/core/java/android/app/PendingIntent.java @@ -192,6 +192,7 @@ public final class PendingIntent implements Parcelable { String resolvedType = intent != null ? intent.resolveTypeIfNeeded( context.getContentResolver()) : null; try { + intent.setAllowFds(false); IIntentSender target = ActivityManagerNative.getDefault().getIntentSender( IActivityManager.INTENT_SENDER_ACTIVITY, packageName, @@ -249,6 +250,7 @@ public final class PendingIntent implements Parcelable { String packageName = context.getPackageName(); String[] resolvedTypes = new String[intents.length]; for (int i=0; i<intents.length; i++) { + intents[i].setAllowFds(false); resolvedTypes[i] = intents[i].resolveTypeIfNeeded(context.getContentResolver()); } try { @@ -287,6 +289,7 @@ public final class PendingIntent implements Parcelable { String resolvedType = intent != null ? intent.resolveTypeIfNeeded( context.getContentResolver()) : null; try { + intent.setAllowFds(false); IIntentSender target = ActivityManagerNative.getDefault().getIntentSender( IActivityManager.INTENT_SENDER_BROADCAST, packageName, @@ -324,6 +327,7 @@ public final class PendingIntent implements Parcelable { String resolvedType = intent != null ? intent.resolveTypeIfNeeded( context.getContentResolver()) : null; try { + intent.setAllowFds(false); IIntentSender target = ActivityManagerNative.getDefault().getIntentSender( IActivityManager.INTENT_SENDER_SERVICE, packageName, diff --git a/core/java/android/app/Service.java b/core/java/android/app/Service.java index ebde6e0..35bd8c0 100644 --- a/core/java/android/app/Service.java +++ b/core/java/android/app/Service.java @@ -50,10 +50,6 @@ import java.io.PrintWriter; * Threads</a>. The {@link IntentService} class is available * as a standard implementation of Service that has its own thread where it * schedules its work to be done.</p> - * - * <p>You can find a detailed discussion about how to create services in the - * <a href="{@docRoot}guide/topics/fundamentals/services.html">Services</a> - * document.</p> * * <p>Topics covered here: * <ol> @@ -64,7 +60,13 @@ import java.io.PrintWriter; * <li><a href="#LocalServiceSample">Local Service Sample</a> * <li><a href="#RemoteMessengerServiceSample">Remote Messenger Service Sample</a> * </ol> - * + * + * <div class="special reference"> + * <h3>Developer Guides</h3> + * <p>For a detailed discussion about how to create services, read the + * <a href="{@docRoot}guide/topics/fundamentals/services.html">Services</a> developer guide.</p> + * </div> + * * <a name="WhatIsAService"></a> * <h3>What is a Service?</h3> * diff --git a/core/java/android/content/BroadcastReceiver.java b/core/java/android/content/BroadcastReceiver.java index 028149b..d71a8d6 100644 --- a/core/java/android/content/BroadcastReceiver.java +++ b/core/java/android/content/BroadcastReceiver.java @@ -90,7 +90,14 @@ import android.util.Slog; * <li><a href="#Permissions">Permissions</a> * <li><a href="#ProcessLifecycle">Process Lifecycle</a> * </ol> - * + * + * <div class="special reference"> + * <h3>Developer Guides</h3> + * <p>For information about how to use this class to receive and resolve intents, read the + * <a href="{@docRoot}guide/topics/intents/intents-filters.html">Intents and Intent Filters</a> + * developer guide.</p> + * </div> + * * <a name="ReceiverLifecycle"></a> * <h3>Receiver Lifecycle</h3> * @@ -366,6 +373,9 @@ public abstract class BroadcastReceiver { mFinished = true; try { + if (mResultExtras != null) { + mResultExtras.setAllowFds(false); + } if (mOrderedHint) { am.finishReceiver(mToken, mResultCode, mResultData, mResultExtras, mAbortBroadcast); @@ -462,6 +472,7 @@ public abstract class BroadcastReceiver { IActivityManager am = ActivityManagerNative.getDefault(); IBinder binder = null; try { + service.setAllowFds(false); binder = am.peekService(service, service.resolveTypeIfNeeded( myContext.getContentResolver())); } catch (RemoteException e) { diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index f44d038..2be5153 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -55,13 +55,21 @@ import java.util.Set; * {@link android.content.Context#bindService} to communicate with a * background {@link android.app.Service}. * - * <p>An Intent provides a facility for performing late runtime binding between - * the code in different applications. Its most significant use is in the - * launching of activities, where it can be thought of as the glue between - * activities. It is - * basically a passive data structure holding an abstract description of an - * action to be performed. The primary pieces of information in an intent - * are:</p> + * <p>An Intent provides a facility for performing late runtime binding between the code in + * different applications. Its most significant use is in the launching of activities, where it + * can be thought of as the glue between activities. It is basically a passive data structure + * holding an abstract description of an action to be performed.</p> + * + * <div class="special reference"> + * <h3>Developer Guides</h3> + * <p>For information about how to create and resolve intents, read the + * <a href="{@docRoot}guide/topics/intents/intents-filters.html">Intents and Intent Filters</a> + * developer guide.</p> + * </div> + * + * <a name="IntentStructure"></a> + * <h3>Intent Structure</h3> + * <p>The primary pieces of information in an intent are:</p> * * <ul> * <li> <p><b>action</b> -- The general action to be performed, such as @@ -3568,6 +3576,13 @@ public class Intent implements Parcelable, Cloneable { return mExtras != null && mExtras.hasFileDescriptors(); } + /** @hide */ + public void setAllowFds(boolean allowFds) { + if (mExtras != null) { + mExtras.setAllowFds(allowFds); + } + } + /** * Retrieve extended data from the intent. * diff --git a/core/java/android/content/IntentFilter.java b/core/java/android/content/IntentFilter.java index f9b3fd5..3b0d846 100644 --- a/core/java/android/content/IntentFilter.java +++ b/core/java/android/content/IntentFilter.java @@ -71,6 +71,14 @@ import java.util.Set; * To specify a path, you also must specify both one or more authorities and * one or more schemes it is associated with. * + * <div class="special reference"> + * <h3>Developer Guides</h3> + * <p>For information about how to create and resolve intents, read the + * <a href="{@docRoot}guide/topics/intents/intents-filters.html">Intents and Intent Filters</a> + * developer guide.</p> + * </div> + * + * <h3>Filter Rules</h3> * <p>A match is based on the following rules. Note that * for an IntentFilter to match an Intent, three conditions must hold: * the <strong>action</strong> and <strong>category</strong> must match, and diff --git a/core/java/android/content/Loader.java b/core/java/android/content/Loader.java index 368c33e..b962800 100644 --- a/core/java/android/content/Loader.java +++ b/core/java/android/content/Loader.java @@ -40,8 +40,12 @@ import java.io.PrintWriter; * * <p>Most implementations should not derive directly from this class, but * instead inherit from {@link AsyncTaskLoader}.</p> - * <p>For more information, see the <a - * href="{@docRoot}guide/topics/fundamentals/loaders.html">Loaders</a> developer guide.</p> + * + * <div class="special reference"> + * <h3>Developer Guides</h3> + * <p>For more information about using loaders, read the + * <a href="{@docRoot}guide/topics/fundamentals/loaders.html">Loaders</a> developer guide.</p> + * </div> * * @param <D> The result returned when the load is complete */ diff --git a/core/java/android/content/SyncManager.java b/core/java/android/content/SyncManager.java index 127efa2..4225393 100644 --- a/core/java/android/content/SyncManager.java +++ b/core/java/android/content/SyncManager.java @@ -1301,7 +1301,7 @@ public class SyncManager implements OnAccountsUpdateListener { elapsedTime = authoritySyncStats.elapsedTime; times = authoritySyncStats.times; - timeStr = String.format("%d/%d%%", + timeStr = String.format("%ds/%d%%", elapsedTime / 1000, elapsedTime * 100 / totalElapsedTime); timesStr = String.format("%d/%d%%", @@ -1309,32 +1309,30 @@ public class SyncManager implements OnAccountsUpdateListener { times * 100 / totalTimes); pw.printf(authorityFormat, name, timesStr, timeStr); - if (authoritySyncStats.accountMap.size() > 1) { - final List<AccountSyncStats> sortedAccounts = - new ArrayList<AccountSyncStats>( - authoritySyncStats.accountMap.values()); - Collections.sort(sortedAccounts, new Comparator<AccountSyncStats>() { - @Override - public int compare(AccountSyncStats lhs, AccountSyncStats rhs) { - // reverse order - int compare = Integer.compare(rhs.times, lhs.times); - if (compare == 0) { - compare = Long.compare(rhs.elapsedTime, lhs.elapsedTime); - } - return compare; + final List<AccountSyncStats> sortedAccounts = + new ArrayList<AccountSyncStats>( + authoritySyncStats.accountMap.values()); + Collections.sort(sortedAccounts, new Comparator<AccountSyncStats>() { + @Override + public int compare(AccountSyncStats lhs, AccountSyncStats rhs) { + // reverse order + int compare = Integer.compare(rhs.times, lhs.times); + if (compare == 0) { + compare = Long.compare(rhs.elapsedTime, lhs.elapsedTime); } - }); - for (AccountSyncStats stats: sortedAccounts) { - elapsedTime = stats.elapsedTime; - times = stats.times; - timeStr = String.format("%d/%d%%", - elapsedTime / 1000, - elapsedTime * 100 / totalElapsedTime); - timesStr = String.format("%d/%d%%", - times, - times * 100 / totalTimes); - pw.printf(accountFormat, stats.name, timesStr, timeStr); + return compare; } + }); + for (AccountSyncStats stats: sortedAccounts) { + elapsedTime = stats.elapsedTime; + times = stats.times; + timeStr = String.format("%ds/%d%%", + elapsedTime / 1000, + elapsedTime * 100 / totalElapsedTime); + timesStr = String.format("%d/%d%%", + times, + times * 100 / totalTimes); + pw.printf(accountFormat, stats.name, timesStr, timeStr); } pw.println(separator); } @@ -1342,9 +1340,8 @@ public class SyncManager implements OnAccountsUpdateListener { pw.println(); pw.println("Recent Sync History"); final String format = " %-" + maxAccount + "s %s\n"; - String lastAuthorityName = null; - String lastAccountKey = null; - long lastEventTime = 0; + final Map<String, Long> lastTimeMap = Maps.newHashMap(); + for (int i = 0; i < N; i++) { SyncStorageEngine.SyncHistoryItem item = items.get(i); SyncStorageEngine.AuthorityInfo authority @@ -1363,21 +1360,32 @@ public class SyncManager implements OnAccountsUpdateListener { final long eventTime = item.eventTime; time.set(eventTime); - pw.printf(" #%-3d: %s %8s %5.1fs", - i + 1, - formatTime(eventTime), - SyncStorageEngine.SOURCES[item.source], - ((float) elapsedTime) / 1000); - if (authorityName.equals(lastAuthorityName) && accountKey.equals(lastAccountKey)) { - final long span = (lastEventTime - eventTime) / 1000; - pw.printf(" %02d:%02d\n", span / 60, span % 60); + final String key = authorityName + "/" + accountKey; + final Long lastEventTime = lastTimeMap.get(key); + final String diffString; + if (lastEventTime == null) { + diffString = ""; } else { - pw.printf(format, accountKey, authorityName); + final long diff = (lastEventTime - eventTime) / 1000; + if (diff < 60) { + diffString = String.valueOf(diff); + } else if (diff < 3600) { + diffString = String.format("%02d:%02d", diff / 60, diff % 60); + } else { + final long sec = diff % 3600; + diffString = String.format("%02d:%02d:%02d", + diff / 3600, sec / 60, sec % 60); + } } + lastTimeMap.put(key, eventTime); - lastAuthorityName = authorityName; - lastAccountKey = accountKey; - lastEventTime = eventTime; + pw.printf(" #%-3d: %s %8s %5.1fs %8s", + i + 1, + formatTime(eventTime), + SyncStorageEngine.SOURCES[item.source], + ((float) elapsedTime) / 1000, + diffString); + pw.printf(format, accountKey, authorityName); if (item.event != SyncStorageEngine.EVENT_STOP || item.upstreamActivity != 0 diff --git a/core/java/android/net/DhcpStateMachine.java b/core/java/android/net/DhcpStateMachine.java index 79c9395..fc6a44a 100644 --- a/core/java/android/net/DhcpStateMachine.java +++ b/core/java/android/net/DhcpStateMachine.java @@ -336,17 +336,17 @@ public class DhcpStateMachine extends StateMachine { DhcpInfoInternal dhcpInfoInternal = new DhcpInfoInternal(); if (dhcpAction == DhcpAction.START) { - Log.d(TAG, "DHCP request on " + mInterfaceName); + if (DBG) Log.d(TAG, "DHCP request on " + mInterfaceName); success = NetworkUtils.runDhcp(mInterfaceName, dhcpInfoInternal); mDhcpInfo = dhcpInfoInternal; } else if (dhcpAction == DhcpAction.RENEW) { - Log.d(TAG, "DHCP renewal on " + mInterfaceName); + if (DBG) Log.d(TAG, "DHCP renewal on " + mInterfaceName); success = NetworkUtils.runDhcpRenew(mInterfaceName, dhcpInfoInternal); dhcpInfoInternal.updateFromDhcpRequest(mDhcpInfo); } if (success) { - Log.d(TAG, "DHCP succeeded on " + mInterfaceName); + if (DBG) Log.d(TAG, "DHCP succeeded on " + mInterfaceName); long leaseDuration = dhcpInfoInternal.leaseDuration; //int to long conversion //Sanity check for renewal @@ -366,7 +366,7 @@ public class DhcpStateMachine extends StateMachine { mController.obtainMessage(CMD_POST_DHCP_ACTION, DHCP_SUCCESS, 0, dhcpInfoInternal) .sendToTarget(); } else { - Log.d(TAG, "DHCP failed on " + mInterfaceName + ": " + + Log.e(TAG, "DHCP failed on " + mInterfaceName + ": " + NetworkUtils.getDhcpError()); NetworkUtils.stopDhcp(mInterfaceName); mController.obtainMessage(CMD_POST_DHCP_ACTION, DHCP_FAILURE, 0) diff --git a/core/java/android/net/DnsPinger.java b/core/java/android/net/DnsPinger.java index 3291e6b..11acabe 100644 --- a/core/java/android/net/DnsPinger.java +++ b/core/java/android/net/DnsPinger.java @@ -22,7 +22,7 @@ import android.os.Looper; import android.os.Message; import android.os.SystemClock; import android.provider.Settings; -import android.util.Slog; +import android.util.Log; import com.android.internal.util.Protocol; @@ -51,7 +51,7 @@ import java.util.concurrent.atomic.AtomicInteger; * @hide */ public final class DnsPinger extends Handler { - private static final boolean V = true; + private static final boolean DBG = false; private static final int RECEIVE_POLL_INTERVAL_MS = 200; private static final int DNS_PORT = 53; @@ -154,7 +154,7 @@ public final class DnsPinger extends Handler { newActivePing.socket.setNetworkInterface(NetworkInterface.getByName( getCurrentLinkProperties().getInterfaceName())); } catch (Exception e) { - Slog.w(TAG,"sendDnsPing::Error binding to socket", e); + loge("sendDnsPing::Error binding to socket " + e); } newActivePing.packetId = (short) sRandom.nextInt(); @@ -165,8 +165,8 @@ public final class DnsPinger extends Handler { // Send the DNS query DatagramPacket packet = new DatagramPacket(buf, buf.length, dnsAddress, DNS_PORT); - if (V) { - Slog.v(TAG, "Sending a ping " + newActivePing.internalId + + if (DBG) { + log("Sending a ping " + newActivePing.internalId + " to " + dnsAddress.getHostAddress() + " with packetId " + newActivePing.packetId + "."); } @@ -196,15 +196,15 @@ public final class DnsPinger extends Handler { curPing.result = (int) (SystemClock.elapsedRealtime() - curPing.start); } else { - if (V) { - Slog.v(TAG, "response ID didn't match, ignoring packet"); + if (DBG) { + log("response ID didn't match, ignoring packet"); } } } catch (SocketTimeoutException e) { // A timeout here doesn't mean anything - squelsh this exception } catch (Exception e) { - if (V) { - Slog.v(TAG, "DnsPinger.pingDns got socket exception: ", e); + if (DBG) { + log("DnsPinger.pingDns got socket exception: " + e); } curPing.result = SOCKET_EXCEPTION; } @@ -244,13 +244,13 @@ public final class DnsPinger extends Handler { public List<InetAddress> getDnsList() { LinkProperties curLinkProps = getCurrentLinkProperties(); if (curLinkProps == null) { - Slog.e(TAG, "getCurLinkProperties:: LP for type" + mConnectionType + " is null!"); + loge("getCurLinkProperties:: LP for type" + mConnectionType + " is null!"); return mDefaultDns; } Collection<InetAddress> dnses = curLinkProps.getDnses(); if (dnses == null || dnses.size() == 0) { - Slog.v(TAG, "getDns::LinkProps has null dns - returning default"); + loge("getDns::LinkProps has null dns - returning default"); return mDefaultDns; } @@ -277,8 +277,8 @@ public final class DnsPinger extends Handler { } private void sendResponse(int internalId, int externalId, int responseVal) { - if(V) { - Slog.d(TAG, "Responding to packet " + internalId + + if(DBG) { + log("Responding to packet " + internalId + " externalId " + externalId + " and val " + responseVal); } @@ -304,7 +304,7 @@ public final class DnsPinger extends Handler { try { return NetworkUtils.numericToInetAddress(dns); } catch (IllegalArgumentException e) { - Slog.w(TAG, "getDefaultDns::malformed default dns address"); + loge("getDefaultDns::malformed default dns address"); return null; } } @@ -323,4 +323,12 @@ public final class DnsPinger extends Handler { 0, 1, // QTYPE, set to 1 = A (host address) 0, 1 // QCLASS, set to 1 = IN (internet) }; + + private void log(String s) { + Log.d(TAG, s); + } + + private void loge(String s) { + Log.e(TAG, s); + } } diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java index f3be39c..e554975 100644 --- a/core/java/android/net/NetworkStats.java +++ b/core/java/android/net/NetworkStats.java @@ -272,6 +272,17 @@ public class NetworkStats implements Parcelable { } /** + * Combine all values from another {@link NetworkStats} into this object. + */ + public void combineAllValues(NetworkStats another) { + NetworkStats.Entry entry = null; + for (int i = 0; i < another.size; i++) { + entry = another.getValues(i, entry); + combineValues(entry); + } + } + + /** * Find first stats index that matches the requested parameters. */ public int findIndex(String iface, int uid, int set, int tag) { @@ -456,6 +467,34 @@ public class NetworkStats implements Parcelable { return result; } + /** + * Return total statistics grouped by {@link #iface}; doesn't mutate the + * original structure. + */ + public NetworkStats groupedByIface() { + final NetworkStats stats = new NetworkStats(elapsedRealtime, 10); + + final Entry entry = new Entry(); + entry.uid = UID_ALL; + entry.set = SET_ALL; + entry.tag = TAG_NONE; + entry.operations = 0L; + + for (int i = 0; i < size; i++) { + // skip specific tags, since already counted in TAG_NONE + if (tag[i] != TAG_NONE) continue; + + entry.iface = iface[i]; + entry.rxBytes = rxBytes[i]; + entry.rxPackets = rxPackets[i]; + entry.txBytes = txBytes[i]; + entry.txPackets = txPackets[i]; + stats.combineValues(entry); + } + + return stats; + } + public void dump(String prefix, PrintWriter pw) { pw.print(prefix); pw.print("NetworkStats: elapsedRealtime="); pw.println(elapsedRealtime); diff --git a/core/java/android/os/AsyncTask.java b/core/java/android/os/AsyncTask.java index 64bba54..9dea4c4 100644 --- a/core/java/android/os/AsyncTask.java +++ b/core/java/android/os/AsyncTask.java @@ -42,6 +42,13 @@ import java.util.concurrent.atomic.AtomicInteger; * and 4 steps, called <code>onPreExecute</code>, <code>doInBackground</code>, * <code>onProgressUpdate</code> and <code>onPostExecute</code>.</p> * + * <div class="special reference"> + * <h3>Developer Guides</h3> + * <p>For more information about using tasks and threads, read the + * <a href="{@docRoot}guide/topics/fundamentals/processes-and-threads.html">Processes and + * Threads</a> developer guide.</p> + * </div> + * * <h2>Usage</h2> * <p>AsyncTask must be subclassed to be used. The subclass will override at least * one method ({@link #doInBackground}), and most often will override a diff --git a/core/java/android/os/Bundle.java b/core/java/android/os/Bundle.java index c288f8a..28206b7 100644 --- a/core/java/android/os/Bundle.java +++ b/core/java/android/os/Bundle.java @@ -54,6 +54,7 @@ public final class Bundle implements Parcelable, Cloneable { private boolean mHasFds = false; private boolean mFdsKnown = true; + private boolean mAllowFds = true; /** * The ClassLoader used when unparcelling data from mParcelledData. @@ -186,7 +187,14 @@ public final class Bundle implements Parcelable, Cloneable { public ClassLoader getClassLoader() { return mClassLoader; } - + + /** @hide */ + public boolean setAllowFds(boolean allowFds) { + boolean orig = mAllowFds; + mAllowFds = allowFds; + return orig; + } + /** * Clones the current Bundle. The internal map is cloned, but the keys and * values to which it refers are copied by reference. @@ -1589,24 +1597,29 @@ public final class Bundle implements Parcelable, Cloneable { * @param parcel The parcel to copy this bundle to. */ public void writeToParcel(Parcel parcel, int flags) { - if (mParcelledData != null) { - int length = mParcelledData.dataSize(); - parcel.writeInt(length); - parcel.writeInt(0x4C444E42); // 'B' 'N' 'D' 'L' - parcel.appendFrom(mParcelledData, 0, length); - } else { - parcel.writeInt(-1); // dummy, will hold length - parcel.writeInt(0x4C444E42); // 'B' 'N' 'D' 'L' - - int oldPos = parcel.dataPosition(); - parcel.writeMapInternal(mMap); - int newPos = parcel.dataPosition(); - - // Backpatch length - parcel.setDataPosition(oldPos - 8); - int length = newPos - oldPos; - parcel.writeInt(length); - parcel.setDataPosition(newPos); + final boolean oldAllowFds = parcel.pushAllowFds(mAllowFds); + try { + if (mParcelledData != null) { + int length = mParcelledData.dataSize(); + parcel.writeInt(length); + parcel.writeInt(0x4C444E42); // 'B' 'N' 'D' 'L' + parcel.appendFrom(mParcelledData, 0, length); + } else { + parcel.writeInt(-1); // dummy, will hold length + parcel.writeInt(0x4C444E42); // 'B' 'N' 'D' 'L' + + int oldPos = parcel.dataPosition(); + parcel.writeMapInternal(mMap); + int newPos = parcel.dataPosition(); + + // Backpatch length + parcel.setDataPosition(oldPos - 8); + int length = newPos - oldPos; + parcel.writeInt(length); + parcel.setDataPosition(newPos); + } + } finally { + parcel.restoreAllowFds(oldAllowFds); } } diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java index e9ed676..15e3af4 100644 --- a/core/java/android/os/Parcel.java +++ b/core/java/android/os/Parcel.java @@ -323,6 +323,12 @@ public final class Parcel { */ public final native void setDataCapacity(int size); + /** @hide */ + public final native boolean pushAllowFds(boolean allowFds); + + /** @hide */ + public final native void restoreAllowFds(boolean lastValue); + /** * Returns the raw bytes of the parcel. * diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java index 886edaf..6d14dfc 100644 --- a/core/java/android/provider/CallLog.java +++ b/core/java/android/provider/CallLog.java @@ -238,6 +238,14 @@ public class CallLog { public static final String CACHED_PHOTO_ID = "photo_id"; /** + * The cached formatted phone number. + * This value is not guaranteed to be present. + * <P>Type: TEXT</P> + * @hide + */ + public static final String CACHED_FORMATTED_NUMBER = "formatted_number"; + + /** * Adds a call to the call log. * * @param ci the CallerInfo object to get the target contact from. Can be null diff --git a/core/java/android/server/BluetoothAdapterStateMachine.java b/core/java/android/server/BluetoothAdapterStateMachine.java index d26364e..da7c489 100644 --- a/core/java/android/server/BluetoothAdapterStateMachine.java +++ b/core/java/android/server/BluetoothAdapterStateMachine.java @@ -77,6 +77,12 @@ final class BluetoothAdapterStateMachine extends StateMachine { static final int PER_PROCESS_TURN_ON = 3; static final int PER_PROCESS_TURN_OFF = 4; + // Turn on Bluetooth Module, Load firmware, and do all the preparation + // needed to get the Bluetooth Module ready but keep it not discoverable + // and not connectable. This way the Bluetooth Module can be quickly + // switched on if needed + static final int TURN_HOT = 5; + // Message(what) to report a event that the state machine need to respond to // // Event indicates sevice records have been loaded @@ -94,23 +100,18 @@ final class BluetoothAdapterStateMachine extends StateMachine { // private internal messages // - // Turn on Bluetooth Module, Load firmware, and do all the preparation - // needed to get the Bluetooth Module ready but keep it not discoverable - // and not connectable. This way the Bluetooth Module can be quickly - // switched on if needed - private static final int TURN_HOT = 101; // USER_TURN_ON is changed to TURN_ON_CONTINUE after we broadcast the // state change intent so that we will not broadcast the intent again in // other state - private static final int TURN_ON_CONTINUE = 102; + private static final int TURN_ON_CONTINUE = 101; // Unload firmware, turning off Bluetooth module power - private static final int TURN_COLD = 103; + private static final int TURN_COLD = 102; // Device disconnecting timeout happens - private static final int DEVICES_DISCONNECT_TIMEOUT = 104; + private static final int DEVICES_DISCONNECT_TIMEOUT = 103; // Prepare Bluetooth timeout happens - private static final int PREPARE_BLUETOOTH_TIMEOUT = 105; + private static final int PREPARE_BLUETOOTH_TIMEOUT = 104; // Bluetooth Powerdown timeout happens - private static final int POWER_DOWN_TIMEOUT = 106; + private static final int POWER_DOWN_TIMEOUT = 105; private Context mContext; private BluetoothService mBluetoothService; @@ -156,11 +157,6 @@ final class BluetoothAdapterStateMachine extends StateMachine { setInitialState(mPowerOff); mPublicState = BluetoothAdapter.STATE_OFF; - - if (mContext.getResources().getBoolean - (com.android.internal.R.bool.config_bluetooth_adapter_quick_switch)) { - sendMessage(TURN_HOT); - } } /** diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java index 63da926..9ca5847 100755 --- a/core/java/android/server/BluetoothService.java +++ b/core/java/android/server/BluetoothService.java @@ -313,6 +313,10 @@ public class BluetoothService extends IBluetooth.Stub { mAdapter = BluetoothAdapter.getDefaultAdapter(); mBluetoothState = new BluetoothAdapterStateMachine(mContext, this, mAdapter); mBluetoothState.start(); + if (mContext.getResources().getBoolean + (com.android.internal.R.bool.config_bluetooth_adapter_quick_switch)) { + mBluetoothState.sendMessage(BluetoothAdapterStateMachine.TURN_HOT); + } mEventLoop = mBluetoothState.getBluetoothEventLoop(); } diff --git a/core/java/android/view/ActionMode.java b/core/java/android/view/ActionMode.java index e954983..34e7d4d 100644 --- a/core/java/android/view/ActionMode.java +++ b/core/java/android/view/ActionMode.java @@ -153,6 +153,18 @@ public abstract class ActionMode { public abstract MenuInflater getMenuInflater(); /** + * Returns whether the UI presenting this action mode can take focus or not. + * This is used by internal components within the framework that would otherwise + * present an action mode UI that requires focus, such as an EditText as a custom view. + * + * @return true if the UI used to show this action mode can take focus + * @hide Internal use only + */ + public boolean isUiFocusable() { + return true; + } + + /** * Callback interface for action modes. Supplied to * {@link View#startActionMode(Callback)}, a Callback * configures and handles events raised by a user's interaction with an action mode. diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index d193d6e..8e5aefd 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -94,20 +94,10 @@ import java.util.concurrent.CopyOnWriteArrayList; * their layout properties. * </p> * - * <div class="special"> - * <p>For an introduction to using this class to develop your - * application's user interface, read the Developer Guide documentation on - * <strong><a href="{@docRoot}guide/topics/ui/index.html">User Interface</a></strong>. Special topics - * include: - * <br/><a href="{@docRoot}guide/topics/ui/declaring-layout.html">Declaring Layout</a> - * <br/><a href="{@docRoot}guide/topics/ui/menus.html">Creating Menus</a> - * <br/><a href="{@docRoot}guide/topics/ui/layout-objects.html">Common Layout Objects</a> - * <br/><a href="{@docRoot}guide/topics/ui/binding.html">Binding to Data with AdapterView</a> - * <br/><a href="{@docRoot}guide/topics/ui/ui-events.html">Handling UI Events</a> - * <br/><a href="{@docRoot}guide/topics/ui/themes.html">Applying Styles and Themes</a> - * <br/><a href="{@docRoot}guide/topics/ui/custom-components.html">Building Custom Components</a> - * <br/><a href="{@docRoot}guide/topics/ui/how-android-draws.html">How Android Draws Views</a>. - * </p> + * <div class="special reference"> + * <h3>Developer Guides</h3> + * <p>For information about using this class to develop your application's user interface, + * read the <a href="{@docRoot}guide/topics/ui/index.html">User Interface</a> developer guide. * </div> * * <a name="Using"></a> @@ -1487,6 +1477,18 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal } /** + * Accessibility event types that are dispatched for text population. + */ + private static final int POPULATING_ACCESSIBILITY_EVENT_TYPES = + AccessibilityEvent.TYPE_VIEW_CLICKED + | AccessibilityEvent.TYPE_VIEW_LONG_CLICKED + | AccessibilityEvent.TYPE_VIEW_SELECTED + | AccessibilityEvent.TYPE_VIEW_FOCUSED + | AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED + | AccessibilityEvent.TYPE_VIEW_HOVER_ENTER + | AccessibilityEvent.TYPE_VIEW_HOVER_EXIT; + + /** * Temporary Rect currently for use in setBackground(). This will probably * be extended in the future to hold our own class with more than just * a Rect. :) @@ -3855,7 +3857,10 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal return; } onInitializeAccessibilityEvent(event); - dispatchPopulateAccessibilityEvent(event); + // Only a subset of accessibility events populates text content. + if ((event.getEventType() & POPULATING_ACCESSIBILITY_EVENT_TYPES) != 0) { + dispatchPopulateAccessibilityEvent(event); + } // In the beginning we called #isShown(), so we know that getParent() is not null. getParent().requestSendAccessibilityEvent(this, event); } @@ -3876,6 +3881,10 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * {@link AccessibilityDelegate#dispatchPopulateAccessibilityEvent(View, AccessibilityEvent)} * is responsible for handling this call. * </p> + * <p> + * <em>Note:</em> Accessibility events of certain types are not dispatched for + * populating the event text via this method. For details refer to {@link AccessibilityEvent}. + * </p> * * @param event The event. * @@ -3895,12 +3904,6 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * Note: Called from the default {@link AccessibilityDelegate}. */ boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event) { - // Do not populate text to scroll events. They describe position change - // and usually come from container with a lot of text which is not very - // informative for accessibility purposes. Also they are fired frequently. - if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_SCROLLED) { - return true; - } onPopulateAccessibilityEvent(event); return false; } @@ -12093,6 +12096,39 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal } /** + * Finds a view by its unuque and stable accessibility id. + * + * @param accessibilityId The searched accessibility id. + * @return The found view. + */ + final View findViewByAccessibilityId(int accessibilityId) { + if (accessibilityId < 0) { + return null; + } + return findViewByAccessibilityIdTraversal(accessibilityId); + } + + /** + * Performs the traversal to find a view by its unuque and stable accessibility id. + * + * <strong>Note:</strong>This method does not stop at the root namespace + * boundary since the user can touch the screen at an arbitrary location + * potentially crossing the root namespace bounday which will send an + * accessibility event to accessibility services and they should be able + * to obtain the event source. Also accessibility ids are guaranteed to be + * unique in the window. + * + * @param accessibilityId The accessibility id. + * @return The found view. + */ + View findViewByAccessibilityIdTraversal(int accessibilityId) { + if (getAccessibilityViewId() == accessibilityId) { + return this; + } + return null; + } + + /** * Look for a child view with the given tag. If this view has the given * tag, return this view. * diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index fb0d80a..5b4a6f8 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -821,6 +821,24 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } } + @Override + View findViewByAccessibilityIdTraversal(int accessibilityId) { + View foundView = super.findViewByAccessibilityIdTraversal(accessibilityId); + if (foundView != null) { + return foundView; + } + final int childrenCount = mChildrenCount; + final View[] children = mChildren; + for (int i = 0; i < childrenCount; i++) { + View child = children[i]; + foundView = child.findViewByAccessibilityIdTraversal(accessibilityId); + if (foundView != null) { + return foundView; + } + } + return null; + } + /** * {@inheritDoc} */ diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 9cb4e5e..e7c91f9 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -74,7 +74,6 @@ import android.view.inputmethod.InputMethodManager; import android.widget.Scroller; import com.android.internal.policy.PolicyManager; -import com.android.internal.util.Predicate; import com.android.internal.view.BaseSurfaceHolder; import com.android.internal.view.IInputMethodCallback; import com.android.internal.view.IInputMethodSession; @@ -4462,9 +4461,6 @@ public final class ViewRootImpl extends Handler implements ViewParent, final class AccessibilityInteractionController { private static final int POOL_SIZE = 5; - private FindByAccessibilitytIdPredicate mFindByAccessibilityIdPredicate = - new FindByAccessibilitytIdPredicate(); - private ArrayList<AccessibilityNodeInfo> mTempAccessibilityNodeInfoList = new ArrayList<AccessibilityNodeInfo>(); @@ -4551,11 +4547,8 @@ public final class ViewRootImpl extends Handler implements ViewParent, AccessibilityNodeInfo info = null; try { - FindByAccessibilitytIdPredicate predicate = mFindByAccessibilityIdPredicate; - predicate.init(accessibilityId); - View root = ViewRootImpl.this.mView; - View target = root.findViewByPredicate(predicate); - if (target != null && target.getVisibility() == View.VISIBLE) { + View target = findViewByAccessibilityId(accessibilityId); + if (target != null) { info = target.createAccessibilityNodeInfo(); } } finally { @@ -4794,25 +4787,12 @@ public final class ViewRootImpl extends Handler implements ViewParent, if (root == null) { return null; } - mFindByAccessibilityIdPredicate.init(accessibilityId); - View foundView = root.findViewByPredicate(mFindByAccessibilityIdPredicate); - if (foundView == null || foundView.getVisibility() != View.VISIBLE) { + View foundView = root.findViewByAccessibilityId(accessibilityId); + if (foundView != null && foundView.getVisibility() != View.VISIBLE) { return null; } return foundView; } - - private final class FindByAccessibilitytIdPredicate implements Predicate<View> { - public int mSearchedId; - - public void init(int searchedId) { - mSearchedId = searchedId; - } - - public boolean apply(View view) { - return (view.getAccessibilityViewId() == mSearchedId); - } - } } private class SendWindowContentChangedAccessibilityEvent implements Runnable { @@ -4820,18 +4800,7 @@ public final class ViewRootImpl extends Handler implements ViewParent, public void run() { if (mView != null) { - // Check again for accessibility state since this is executed delayed. - AccessibilityManager accessibilityManager = - AccessibilityManager.getInstance(mView.mContext); - if (accessibilityManager.isEnabled()) { - // Send the event directly since we do not want to append the - // source text because this is the text for the entire window - // and we just want to notify that the content has changed. - AccessibilityEvent event = AccessibilityEvent.obtain( - AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED); - mView.onInitializeAccessibilityEvent(event); - accessibilityManager.sendAccessibilityEvent(event); - } + mView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED); mIsPending = false; } } diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java index c93b564..91fbb0e 100644 --- a/core/java/android/view/accessibility/AccessibilityEvent.java +++ b/core/java/android/view/accessibility/AccessibilityEvent.java @@ -69,14 +69,16 @@ import java.util.List; * <em>Type:</em>{@link #TYPE_VIEW_CLICKED}</br> * <em>Properties:</em></br> * <ul> + * <li>{@link #getEventType()} - The type of the event.</li> * <li>{@link #getSource()} - The source info (for registered clients).</li> * <li>{@link #getClassName()} - The class name of the source.</li> * <li>{@link #getPackageName()} - The package name of the source.</li> * <li>{@link #getEventTime()} - The event time.</li> - * <li>{@link #getText()} - The text of the source.</li> + * <li>{@link #getText()} - The text of the source's sub-tree.</li> * <li>{@link #isEnabled()} - Whether the source is enabled.</li> * <li>{@link #isPassword()} - Whether the source is password.</li> * <li>{@link #isChecked()} - Whether the source is checked.</li> + * <li>{@link #getContentDescription()} - The content description of the source.</li> * </ul> * </p> * <p> @@ -85,14 +87,16 @@ import java.util.List; * <em>Type:</em>{@link #TYPE_VIEW_LONG_CLICKED}</br> * <em>Properties:</em></br> * <ul> + * <li>{@link #getEventType()} - The type of the event.</li> * <li>{@link #getSource()} - The source info (for registered clients).</li> * <li>{@link #getClassName()} - The class name of the source.</li> * <li>{@link #getPackageName()} - The package name of the source.</li> * <li>{@link #getEventTime()} - The event time.</li> - * <li>{@link #getText()} - The text of the source.</li> + * <li>{@link #getText()} - The text of the source's sub-tree.</li> * <li>{@link #isEnabled()} - Whether the source is enabled.</li> * <li>{@link #isPassword()} - Whether the source is password.</li> * <li>{@link #isChecked()} - Whether the source is checked.</li> + * <li>{@link #getContentDescription()} - The content description of the source.</li> * </ul> * </p> * <p> @@ -101,16 +105,18 @@ import java.util.List; * <em>Type:</em> {@link #TYPE_VIEW_SELECTED}</br> * <em>Properties:</em></br> * <ul> + * <li>{@link #getEventType()} - The type of the event.</li> * <li>{@link #getSource()} - The source info (for registered clients).</li> * <li>{@link #getClassName()} - The class name of the source.</li> * <li>{@link #getPackageName()} - The package name of the source.</li> * <li>{@link #getEventTime()} - The event time.</li> - * <li>{@link #getText()} - The text of the source.</li> + * <li>{@link #getText()} - The text of the source's sub-tree.</li> * <li>{@link #isEnabled()} - Whether the source is enabled.</li> * <li>{@link #isPassword()} - Whether the source is password.</li> * <li>{@link #isChecked()} - Whether the source is checked.</li> * <li>{@link #getItemCount()} - The number of selectable items of the source.</li> * <li>{@link #getCurrentItemIndex()} - The currently selected item index.</li> + * <li>{@link #getContentDescription()} - The content description of the source.</li> * </ul> * </p> * <p> @@ -119,16 +125,18 @@ import java.util.List; * <em>Type:</em> {@link #TYPE_VIEW_FOCUSED}</br> * <em>Properties:</em></br> * <ul> + * <li>{@link #getEventType()} - The type of the event.</li> * <li>{@link #getSource()} - The source info (for registered clients).</li> * <li>{@link #getClassName()} - The class name of the source.</li> * <li>{@link #getPackageName()} - The package name of the source.</li> * <li>{@link #getEventTime()} - The event time.</li> - * <li>{@link #getText()} - The text of the source.</li> + * <li>{@link #getText()} - The text of the source's sub-tree.</li> * <li>{@link #isEnabled()} - Whether the source is enabled.</li> * <li>{@link #isPassword()} - Whether the source is password.</li> * <li>{@link #isChecked()} - Whether the source is checked.</li> * <li>{@link #getItemCount()} - The number of focusable items on the screen.</li> * <li>{@link #getCurrentItemIndex()} - The currently focused item index.</li> + * <li>{@link #getContentDescription()} - The content description of the source.</li> * </ul> * </p> * <p> @@ -137,6 +145,7 @@ import java.util.List; * <em>Type:</em> {@link #TYPE_VIEW_TEXT_CHANGED}</br> * <em>Properties:</em></br> * <ul> + * <li>{@link #getEventType()} - The type of the event.</li> * <li>{@link #getSource()} - The source info (for registered clients).</li> * <li>{@link #getClassName()} - The class name of the source.</li> * <li>{@link #getPackageName()} - The package name of the source.</li> @@ -149,7 +158,17 @@ import java.util.List; * <li>{@link #getAddedCount()} - The number of added characters.</li> * <li>{@link #getRemovedCount()} - The number of removed characters.</li> * <li>{@link #getBeforeText()} - The text of the source before the change.</li> + * <li>{@link #getContentDescription()} - The content description of the source.</li> * </ul> + * <em>Note:</em> This event type is not dispatched to descendants though + * {@link android.view.View#dispatchPopulateAccessibilityEvent(AccessibilityEvent) + * View.dispatchPopulateAccessibilityEvent(AccessibilityEvent)}, hence the event + * source {@link android.view.View} and the sub-tree rooted at it will not receive + * calls to {@link android.view.View#onPopulateAccessibilityEvent(AccessibilityEvent) + * View.onPopulateAccessibilityEvent(AccessibilityEvent)}. The preferred way to add + * text content to such events is by setting the + * {@link android.R.styleable#View_contentDescription contentDescription} of the source + * view.</br> * </p> * <p> * <b>View text selection changed</b> - represents the event of changing the text @@ -157,35 +176,47 @@ import java.util.List; * <em>Type:</em> {@link #TYPE_VIEW_TEXT_SELECTION_CHANGED} </br> * <em>Properties:</em></br> * <ul> + * <li>{@link #getEventType()} - The type of the event.</li> * <li>{@link #getSource()} - The source info (for registered clients).</li> * <li>{@link #getClassName()} - The class name of the source.</li> * <li>{@link #getPackageName()} - The package name of the source.</li> * <li>{@link #getEventTime()} - The event time.</li> * <li>{@link #getText()} - The text of the source.</li> - * <li>{@link #isEnabled()} - Whether the source is enabled.</li> * <li>{@link #isPassword()} - Whether the source is password.</li> * <li>{@link #getFromIndex()} - The selection start index.</li> * <li>{@link #getToIndex()} - The selection end index.</li> * <li>{@link #getItemCount()} - The length of the source text.</li> + * <li>{@link #isEnabled()} - Whether the source is enabled.</li> + * <li>{@link #getContentDescription()} - The content description of the source.</li> * </ul> + * <em>Note:</em> This event type is not dispatched to descendants though + * {@link android.view.View#dispatchPopulateAccessibilityEvent(AccessibilityEvent) + * View.dispatchPopulateAccessibilityEvent(AccessibilityEvent)}, hence the event + * source {@link android.view.View} and the sub-tree rooted at it will not receive + * calls to {@link android.view.View#onPopulateAccessibilityEvent(AccessibilityEvent) + * View.onPopulateAccessibilityEvent(AccessibilityEvent)}. The preferred way to add + * text content to such events is by setting the + * {@link android.R.styleable#View_contentDescription contentDescription} of the source + * view.</br> * </p> * <p> * <b>View scrolled</b> - represents the event of scrolling a view. If * the source is a descendant of {@link android.widget.AdapterView} the * scroll is reported in terms of visible items - the first visible item, * the last visible item, and the total items - because the the source - * is unaware if its pixel size since its adapter is responsible for + * is unaware of its pixel size since its adapter is responsible for * creating views. In all other cases the scroll is reported as the current * scroll on the X and Y axis respectively plus the height of the source in * pixels.</br> * <em>Type:</em> {@link #TYPE_VIEW_SCROLLED}</br> * <em>Properties:</em></br> * <ul> + * <li>{@link #getEventType()} - The type of the event.</li> * <li>{@link #getSource()} - The source info (for registered clients).</li> * <li>{@link #getClassName()} - The class name of the source.</li> * <li>{@link #getPackageName()} - The package name of the source.</li> * <li>{@link #getEventTime()} - The event time.</li> - * <li>{@link #getText()} - The text of the source.</li> + * <li>{@link #getText()} - The text of the source's sub-tree.</li> * <li>{@link #isEnabled()} - Whether the source is enabled.</li> * <li>{@link #getScrollX()} - The horizontal offset of the source * (without descendants of AdapterView)).</li> @@ -197,56 +228,165 @@ import java.util.List; * (for descendants of AdapterView).</li> * <li>{@link #getItemCount()} - The total items of the source (for descendants of AdapterView) * or the height of the source in pixels (all other cases).</li> + * <li>{@link #getText()} - Text for providing more context.</li> + * <li>{@link #getContentDescription()} - The content description of the source.</li> * </ul> + * <em>Note:</em> This event type is not dispatched to descendants though + * {@link android.view.View#dispatchPopulateAccessibilityEvent(AccessibilityEvent) + * View.dispatchPopulateAccessibilityEvent(AccessibilityEvent)}, hence the event + * source {@link android.view.View} and the sub-tree rooted at it will not receive + * calls to {@link android.view.View#onPopulateAccessibilityEvent(AccessibilityEvent) + * View.onPopulateAccessibilityEvent(AccessibilityEvent)}. The preferred way to add + * text content to such events is by setting the + * {@link android.R.styleable#View_contentDescription contentDescription} of the source + * view.</br> * </p> * <p> * <b>TRANSITION TYPES</b></br> * </p> + * <p> * <b>Window state changed</b> - represents the event of opening a * {@link android.widget.PopupWindow}, {@link android.view.Menu}, * {@link android.app.Dialog}, etc.</br> * <em>Type:</em> {@link #TYPE_WINDOW_STATE_CHANGED}</br> * <em>Properties:</em></br> * <ul> + * <li>{@link #getEventType()} - The type of the event.</li> * <li>{@link #getSource()} - The source info (for registered clients).</li> * <li>{@link #getClassName()} - The class name of the source.</li> * <li>{@link #getPackageName()} - The package name of the source.</li> * <li>{@link #getEventTime()} - The event time.</li> - * <li>{@link #getText()} - The text of the source.</li> + * <li>{@link #getText()} - The text of the source's sub-tree.</li> + * <li>{@link #isEnabled()} - Whether the source is enabled.</li> * </ul> * </p> * <p> * <b>Window content changed</b> - represents the event of change in the * content of a window. This change can be adding/removing view, changing * a view size, etc.</br> + * </p> * <p> * <strong>Note:</strong> This event is fired only for the window source of the - * last accessibility event different from {@link #TYPE_NOTIFICATION_STATE_CHANGED}) + * last accessibility event different from {@link #TYPE_NOTIFICATION_STATE_CHANGED} * and its purpose is to notify clients that the content of the user interaction - * window has changed. - * </p> + * window has changed.</br> * <em>Type:</em> {@link #TYPE_WINDOW_CONTENT_CHANGED}</br> * <em>Properties:</em></br> * <ul> + * <li>{@link #getEventType()} - The type of the event.</li> * <li>{@link #getSource()} - The source info (for registered clients).</li> * <li>{@link #getClassName()} - The class name of the source.</li> * <li>{@link #getPackageName()} - The package name of the source.</li> * <li>{@link #getEventTime()} - The event time.</li> * </ul> + * <em>Note:</em> This event type is not dispatched to descendants though + * {@link android.view.View#dispatchPopulateAccessibilityEvent(AccessibilityEvent) + * View.dispatchPopulateAccessibilityEvent(AccessibilityEvent)}, hence the event + * source {@link android.view.View} and the sub-tree rooted at it will not receive + * calls to {@link android.view.View#onPopulateAccessibilityEvent(AccessibilityEvent) + * View.onPopulateAccessibilityEvent(AccessibilityEvent)}. The preferred way to add + * text content to such events is by setting the + * {@link android.R.styleable#View_contentDescription contentDescription} of the source + * view.</br> + * </p> * <p> * <b>NOTIFICATION TYPES</b></br> + * </p> * <p> * <b>Notification state changed</b> - represents the event showing - * {@link android.app.Notification}. + * {@link android.app.Notification}.</br> * <em>Type:</em> {@link #TYPE_NOTIFICATION_STATE_CHANGED}</br> * <em>Properties:</em></br> * <ul> + * <li>{@link #getEventType()} - The type of the event.</li> * <li>{@link #getClassName()} - The class name of the source.</li> * <li>{@link #getPackageName()} - The package name of the source.</li> * <li>{@link #getEventTime()} - The event time.</li> - * <li>{@link #getText()} - The text of the source.</li> + * <li>{@link #getText()} - The text of the source's sub-tree.</li> * <li>{@link #getParcelableData()} - The posted {@link android.app.Notification}.</li> + * <li>{@link #getText()} - Text for providing more context.</li> * </ul> + * <em>Note:</em> This event type is not dispatched to descendants though + * {@link android.view.View#dispatchPopulateAccessibilityEvent(AccessibilityEvent) + * View.dispatchPopulateAccessibilityEvent(AccessibilityEvent)}, hence the event + * source {@link android.view.View} and the sub-tree rooted at it will not receive + * calls to {@link android.view.View#onPopulateAccessibilityEvent(AccessibilityEvent) + * View.onPopulateAccessibilityEvent(AccessibilityEvent)}. The preferred way to add + * text content to such events is by setting the + * {@link android.R.styleable#View_contentDescription contentDescription} of the source + * view.</br> + * </p> + * <p> + * <b>EXPLORATION TYPES</b></br> + * </p> + * <p> + * <b>View hover enter</b> - represents the event of beginning to hover + * over a {@link android.view.View}. The hover may be generated via + * exploring the screen by touch or via a pointing device.</br> + * <em>Type:</em> {@link #TYPE_VIEW_HOVER_ENTER}</br> + * <em>Properties:</em></br> + * <ul> + * <li>{@link #getEventType()} - The type of the event.</li> + * <li>{@link #getSource()} - The source info (for registered clients).</li> + * <li>{@link #getClassName()} - The class name of the source.</li> + * <li>{@link #getPackageName()} - The package name of the source.</li> + * <li>{@link #getEventTime()} - The event time.</li> + * <li>{@link #getText()} - The text of the source's sub-tree.</li> + * <li>{@link #isEnabled()} - Whether the source is enabled.</li> + * <li>{@link #getContentDescription()} - The content description of the source.</li> + * </ul> + * </p> + * <b>View hover exit</b> - represents the event of stopping to hover + * over a {@link android.view.View}. The hover may be generated via + * exploring the screen by touch or via a pointing device.</br> + * <em>Type:</em> {@link #TYPE_VIEW_HOVER_EXIT}</br> + * <em>Properties:</em></br> + * <ul> + * <li>{@link #getEventType()} - The type of the event.</li> + * <li>{@link #getSource()} - The source info (for registered clients).</li> + * <li>{@link #getClassName()} - The class name of the source.</li> + * <li>{@link #getPackageName()} - The package name of the source.</li> + * <li>{@link #getEventTime()} - The event time.</li> + * <li>{@link #getText()} - The text of the source's sub-tree.</li> + * <li>{@link #isEnabled()} - Whether the source is enabled.</li> + * <li>{@link #getContentDescription()} - The content description of the source.</li> + * </ul> + * </p> + * <p> + * <b>Touch exploration gesture start</b> - represents the event of starting a touch + * exploring gesture.</br> + * <em>Type:</em> {@link #TYPE_TOUCH_EXPLORATION_GESTURE_START}</br> + * <em>Properties:</em></br> + * <ul> + * <li>{@link #getEventType()} - The type of the event.</li> + * </ul> + * <em>Note:</em> This event type is not dispatched to descendants though + * {@link android.view.View#dispatchPopulateAccessibilityEvent(AccessibilityEvent) + * View.dispatchPopulateAccessibilityEvent(AccessibilityEvent)}, hence the event + * source {@link android.view.View} and the sub-tree rooted at it will not receive + * calls to {@link android.view.View#onPopulateAccessibilityEvent(AccessibilityEvent) + * View.onPopulateAccessibilityEvent(AccessibilityEvent)}. The preferred way to add + * text content to such events is by setting the + * {@link android.R.styleable#View_contentDescription contentDescription} of the source + * view.</br> + * </p> + * <p> + * <b>Touch exploration gesture end</b> - represents the event of ending a touch + * exploring gesture.</br> + * <em>Type:</em> {@link #TYPE_TOUCH_EXPLORATION_GESTURE_END}</br> + * <em>Properties:</em></br> + * <ul> + * <li>{@link #getEventType()} - The type of the event.</li> + * </ul> + * <em>Note:</em> This event type is not dispatched to descendants though + * {@link android.view.View#dispatchPopulateAccessibilityEvent(AccessibilityEvent) + * View.dispatchPopulateAccessibilityEvent(AccessibilityEvent)}, hence the event + * source {@link android.view.View} and the sub-tree rooted at it will not receive + * calls to {@link android.view.View#onPopulateAccessibilityEvent(AccessibilityEvent) + * View.onPopulateAccessibilityEvent(AccessibilityEvent)}. The preferred way to add + * text content to such events is by setting the + * {@link android.R.styleable#View_contentDescription contentDescription} of the source + * view.</br> * </p> * <p> * <b>Security note</b> @@ -254,6 +394,7 @@ import java.util.List; * Since an event contains the text of its source privacy can be compromised by leaking * sensitive information such as passwords. To address this issue any event fired in response * to manipulation of a PASSWORD field does NOT CONTAIN the text of the password. + * </p> * * @see android.view.accessibility.AccessibilityManager * @see android.accessibilityservice.AccessibilityService diff --git a/core/java/android/webkit/FindActionModeCallback.java b/core/java/android/webkit/FindActionModeCallback.java index a322fa3..7398262 100644 --- a/core/java/android/webkit/FindActionModeCallback.java +++ b/core/java/android/webkit/FindActionModeCallback.java @@ -180,6 +180,14 @@ class FindActionModeCallback implements ActionMode.Callback, TextWatcher, @Override public boolean onCreateActionMode(ActionMode mode, Menu menu) { + if (!mode.isUiFocusable()) { + // If the action mode we're running in is not focusable the user + // will not be able to type into the find on page field. This + // should only come up when we're running in a dialog which is + // already less than ideal; disable the option for now. + return false; + } + mode.setCustomView(mCustomView); mode.getMenuInflater().inflate(com.android.internal.R.menu.webview_find, menu); diff --git a/core/java/android/webkit/SelectActionModeCallback.java b/core/java/android/webkit/SelectActionModeCallback.java index 104deb1..8c174aa 100644 --- a/core/java/android/webkit/SelectActionModeCallback.java +++ b/core/java/android/webkit/SelectActionModeCallback.java @@ -17,13 +17,12 @@ package android.webkit; import android.app.SearchManager; +import android.content.Context; import android.content.Intent; import android.provider.Browser; -import android.webkit.WebView; import android.view.ActionMode; import android.view.Menu; import android.view.MenuItem; -import android.view.View; class SelectActionModeCallback implements ActionMode.Callback { private WebView mWebView; @@ -45,9 +44,25 @@ class SelectActionModeCallback implements ActionMode.Callback { @Override public boolean onCreateActionMode(ActionMode mode, Menu menu) { - mode.getMenuInflater().inflate(com.android.internal.R.menu.webview_copy, - menu); - mode.setTitle(com.android.internal.R.string.textSelectionCABTitle); + mode.getMenuInflater().inflate(com.android.internal.R.menu.webview_copy, menu); + + final Context context = mWebView.getContext(); + boolean allowText = context.getResources().getBoolean( + com.android.internal.R.bool.config_allowActionMenuItemTextWithIcon); + mode.setTitle(allowText ? + context.getString(com.android.internal.R.string.textSelectionCABTitle) : null); + + if (!mode.isUiFocusable()) { + // If the action mode UI we're running in isn't capable of taking window focus + // the user won't be able to type into the find on page UI. Disable this functionality. + // (Note that this should only happen in floating dialog windows.) + // This can be removed once we can handle multiple focusable windows at a time + // in a better way. + final MenuItem findOnPageItem = menu.findItem(com.android.internal.R.id.find); + if (findOnPageItem != null) { + findOnPageItem.setVisible(false); + } + } mActionMode = mode; return true; } diff --git a/core/java/android/webkit/WebTextView.java b/core/java/android/webkit/WebTextView.java index 217ad7c..092e2b5 100644 --- a/core/java/android/webkit/WebTextView.java +++ b/core/java/android/webkit/WebTextView.java @@ -1092,4 +1092,11 @@ import junit.framework.Assert; return url != null ? url.getProtocol() + "://" + url.getHost() + url.getPath() : null; } + + public void setGravityForRtl(boolean rtl) { + int gravity = rtl ? Gravity.RIGHT : Gravity.LEFT; + gravity |= mSingle ? Gravity.CENTER_VERTICAL : Gravity.TOP; + setGravity(gravity); + } + } diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 370cce4..eaed9fe 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -4078,8 +4078,8 @@ public class WebView extends AbsoluteLayout boolean pressed = (mTouchMode == TOUCH_SHORTPRESS_START_MODE || mTouchMode == TOUCH_INIT_MODE || mTouchMode == TOUCH_SHORTPRESS_MODE); - nativeRecordButtons(hasFocus() && hasWindowFocus(), - (pressed && !USE_WEBKIT_RINGS) + recordButtons(canvas, + hasFocus() && hasWindowFocus(), (pressed && !USE_WEBKIT_RINGS) || mTrackballDown || mGotCenterDown, false); drawCoreAndCursorRing(canvas, mBackgroundColor, mDrawCursorRing && drawRings); @@ -4760,12 +4760,12 @@ public class WebView extends AbsoluteLayout } String text = nativeFocusCandidateText(); int nodePointer = nativeFocusCandidatePointer(); - mWebTextView.setGravity(nativeFocusCandidateIsRtlText() ? - Gravity.RIGHT : Gravity.NO_GRAVITY); // This needs to be called before setType, which may call // requestFormData, and it needs to have the correct nodePointer. mWebTextView.setNodePointer(nodePointer); mWebTextView.setType(nativeFocusCandidateType()); + // Gravity needs to be set after setType + mWebTextView.setGravityForRtl(nativeFocusCandidateIsRtlText()); updateWebTextViewPadding(); if (null == text) { if (DebugFlags.WEB_VIEW) { @@ -5133,7 +5133,7 @@ public class WebView extends AbsoluteLayout .obtainMessage(LONG_PRESS_CENTER), LONG_PRESS_TIMEOUT); // Already checked mNativeClass, so we do not need to check it // again. - nativeRecordButtons(hasFocus() && hasWindowFocus(), true, true); + recordButtons(null, hasFocus() && hasWindowFocus(), true, true); if (!wantsKeyEvents) return true; } // Bubble up the key event as WebView doesn't handle it @@ -5561,7 +5561,7 @@ public class WebView extends AbsoluteLayout mDrawCursorRing = true; setFocusControllerActive(true); if (mNativeClass != 0) { - nativeRecordButtons(true, false, true); + recordButtons(null, true, false, true); } } else { if (!inEditingMode()) { @@ -5570,7 +5570,7 @@ public class WebView extends AbsoluteLayout mDrawCursorRing = false; setFocusControllerActive(false); } - // We do not call nativeRecordButtons here because we assume + // We do not call recordButtons here because we assume // that when we lost focus, or window focus, it got called with // false for the first parameter } @@ -5589,7 +5589,7 @@ public class WebView extends AbsoluteLayout mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS); mTouchMode = TOUCH_DONE_MODE; if (mNativeClass != 0) { - nativeRecordButtons(false, false, true); + recordButtons(null, false, false, true); } setFocusControllerActive(false); } @@ -5647,13 +5647,13 @@ public class WebView extends AbsoluteLayout if (hasWindowFocus()) { mDrawCursorRing = true; if (mNativeClass != 0) { - nativeRecordButtons(true, false, true); + recordButtons(null, true, false, true); } setFocusControllerActive(true); //} else { // The WebView has gained focus while we do not have // windowfocus. When our window lost focus, we should have - // called nativeRecordButtons(false...) + // called recordButtons(false...) } } else { // When we lost focus, unless focus went to the TextView (which is @@ -5661,7 +5661,7 @@ public class WebView extends AbsoluteLayout if (!inEditingMode()) { mDrawCursorRing = false; if (mNativeClass != 0) { - nativeRecordButtons(false, false, true); + recordButtons(null, false, false, true); } setFocusControllerActive(false); } @@ -6762,7 +6762,7 @@ public class WebView extends AbsoluteLayout if (mNativeClass == 0) { return false; } - nativeRecordButtons(hasFocus() && hasWindowFocus(), true, true); + recordButtons(null, hasFocus() && hasWindowFocus(), true, true); if (time - mLastCursorTime <= TRACKBALL_TIMEOUT && !mLastCursorBounds.equals(nativeGetCursorRingBounds())) { nativeSelectBestAt(mLastCursorBounds); @@ -9349,6 +9349,24 @@ public class WebView extends AbsoluteLayout return nativeTileProfilingGetFloat(frame, tile, key); } + /** + * Helper method to deal with differences between hardware and software rendering + */ + private void recordButtons(Canvas canvas, boolean focus, boolean pressed, + boolean inval) { + boolean isHardwareAccel = canvas != null + ? canvas.isHardwareAccelerated() + : isHardwareAccelerated(); + if (isHardwareAccel) { + // We never want to change button state if we are hardware accelerated, + // but we DO want to invalidate as necessary so that the GL ring + // can be drawn + nativeRecordButtons(false, false, inval); + } else { + nativeRecordButtons(focus, pressed, inval); + } + } + private native int nativeCacheHitFramePointer(); private native boolean nativeCacheHitIsPlugin(); private native Rect nativeCacheHitNodeBounds(); diff --git a/core/java/android/widget/ActivityChooserView.java b/core/java/android/widget/ActivityChooserView.java index c37cc52..60b24bc 100644 --- a/core/java/android/widget/ActivityChooserView.java +++ b/core/java/android/widget/ActivityChooserView.java @@ -33,6 +33,8 @@ import android.view.View; import android.view.ViewGroup; import android.view.ViewTreeObserver; import android.view.ViewTreeObserver.OnGlobalLayoutListener; +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityManager; import android.widget.ActivityChooserModel.ActivityChooserModelClient; /** @@ -169,6 +171,11 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod private boolean mIsAttachedToWindow; /** + * String resource for formatting content description of the default target. + */ + private int mDefaultActionButtonContentDescription; + + /** * Create a new instance. * * @param context The application environment. @@ -259,7 +266,7 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod * * <strong>Note:</strong> Clients would like to set this drawable * as a clue about the action the chosen activity will perform. For - * example, if share activity is to be chosen the drawable should + * example, if a share activity is to be chosen the drawable should * give a clue that sharing is to be performed. * * @param drawable The drawable. @@ -269,6 +276,21 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod } /** + * Sets the content description for the button that expands the activity + * overflow list. + * + * description as a clue about the action performed by the button. + * For example, if a share activity is to be chosen the content + * description should be something like "Share with". + * + * @param resourceId The content description resource id. + */ + public void setExpandActivityOverflowButtonContentDescription(int resourceId) { + CharSequence contentDescription = mContext.getString(resourceId); + mExpandActivityOverflowButtonImage.setContentDescription(contentDescription); + } + + /** * Set the provider hosting this view, if applicable. * @hide Internal use only */ @@ -329,6 +351,8 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod if (mProvider != null) { mProvider.subUiVisibilityChanged(true); } + popupWindow.getListView().setContentDescription(mContext.getString( + R.string.activitychooserview_choose_application)); } } @@ -431,6 +455,20 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod } /** + * Sets a content description of the default action button. This + * resource should be a string taking one formatting argument and + * will be used for formatting the content description of the button + * dynamically as the default target changes. For example, a resource + * pointing to the string "share with %1$s" will result in a content + * description "share with Bluetooth" for the Bluetooth activity. + * + * @param resourceId The resource id. + */ + public void setDefaultActionButtonContentDescription(int resourceId) { + mDefaultActionButtonContentDescription = resourceId; + } + + /** * Gets the list popup window which is lazily initialized. * * @return The popup. @@ -465,6 +503,12 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod ResolveInfo activity = mAdapter.getDefaultActivity(); PackageManager packageManager = mContext.getPackageManager(); mDefaultActivityButtonImage.setImageDrawable(activity.loadIcon(packageManager)); + if (mDefaultActionButtonContentDescription != 0) { + CharSequence label = activity.loadLabel(packageManager); + String contentDescription = mContext.getString( + mDefaultActionButtonContentDescription, label); + mDefaultActivityButton.setContentDescription(contentDescription); + } } else { mDefaultActivityButton.setVisibility(View.GONE); } diff --git a/core/java/android/widget/AdapterView.java b/core/java/android/widget/AdapterView.java index a4b4e78..61c5dd4 100644 --- a/core/java/android/widget/AdapterView.java +++ b/core/java/android/widget/AdapterView.java @@ -881,20 +881,14 @@ public abstract class AdapterView<T extends Adapter> extends ViewGroup { @Override public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { - final int eventType = event.getEventType(); - switch (eventType) { - case AccessibilityEvent.TYPE_VIEW_SCROLLED: - // Do not populate the text of scroll events. - return true; - case AccessibilityEvent.TYPE_VIEW_FOCUSED: - // This is an exceptional case which occurs when a window gets the - // focus and sends a focus event via its focused child to announce - // current focus/selection. AdapterView fires selection but not focus - // events so we change the event type here. - if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_FOCUSED) { - event.setEventType(AccessibilityEvent.TYPE_VIEW_SELECTED); - } - break; + if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_FOCUSED) { + // This is an exceptional case which occurs when a window gets the + // focus and sends a focus event via its focused child to announce + // current focus/selection. AdapterView fires selection but not focus + // events so we change the event type here. + if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_FOCUSED) { + event.setEventType(AccessibilityEvent.TYPE_VIEW_SELECTED); + } } View selectedView = getSelectedView(); diff --git a/core/java/android/widget/AnalogClock.java b/core/java/android/widget/AnalogClock.java index 84ebec3..63a0870 100644 --- a/core/java/android/widget/AnalogClock.java +++ b/core/java/android/widget/AnalogClock.java @@ -25,6 +25,7 @@ import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.drawable.Drawable; import android.os.Handler; +import android.text.format.DateUtils; import android.text.format.Time; import android.util.AttributeSet; import android.view.View; @@ -228,6 +229,8 @@ public class AnalogClock extends View { mMinutes = minute + second / 60.0f; mHour = hour + mMinutes / 60.0f; mChanged = true; + + updateContentDescription(mCalendar); } private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { @@ -243,4 +246,11 @@ public class AnalogClock extends View { invalidate(); } }; + + private void updateContentDescription(Time time) { + final int flags = DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_24HOUR; + String contentDescription = DateUtils.formatDateTime(mContext, + time.toMillis(false), flags); + setContentDescription(contentDescription); + } } diff --git a/core/java/android/widget/Gallery.java b/core/java/android/widget/Gallery.java index 3f5b571..a0eba9a 100644 --- a/core/java/android/widget/Gallery.java +++ b/core/java/android/widget/Gallery.java @@ -371,16 +371,6 @@ public class Gallery extends AbsSpinner implements GestureDetector.OnGestureList } } - @Override - public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { - // Do not append text content to scroll events they are fired frequently - // and the client has already received another event type with the text. - if (event.getEventType() != AccessibilityEvent.TYPE_VIEW_SCROLLED) { - super.dispatchPopulateAccessibilityEvent(event); - } - return false; - } - /** * Tracks a motion scroll. In reality, this is used to do just about any * movement to items (touch scroll, arrow-key scroll, set an item as selected). diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java index 1bbc501..324dfd7 100644 --- a/core/java/android/widget/HorizontalScrollView.java +++ b/core/java/android/widget/HorizontalScrollView.java @@ -725,16 +725,6 @@ public class HorizontalScrollView extends FrameLayout { event.setScrollable(true); } - @Override - public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { - // Do not append text content to scroll events they are fired frequently - // and the client has already received another event type with the text. - if (event.getEventType() != AccessibilityEvent.TYPE_VIEW_SCROLLED) { - super.dispatchPopulateAccessibilityEvent(event); - } - return false; - } - private int getScrollRange() { int scrollRange = 0; if (getChildCount() > 0) { diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java index 61ea5c9..3ac4e80 100644 --- a/core/java/android/widget/ScrollView.java +++ b/core/java/android/widget/ScrollView.java @@ -730,16 +730,6 @@ public class ScrollView extends FrameLayout { event.setScrollable(true); } - @Override - public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { - // Do not append text content to scroll events they are fired frequently - // and the client has already received another event type with the text. - if (event.getEventType() != AccessibilityEvent.TYPE_VIEW_SCROLLED) { - super.dispatchPopulateAccessibilityEvent(event); - } - return false; - } - private int getScrollRange() { int scrollRange = 0; if (getChildCount() > 0) { diff --git a/core/java/android/widget/ShareActionProvider.java b/core/java/android/widget/ShareActionProvider.java index 3627890..bb27b73 100644 --- a/core/java/android/widget/ShareActionProvider.java +++ b/core/java/android/widget/ShareActionProvider.java @@ -171,6 +171,12 @@ public class ShareActionProvider extends ActionProvider { activityChooserView.setExpandActivityOverflowButtonDrawable(drawable); activityChooserView.setProvider(this); + // Set content description. + activityChooserView.setDefaultActionButtonContentDescription( + R.string.shareactionprovider_share_with_application); + activityChooserView.setExpandActivityOverflowButtonContentDescription( + R.string.shareactionprovider_share_with); + return activityChooserView; } diff --git a/core/java/android/widget/SpellChecker.java b/core/java/android/widget/SpellChecker.java index ac9535a..ce17184 100644 --- a/core/java/android/widget/SpellChecker.java +++ b/core/java/android/widget/SpellChecker.java @@ -30,8 +30,6 @@ import android.view.textservice.TextServicesManager; import com.android.internal.util.ArrayUtils; -import java.util.Locale; - /** * Helper class for TextView. Bridge between the TextView and the Dictionnary service. @@ -174,8 +172,6 @@ public class SpellChecker implements SpellCheckerSessionListener { final int sequenceNumber = suggestionsInfo.getSequence(); for (int j = 0; j < mLength; j++) { - final SpellCheckSpan spellCheckSpan = mSpellCheckSpans[j]; - if (sequenceNumber == mIds[j]) { final int attributes = suggestionsInfo.getSuggestionsAttributes(); boolean isInDictionary = @@ -183,32 +179,79 @@ public class SpellChecker implements SpellCheckerSessionListener { boolean looksLikeTypo = ((attributes & SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_TYPO) > 0); + SpellCheckSpan spellCheckSpan = mSpellCheckSpans[j]; if (!isInDictionary && looksLikeTypo) { - String[] suggestions = getSuggestions(suggestionsInfo); - SuggestionSpan suggestionSpan = new SuggestionSpan( - mTextView.getContext(), suggestions, - SuggestionSpan.FLAG_EASY_CORRECT | - SuggestionSpan.FLAG_MISSPELLED); - final int start = editable.getSpanStart(spellCheckSpan); - final int end = editable.getSpanEnd(spellCheckSpan); - editable.setSpan(suggestionSpan, start, end, - Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - // TODO limit to the word rectangle region - mTextView.invalidate(); + createMisspelledSuggestionSpan(editable, suggestionsInfo, spellCheckSpan); } editable.removeSpan(spellCheckSpan); + break; } } } } - private static String[] getSuggestions(SuggestionsInfo suggestionsInfo) { - // A negative suggestion count is possible - final int len = Math.max(0, suggestionsInfo.getSuggestionsCount()); - String[] suggestions = new String[len]; - for (int j = 0; j < len; j++) { - suggestions[j] = suggestionsInfo.getSuggestionAt(j); + private void createMisspelledSuggestionSpan(Editable editable, SuggestionsInfo suggestionsInfo, + SpellCheckSpan spellCheckSpan) { + final int start = editable.getSpanStart(spellCheckSpan); + final int end = editable.getSpanEnd(spellCheckSpan); + + // Other suggestion spans may exist on that region, with identical suggestions, filter + // them out to avoid duplicates. First, filter suggestion spans on that exact region. + SuggestionSpan[] suggestionSpans = editable.getSpans(start, end, SuggestionSpan.class); + final int length = suggestionSpans.length; + for (int i = 0; i < length; i++) { + final int spanStart = editable.getSpanStart(suggestionSpans[i]); + final int spanEnd = editable.getSpanEnd(suggestionSpans[i]); + if (spanStart != start || spanEnd != end) { + suggestionSpans[i] = null; + break; + } + } + + final int suggestionsCount = suggestionsInfo.getSuggestionsCount(); + String[] suggestions; + if (suggestionsCount <= 0) { + // A negative suggestion count is possible + suggestions = ArrayUtils.emptyArray(String.class); + } else { + int numberOfSuggestions = 0; + suggestions = new String[suggestionsCount]; + + for (int i = 0; i < suggestionsCount; i++) { + final String spellSuggestion = suggestionsInfo.getSuggestionAt(i); + if (spellSuggestion == null) break; + boolean suggestionFound = false; + + for (int j = 0; j < length && !suggestionFound; j++) { + if (suggestionSpans[j] == null) break; + + String[] suggests = suggestionSpans[j].getSuggestions(); + for (int k = 0; k < suggests.length; k++) { + if (spellSuggestion.equals(suggests[k])) { + // The suggestion is already provided by an other SuggestionSpan + suggestionFound = true; + break; + } + } + } + + if (!suggestionFound) { + suggestions[numberOfSuggestions++] = spellSuggestion; + } + } + + if (numberOfSuggestions != suggestionsCount) { + String[] newSuggestions = new String[numberOfSuggestions]; + System.arraycopy(suggestions, 0, newSuggestions, 0, numberOfSuggestions); + suggestions = newSuggestions; + } } - return suggestions; + + SuggestionSpan suggestionSpan = new SuggestionSpan(mTextView.getContext(), suggestions, + SuggestionSpan.FLAG_EASY_CORRECT | SuggestionSpan.FLAG_MISSPELLED); + editable.setSpan(suggestionSpan, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + + // TODO limit to the word rectangle region + mTextView.invalidate(); } } diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 5cd7902..17f0e05 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -353,6 +353,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // Set when this TextView gained focus with some text selected. Will start selection mode. private boolean mCreatedWithASelection = false; + // Size of the window for the word iterator, should be greater than the longest word's length + private static final int WORD_ITERATOR_WINDOW_WIDTH = 50; private WordIterator mWordIterator; private SpellChecker mSpellChecker; @@ -2937,11 +2939,19 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener Spannable sp = new SpannableString(mText); - for (ChangeWatcher cw : - sp.getSpans(0, sp.length(), ChangeWatcher.class)) { + for (ChangeWatcher cw : sp.getSpans(0, sp.length(), ChangeWatcher.class)) { sp.removeSpan(cw); } + SuggestionSpan[] suggestionSpans = sp.getSpans(0, sp.length(), SuggestionSpan.class); + for (int i = 0; i < suggestionSpans.length; i++) { + int flags = suggestionSpans[i].getFlags(); + if ((flags & SuggestionSpan.FLAG_EASY_CORRECT) != 0 + && (flags & SuggestionSpan.FLAG_MISSPELLED) != 0) { + sp.removeSpan(suggestionSpans[i]); + } + } + sp.removeSpan(mSuggestionRangeSpan); ss.text = sp; @@ -4449,7 +4459,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (mSpellChecker != null) { mSpellChecker.closeSession(); - removeMisspelledSpans(); // Forces the creation of a new SpellChecker next time this window is created. // Will handle the cases where the settings has been changed in the meantime. mSpellChecker = null; @@ -7771,7 +7780,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // Iterate over the newly added text and schedule new SpellCheckSpans while (wordStart <= shiftedEnd) { - if (wordEnd >= shiftedStart) { + if (wordEnd >= shiftedStart && wordEnd > wordStart) { // A new word has been created across the interval boundaries. Remove previous spans if (wordStart < shiftedStart && wordEnd > shiftedStart) { removeSpansAt(start, spellCheckSpans, text); @@ -8461,24 +8470,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } - /** - * Removes the suggestion spans for misspelled words. - */ - private void removeMisspelledSpans() { - if (mText instanceof Spannable) { - Spannable spannable = (Spannable) mText; - SuggestionSpan[] suggestionSpans = spannable.getSpans(0, - spannable.length(), SuggestionSpan.class); - for (int i = 0; i < suggestionSpans.length; i++) { - int flags = suggestionSpans[i].getFlags(); - if ((flags & SuggestionSpan.FLAG_EASY_CORRECT) != 0 - && (flags & SuggestionSpan.FLAG_MISSPELLED) != 0) { - spannable.removeSpan(suggestionSpans[i]); - } - } - } - } - @Override public boolean onGenericMotionEvent(MotionEvent event) { if (mMovement != null && mText instanceof Spannable && mLayout != null) { @@ -8934,10 +8925,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // If a URLSpan (web address, email, phone...) is found at that position, select it. URLSpan[] urlSpans = ((Spanned) mText).getSpans(minOffset, maxOffset, URLSpan.class); - if (urlSpans.length == 1) { - URLSpan url = urlSpans[0]; - selectionStart = ((Spanned) mText).getSpanStart(url); - selectionEnd = ((Spanned) mText).getSpanEnd(url); + if (urlSpans.length >= 1) { + URLSpan urlSpan = urlSpans[0]; + selectionStart = ((Spanned) mText).getSpanStart(urlSpan); + selectionEnd = ((Spanned) mText).getSpanEnd(urlSpan); } else { final int shift = prepareWordIterator(minOffset, maxOffset); @@ -8948,10 +8939,42 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener selectionEnd = mWordIterator.getEnd(maxOffset - shift); if (selectionEnd == BreakIterator.DONE) return false; selectionEnd += shift; + + if (selectionStart == selectionEnd) { + // Possible when the word iterator does not properly handle the text's language + long range = getCharRange(selectionStart); + selectionStart = extractRangeStartFromLong(range); + selectionEnd = extractRangeEndFromLong(range); + } } Selection.setSelection((Spannable) mText, selectionStart, selectionEnd); - return true; + return selectionEnd > selectionStart; + } + + private long getCharRange(int offset) { + final int textLength = mText.length(); + if (offset + 1 < textLength) { + final char currentChar = mText.charAt(offset); + final char nextChar = mText.charAt(offset + 1); + if (Character.isSurrogatePair(currentChar, nextChar)) { + return packRangeInLong(offset, offset + 2); + } + } + if (offset < textLength) { + return packRangeInLong(offset, offset + 1); + } + if (offset - 2 >= 0) { + final char previousChar = mText.charAt(offset - 1); + final char previousPreviousChar = mText.charAt(offset - 2); + if (Character.isSurrogatePair(previousPreviousChar, previousChar)) { + return packRangeInLong(offset - 2, offset); + } + } + if (offset - 1 >= 0) { + return packRangeInLong(offset - 1, offset); + } + return packRangeInLong(offset, offset); } int prepareWordIterator(int start, int end) { @@ -8959,9 +8982,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mWordIterator = new WordIterator(); } - final int TEXT_WINDOW_WIDTH = 50; // Should be larger than the longest word's length - final int windowStart = Math.max(0, start - TEXT_WINDOW_WIDTH); - final int windowEnd = Math.min(mText.length(), end + TEXT_WINDOW_WIDTH); + final int windowStart = Math.max(0, start - WORD_ITERATOR_WINDOW_WIDTH); + final int windowEnd = Math.min(mText.length(), end + WORD_ITERATOR_WINDOW_WIDTH); mWordIterator.setCharSequence(mText.subSequence(windowStart, windowEnd)); return windowStart; @@ -9340,7 +9362,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // Start a new selection if (!handled) { - handled = startSelectionActionMode(); + vibrate = handled = startSelectionActionMode(); } if (vibrate) { @@ -9930,7 +9952,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // Fallback on the default highlight color when the first span does not provide one mSuggestionRangeSpan.setBackgroundColor(mHighlightColor); } else { - final float BACKGROUND_TRANSPARENCY = 0.3f; + final float BACKGROUND_TRANSPARENCY = 0.4f; final int newAlpha = (int) (Color.alpha(underlineColor) * BACKGROUND_TRANSPARENCY); mSuggestionRangeSpan.setBackgroundColor( (underlineColor & 0x00FFFFFF) + (newAlpha << 24)); @@ -9956,8 +9978,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener suggestionInfo.text.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); // Add the text before and after the span. - suggestionInfo.text.insert(0, mText.subSequence(unionStart, spanStart).toString()); - suggestionInfo.text.append(mText.subSequence(spanEnd, unionEnd).toString()); + suggestionInfo.text.insert(0, mText.toString().substring(unionStart, spanStart)); + suggestionInfo.text.append(mText.toString().substring(spanEnd, unionEnd)); } @Override @@ -9989,14 +10011,16 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener hide(); return; } - final String originalText = mText.subSequence(spanStart, spanEnd).toString(); + final String originalText = mText.toString().substring(spanStart, spanEnd); if (suggestionInfo.suggestionIndex == ADD_TO_DICTIONARY) { Intent intent = new Intent(Settings.ACTION_USER_DICTIONARY_INSERT); intent.putExtra("word", originalText); intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK); getContext().startActivity(intent); - suggestionInfo.removeMisspelledFlag(); + // There is no way to know if the word was indeed added. Re-check. + editable.removeSpan(suggestionInfo.suggestionSpan); + updateSpellCheckSpans(spanStart, spanEnd); } else { // SuggestionSpans are removed by replace: save them before SuggestionSpan[] suggestionSpans = editable.getSpans(spanStart, spanEnd, @@ -10024,8 +10048,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (!TextUtils.isEmpty( suggestionInfo.suggestionSpan.getNotificationTargetClassName())) { InputMethodManager imm = InputMethodManager.peekInstance(); - imm.notifySuggestionPicked(suggestionInfo.suggestionSpan, originalText, - suggestionInfo.suggestionIndex); + if (imm != null) { + imm.notifySuggestionPicked(suggestionInfo.suggestionSpan, originalText, + suggestionInfo.suggestionIndex); + } } // Swap text content between actual text and Suggestion span @@ -10045,7 +10071,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } - // Move cursor at the end of the replacement word + // Move cursor at the end of the replaced word Selection.setSelection(editable, spanEnd + lengthDifference); } @@ -10174,8 +10200,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (!hasSelection()) { // There may already be a selection on device rotation - boolean currentWordSelected = selectCurrentWord(); - if (!currentWordSelected) { + if (!selectCurrentWord()) { // No word found under cursor or text selection not permitted. return false; } @@ -10256,7 +10281,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener @Override public boolean onCreateActionMode(ActionMode mode, Menu menu) { - TypedArray styledAttributes = mContext.obtainStyledAttributes(R.styleable.Theme); + TypedArray styledAttributes = mContext.obtainStyledAttributes( + com.android.internal.R.styleable.SelectionModeDrawables); boolean allowText = getContext().getResources().getBoolean( com.android.internal.R.bool.config_allowActionMenuItemTextWithIcon); @@ -10269,7 +10295,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (!allowText) { // Provide an icon, text will not be displayed on smaller screens. selectAllIconId = styledAttributes.getResourceId( - R.styleable.Theme_actionModeSelectAllDrawable, 0); + R.styleable.SelectionModeDrawables_actionModeSelectAllDrawable, 0); } menu.add(0, ID_SELECT_ALL, 0, com.android.internal.R.string.selectAll). @@ -10281,7 +10307,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (canCut()) { menu.add(0, ID_CUT, 0, com.android.internal.R.string.cut). setIcon(styledAttributes.getResourceId( - R.styleable.Theme_actionModeCutDrawable, 0)). + R.styleable.SelectionModeDrawables_actionModeCutDrawable, 0)). setAlphabeticShortcut('x'). setShowAsAction( MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); @@ -10290,7 +10316,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (canCopy()) { menu.add(0, ID_COPY, 0, com.android.internal.R.string.copy). setIcon(styledAttributes.getResourceId( - R.styleable.Theme_actionModeCopyDrawable, 0)). + R.styleable.SelectionModeDrawables_actionModeCopyDrawable, 0)). setAlphabeticShortcut('c'). setShowAsAction( MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); @@ -10299,7 +10325,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (canPaste()) { menu.add(0, ID_PASTE, 0, com.android.internal.R.string.paste). setIcon(styledAttributes.getResourceId( - R.styleable.Theme_actionModePasteDrawable, 0)). + R.styleable.SelectionModeDrawables_actionModePasteDrawable, 0)). setAlphabeticShortcut('v'). setShowAsAction( MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); diff --git a/core/java/android/widget/TimePicker.java b/core/java/android/widget/TimePicker.java index 7444d46..f52e773 100644 --- a/core/java/android/widget/TimePicker.java +++ b/core/java/android/widget/TimePicker.java @@ -237,9 +237,7 @@ public class TimePicker extends FrameLayout { } // set the content descriptions - if (AccessibilityManager.getInstance(mContext).isEnabled()) { - setContentDescriptions(); - } + setContentDescriptions(); } @Override diff --git a/core/java/com/android/internal/view/StandaloneActionMode.java b/core/java/com/android/internal/view/StandaloneActionMode.java index ecda47e..edf4443 100644 --- a/core/java/com/android/internal/view/StandaloneActionMode.java +++ b/core/java/com/android/internal/view/StandaloneActionMode.java @@ -36,17 +36,19 @@ public class StandaloneActionMode extends ActionMode implements MenuBuilder.Call private ActionMode.Callback mCallback; private WeakReference<View> mCustomView; private boolean mFinished; + private boolean mFocusable; private MenuBuilder mMenu; public StandaloneActionMode(Context context, ActionBarContextView view, - ActionMode.Callback callback) { + ActionMode.Callback callback, boolean isFocusable) { mContext = context; mContextView = view; mCallback = callback; mMenu = new MenuBuilder(context).setDefaultShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); mMenu.setCallback(this); + mFocusable = isFocusable; } @Override @@ -139,4 +141,8 @@ public class StandaloneActionMode extends ActionMode implements MenuBuilder.Call invalidate(); mContextView.showOverflowMenu(); } + + public boolean isUiFocusable() { + return mFocusable; + } } diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index 4a38775..2694aa2 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -24,11 +24,15 @@ import android.app.admin.DevicePolicyManager; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; +import android.content.pm.PackageManager; +import android.hardware.Camera; +import android.hardware.Camera.CameraInfo; import android.os.FileObserver; import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; +import android.os.SystemProperties; import android.os.storage.IMountService; import android.provider.Settings; import android.security.KeyStore; @@ -55,6 +59,8 @@ import java.util.concurrent.atomic.AtomicBoolean; */ public class LockPatternUtils { + private static final String OPTION_ENABLE_FACELOCK = "enable_facelock"; + private static final String TAG = "LockPatternUtils"; private static final String SYSTEM_DIRECTORY = "/system/"; @@ -110,6 +116,7 @@ public class LockPatternUtils { public static final String PASSWORD_TYPE_ALTERNATE_KEY = "lockscreen.password_type_alternate"; private final static String LOCK_PASSWORD_SALT_KEY = "lockscreen.password_salt"; private final static String DISABLE_LOCKSCREEN_KEY = "lockscreen.disabled"; + private final static String LOCKSCREEN_OPTIONS = "lockscreen.options"; public final static String LOCKSCREEN_BIOMETRIC_WEAK_FALLBACK = "lockscreen.biometric_weak_fallback"; @@ -339,12 +346,21 @@ public class LockPatternUtils { */ public int getActivePasswordQuality() { int activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; - switch (getKeyguardStoredPasswordQuality()) { + // Note we don't want to use getKeyguardStoredPasswordQuality() because we want this to + // return biometric_weak if that is being used instead of the backup + int quality = + (int) getLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING); + switch (quality) { case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING: if (isLockPatternEnabled()) { activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_SOMETHING; } break; + case DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK: + if (isBiometricWeakInstalled()) { + activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK; + } + break; case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC: if (isLockPasswordEnabled()) { activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_NUMERIC; @@ -366,6 +382,7 @@ public class LockPatternUtils { } break; } + return activePasswordQuality; } @@ -434,7 +451,7 @@ public class LockPatternUtils { * Calls back SetupFaceLock to delete the gallery file when the lock type is changed */ void deleteGallery() { - if(isBiometricEnabled()) { + if(usingBiometricWeak()) { Intent intent = new Intent().setClassName("com.android.facelock", "com.android.facelock.SetupFaceLock"); intent.putExtra("deleteGallery", true); @@ -677,6 +694,9 @@ public class LockPatternUtils { return quality; } + /** + * @return true if the lockscreen method is set to biometric weak + */ public boolean usingBiometricWeak() { int quality = (int) getLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING); @@ -810,7 +830,7 @@ public class LockPatternUtils { || backupMode == DevicePolicyManager.PASSWORD_QUALITY_COMPLEX; return savedPasswordExists() && (passwordEnabled || - (isBiometricEnabled() && backupEnabled)); + (usingBiometricWeak() && backupEnabled)); } /** @@ -824,16 +844,36 @@ public class LockPatternUtils { return getBoolean(Settings.Secure.LOCK_PATTERN_ENABLED) && (getLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING) == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING || - (isBiometricEnabled() && backupEnabled)); + (usingBiometricWeak() && backupEnabled)); } /** - * @return Whether biometric weak lock is enabled. + * @return Whether biometric weak lock is installed and that the front facing camera exists */ - public boolean isBiometricEnabled() { - // TODO: check if it's installed - return getLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING) - == DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK; + public boolean isBiometricWeakInstalled() { + // Check that the system flag was set + if (!OPTION_ENABLE_FACELOCK.equals(getString(LOCKSCREEN_OPTIONS))) { + return false; + } + + // Check that it's installed + PackageManager pm = mContext.getPackageManager(); + try { + pm.getPackageInfo("com.android.facelock", PackageManager.GET_ACTIVITIES); + } catch (PackageManager.NameNotFoundException e) { + return false; + } + + // Check that the camera is enabled + if (!pm.hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT)) { + return false; + } + if (getDevicePolicyManager().getCameraDisabled(null)) { + return false; + } + + + return true; } /** |
