diff options
Diffstat (limited to 'core/java')
64 files changed, 1865 insertions, 1162 deletions
diff --git a/core/java/android/animation/PropertyValuesHolder.java b/core/java/android/animation/PropertyValuesHolder.java index 58f23f7..5b1a7cf 100644 --- a/core/java/android/animation/PropertyValuesHolder.java +++ b/core/java/android/animation/PropertyValuesHolder.java @@ -384,8 +384,7 @@ public class PropertyValuesHolder implements Cloneable { try { returnVal = targetClass.getMethod(methodName, args); } catch (NoSuchMethodException e) { - Log.e("PropertyValuesHolder", - "Couldn't find no-arg method for property " + mPropertyName + ": " + e); + // Swallow the error, log it later } } else { args = new Class[1]; @@ -412,9 +411,12 @@ public class PropertyValuesHolder implements Cloneable { } } // If we got here, then no appropriate function was found - Log.e("PropertyValuesHolder", - "Couldn't find setter/getter for property " + mPropertyName + - " with value type "+ mValueType); + } + + if (returnVal == null) { + Log.w("PropertyValuesHolder", "Method " + + getMethodName(prefix, mPropertyName) + "() with type " + mValueType + + " not found on target class " + targetClass); } return returnVal; @@ -495,7 +497,7 @@ public class PropertyValuesHolder implements Cloneable { } return; } catch (ClassCastException e) { - Log.e("PropertyValuesHolder","No such property (" + mProperty.getName() + + Log.w("PropertyValuesHolder","No such property (" + mProperty.getName() + ") on target object " + target + ". Trying reflection instead"); mProperty = null; } @@ -508,6 +510,10 @@ public class PropertyValuesHolder implements Cloneable { if (!kf.hasValue()) { if (mGetter == null) { setupGetter(targetClass); + if (mGetter == null) { + // Already logged the error - just return to avoid NPE + return; + } } try { kf.setValue(mGetter.invoke(target)); @@ -535,6 +541,10 @@ public class PropertyValuesHolder implements Cloneable { if (mGetter == null) { Class targetClass = target.getClass(); setupGetter(targetClass); + if (mGetter == null) { + // Already logged the error - just return to avoid NPE + return; + } } kf.setValue(mGetter.invoke(target)); } catch (InvocationTargetException e) { @@ -854,8 +864,9 @@ public class PropertyValuesHolder implements Cloneable { } } } catch (NoSuchMethodError e) { - Log.d("PropertyValuesHolder", - "Can't find native method using JNI, use reflection" + e); + // Couldn't find it via JNI - try reflection next. Probably means the method + // doesn't exist, or the type is wrong. An error will be logged later if + // reflection fails as well. } finally { mPropertyMapLock.writeLock().unlock(); } @@ -990,8 +1001,9 @@ public class PropertyValuesHolder implements Cloneable { } } } catch (NoSuchMethodError e) { - Log.d("PropertyValuesHolder", - "Can't find native method using JNI, use reflection" + e); + // Couldn't find it via JNI - try reflection next. Probably means the method + // doesn't exist, or the type is wrong. An error will be logged later if + // reflection fails as well. } finally { mPropertyMapLock.writeLock().unlock(); } diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 8d6a496..29d96fe 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -1242,7 +1242,7 @@ public class Activity extends ContextThemeWrapper * making sure nothing is lost if there are not enough resources to start * the new activity without first killing this one. This is also a good * place to do things like stop animations and other things that consume a - * noticeable mount of CPU in order to make the switch to the next activity + * noticeable amount of CPU in order to make the switch to the next activity * as fast as possible, or to close resources that are exclusive access * such as the camera. * diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java index 423b02a..b730581 100644 --- a/core/java/android/app/ActivityOptions.java +++ b/core/java/android/app/ActivityOptions.java @@ -325,10 +325,11 @@ public class ActivityOptions { } /** - * Join the values in <var>otherOptions</var> in to this one. Any values + * Update the current values in this ActivityOptions from those supplied + * in <var>otherOptions</var>. Any values * defined in <var>otherOptions</var> replace those in the base options. */ - public void join(ActivityOptions otherOptions) { + public void update(ActivityOptions otherOptions) { if (otherOptions.mPackageName != null) { mPackageName = otherOptions.mPackageName; } diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java index f04ff6a..2cc3b02 100644 --- a/core/java/android/app/Dialog.java +++ b/core/java/android/app/Dialog.java @@ -27,6 +27,7 @@ import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Bundle; import android.os.Handler; +import android.os.Looper; import android.os.Message; import android.util.Log; import android.util.TypedValue; @@ -103,7 +104,6 @@ public class Dialog implements DialogInterface, Window.Callback, private boolean mShowing = false; private boolean mCanceled = false; - private final Thread mUiThread; private final Handler mHandler = new Handler(); private static final int DISMISS = 0x43; @@ -162,7 +162,6 @@ public class Dialog implements DialogInterface, Window.Callback, w.setCallback(this); w.setWindowManager(mWindowManager, null, null); w.setGravity(Gravity.CENTER); - mUiThread = Thread.currentThread(); mListenersHandler = new ListenersHandler(this); } @@ -299,11 +298,10 @@ public class Dialog implements DialogInterface, Window.Callback, * that in {@link #onStop}. */ public void dismiss() { - if (Thread.currentThread() != mUiThread) { - mHandler.post(mDismissAction); + if (Looper.myLooper() == mHandler.getLooper()) { + dismissDialog(); } else { - mHandler.removeCallbacks(mDismissAction); - mDismissAction.run(); + mHandler.post(mDismissAction); } } diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 8f4efab..0d76877 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -806,6 +806,10 @@ public class Notification implements Parcelable contentView.setViewVisibility(R.id.time, View.VISIBLE); contentView.setLong(R.id.time, "setTime", when); } + if (this.number != 0) { + NumberFormat f = NumberFormat.getIntegerInstance(); + contentView.setTextViewText(R.id.info, f.format(this.number)); + } this.contentView = contentView; this.contentIntent = contentIntent; @@ -1397,7 +1401,8 @@ public class Notification implements Parcelable if (mSubText != null) { contentView.setTextViewText(R.id.text, mSubText); - contentView.setViewVisibility(R.id.text2, View.VISIBLE); + contentView.setViewVisibility(R.id.text2, + mContentText != null ? View.VISIBLE : View.GONE); } else { contentView.setViewVisibility(R.id.text2, View.GONE); if (mProgressMax != 0 || mProgressIndeterminate) { @@ -1428,12 +1433,12 @@ public class Notification implements Parcelable int N = mActions.size(); if (N > 0) { - Log.d("Notification", "has actions: " + mContentText); + // Log.d("Notification", "has actions: " + mContentText); big.setViewVisibility(R.id.actions, View.VISIBLE); if (N>3) N=3; for (int i=0; i<N; i++) { final RemoteViews button = generateActionButton(mActions.get(i)); - Log.d("Notification", "adding action " + i + ": " + mActions.get(i).title); + //Log.d("Notification", "adding action " + i + ": " + mActions.get(i).title); big.addView(R.id.actions, button); } } @@ -1549,9 +1554,28 @@ public class Notification implements Parcelable * An object that can apply a rich notification style to a {@link Notification.Builder} * object. */ - public static class Style { + public static abstract class Style + { + private CharSequence mBigContentTitle; + private CharSequence mSummaryText = null; + protected Builder mBuilder; + /** + * Overrides ContentTitle in the big form of the template. + * This defaults to the value passed to setContentTitle(). + */ + protected void internalSetBigContentTitle(CharSequence title) { + mBigContentTitle = title; + } + + /** + * Set the first line of text after the detail section in the big form of the template. + */ + protected void internalSetSummaryText(CharSequence cs) { + mSummaryText = cs; + } + public void setBuilder(Builder builder) { if (mBuilder != builder) { mBuilder = builder; @@ -1559,12 +1583,42 @@ public class Notification implements Parcelable } } - public Notification build() { + protected void checkBuilder() { if (mBuilder == null) { throw new IllegalArgumentException("Style requires a valid Builder object"); } - return mBuilder.buildUnstyled(); } + + protected RemoteViews getStandardView(int layoutId) { + checkBuilder(); + + if (mBigContentTitle != null) { + mBuilder.setContentTitle(mBigContentTitle); + } + + if (mBuilder.mSubText == null) { + mBuilder.setContentText(null); + } + + RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(layoutId); + + if (mBuilder.mSubText == null) { + contentView.setViewVisibility(R.id.line3, View.GONE); + } + + if (mBigContentTitle != null && mBigContentTitle.equals("")) { + contentView.setViewVisibility(R.id.line1, View.GONE); + } + + if (mSummaryText != null && !mSummaryText.equals("")) { + contentView.setViewVisibility(R.id.overflow_title, View.VISIBLE); + contentView.setTextViewText(R.id.overflow_title, mSummaryText); + } + + return contentView; + } + + public abstract Notification build(); } /** @@ -1594,13 +1648,30 @@ public class Notification implements Parcelable setBuilder(builder); } + /** + * Overrides ContentTitle in the big form of the template. + * This defaults to the value passed to setContentTitle(). + */ + public BigPictureStyle setBigContentTitle(CharSequence title) { + internalSetBigContentTitle(title); + return this; + } + + /** + * Set the first line of text after the detail section in the big form of the template. + */ + public BigPictureStyle setSummaryText(CharSequence cs) { + internalSetSummaryText(cs); + return this; + } + public BigPictureStyle bigPicture(Bitmap b) { mPicture = b; return this; } private RemoteViews makeBigContentView() { - RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(R.layout.notification_template_big_picture); + RemoteViews contentView = getStandardView(R.layout.notification_template_big_picture); contentView.setImageViewBitmap(R.id.big_picture, mPicture); @@ -1609,9 +1680,7 @@ public class Notification implements Parcelable @Override public Notification build() { - if (mBuilder == null) { - throw new IllegalArgumentException("Style requires a valid Builder object"); - } + checkBuilder(); Notification wip = mBuilder.buildUnstyled(); wip.bigContentView = makeBigContentView(); return wip; @@ -1645,14 +1714,30 @@ public class Notification implements Parcelable setBuilder(builder); } + /** + * Overrides ContentTitle in the big form of the template. + * This defaults to the value passed to setContentTitle(). + */ + public BigTextStyle setBigContentTitle(CharSequence title) { + internalSetBigContentTitle(title); + return this; + } + + /** + * Set the first line of text after the detail section in the big form of the template. + */ + public BigTextStyle setSummaryText(CharSequence cs) { + internalSetSummaryText(cs); + return this; + } + public BigTextStyle bigText(CharSequence cs) { mBigText = cs; return this; } private RemoteViews makeBigContentView() { - int bigTextId = R.layout.notification_template_big_text; - RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(bigTextId); + RemoteViews contentView = getStandardView(R.layout.notification_template_big_text); contentView.setTextViewText(R.id.big_text, mBigText); contentView.setViewVisibility(R.id.big_text, View.VISIBLE); contentView.setViewVisibility(R.id.text2, View.GONE); @@ -1662,9 +1747,7 @@ public class Notification implements Parcelable @Override public Notification build() { - if (mBuilder == null) { - throw new IllegalArgumentException("Style requires a valid Builder object"); - } + checkBuilder(); Notification wip = mBuilder.buildUnstyled(); wip.bigContentView = makeBigContentView(); return wip; @@ -1678,12 +1761,14 @@ public class Notification implements Parcelable * <pre class="prettyprint"> * Notification noti = new Notification.InboxStyle( * new Notification.Builder() - * .setContentTitle("New mail from " + sender.toString()) + * .setContentTitle("5 New mails from " + sender.toString()) * .setContentText(subject) * .setSmallIcon(R.drawable.new_mail) * .setLargeIcon(aBitmap)) * .addLine(str1) * .addLine(str2) + * .setContentTitle("") + * .setSummaryText("+3 more") * .build(); * </pre> * @@ -1699,16 +1784,35 @@ public class Notification implements Parcelable setBuilder(builder); } + /** + * Overrides ContentTitle in the big form of the template. + * This defaults to the value passed to setContentTitle(). + */ + public InboxStyle setBigContentTitle(CharSequence title) { + internalSetBigContentTitle(title); + return this; + } + + /** + * Set the first line of text after the detail section in the big form of the template. + */ + public InboxStyle setSummaryText(CharSequence cs) { + internalSetSummaryText(cs); + return this; + } + public InboxStyle addLine(CharSequence cs) { mTexts.add(cs); return this; } private RemoteViews makeBigContentView() { - RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(R.layout.notification_template_inbox); + RemoteViews contentView = getStandardView(R.layout.notification_template_inbox); + contentView.setViewVisibility(R.id.text2, View.GONE); + + int[] rowIds = {R.id.inbox_text0, R.id.inbox_text1, R.id.inbox_text2, R.id.inbox_text3, + R.id.inbox_text4}; - int[] rowIds = {R.id.inbox_text0, R.id.inbox_text1, R.id.inbox_text2, R.id.inbox_text3, R.id.inbox_text4}; - int i=0; while (i < mTexts.size() && i < rowIds.length) { CharSequence str = mTexts.get(i); @@ -1724,9 +1828,7 @@ public class Notification implements Parcelable @Override public Notification build() { - if (mBuilder == null) { - throw new IllegalArgumentException("Style requires a valid Builder object"); - } + checkBuilder(); Notification wip = mBuilder.buildUnstyled(); wip.bigContentView = makeBigContentView(); return wip; diff --git a/core/java/android/content/AbstractThreadedSyncAdapter.java b/core/java/android/content/AbstractThreadedSyncAdapter.java index 6bffed7..bafe67d 100644 --- a/core/java/android/content/AbstractThreadedSyncAdapter.java +++ b/core/java/android/content/AbstractThreadedSyncAdapter.java @@ -21,6 +21,7 @@ import android.os.Bundle; import android.os.IBinder; import android.os.Process; import android.os.RemoteException; +import android.os.Trace; import java.util.HashMap; import java.util.concurrent.atomic.AtomicInteger; @@ -233,9 +234,15 @@ public abstract class AbstractThreadedSyncAdapter { mThreadsKey = toSyncKey(account); } + @Override public void run() { Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); + // Trace this sync instance. Note, conceptually this should be in + // SyncStorageEngine.insertStartSyncEvent(), but the trace functions require unique + // threads in order to track overlapping operations, so we'll do it here for now. + Trace.traceBegin(Trace.TRACE_TAG_SYNC_MANAGER, mAuthority); + SyncResult syncResult = new SyncResult(); ContentProviderClient provider = null; try { @@ -250,6 +257,8 @@ public abstract class AbstractThreadedSyncAdapter { syncResult.databaseError = true; } } finally { + Trace.traceEnd(Trace.TRACE_TAG_SYNC_MANAGER); + if (provider != null) { provider.release(); } diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index b4669a7..4ed6f25 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -1099,6 +1099,14 @@ public class Intent implements Parcelable, Cloneable { @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_WEB_SEARCH = "android.intent.action.WEB_SEARCH"; /** + * Activity Action: Perform assist action. + * <p> + * Input: nothing + * Output: nothing. + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_ASSIST = "android.intent.action.ASSIST"; + /** * Activity Action: List all available applications * <p>Input: Nothing. * <p>Output: nothing. @@ -1240,7 +1248,11 @@ public class Intent implements Parcelable, Cloneable { * Used as a boolean extra field with {@link #ACTION_INSTALL_PACKAGE} to install a * package. Tells the installer UI to skip the confirmation with the user * if the .apk is replacing an existing one. + * @deprecated As of {@link android.os.Build.VERSION_CODES#JELLY_BEAN}, Android + * will no longer show an interstitial message about updating existing + * applications so this is no longer needed. */ + @Deprecated public static final String EXTRA_ALLOW_REPLACE = "android.intent.extra.ALLOW_REPLACE"; diff --git a/core/java/android/content/SyncManager.java b/core/java/android/content/SyncManager.java index 34c40a0..badcb03 100644 --- a/core/java/android/content/SyncManager.java +++ b/core/java/android/content/SyncManager.java @@ -17,7 +17,6 @@ package android.content; import com.android.internal.R; -import com.android.internal.util.ArrayUtils; import com.google.android.collect.Lists; import com.google.android.collect.Maps; @@ -32,7 +31,6 @@ import android.app.AppGlobals; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; -import android.app.DownloadManager.Request; import android.content.SyncStorageEngine.OnSyncRequestListener; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; @@ -1998,6 +1996,7 @@ public class SyncManager implements OnAccountsUpdateListener { ActiveSyncContext conflict = null; ActiveSyncContext longRunning = null; ActiveSyncContext toReschedule = null; + ActiveSyncContext oldestNonExpeditedRegular = null; for (ActiveSyncContext activeSyncContext : mActiveSyncContexts) { final SyncOperation activeOp = activeSyncContext.mSyncOperation; @@ -2005,6 +2004,13 @@ public class SyncManager implements OnAccountsUpdateListener { numInit++; } else { numRegular++; + if (!activeOp.isExpedited()) { + if (oldestNonExpeditedRegular == null + || (oldestNonExpeditedRegular.mStartTime + > activeSyncContext.mStartTime)) { + oldestNonExpeditedRegular = activeSyncContext; + } + } } if (activeOp.account.type.equals(candidate.account.type) && activeOp.authority.equals(candidate.authority) @@ -2027,8 +2033,13 @@ public class SyncManager implements OnAccountsUpdateListener { Log.v(TAG, " numActiveInit=" + numInit + ", numActiveRegular=" + numRegular); Log.v(TAG, " longRunning: " + longRunning); Log.v(TAG, " conflict: " + conflict); + Log.v(TAG, " oldestNonExpeditedRegular: " + oldestNonExpeditedRegular); } + final boolean roomAvailable = candidateIsInitialization + ? numInit < MAX_SIMULTANEOUS_INITIALIZATION_SYNCS + : numRegular < MAX_SIMULTANEOUS_REGULAR_SYNCS; + if (conflict != null) { if (candidateIsInitialization && !conflict.mSyncOperation.isInitialization() && numInit < MAX_SIMULTANEOUS_INITIALIZATION_SYNCS) { @@ -2048,23 +2059,32 @@ public class SyncManager implements OnAccountsUpdateListener { } else { continue; } - } else { - final boolean roomAvailable = candidateIsInitialization - ? numInit < MAX_SIMULTANEOUS_INITIALIZATION_SYNCS - : numRegular < MAX_SIMULTANEOUS_REGULAR_SYNCS; - if (roomAvailable) { - // dispatch candidate - } else if (longRunning != null - && (candidateIsInitialization - == longRunning.mSyncOperation.isInitialization())) { - toReschedule = longRunning; - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "canceling and rescheduling sync since it ran roo long, " - + longRunning); - } - } else { - continue; + } else if (roomAvailable) { + // dispatch candidate + } else if (candidate.isExpedited() && oldestNonExpeditedRegular != null + && !candidateIsInitialization) { + // We found an active, non-expedited regular sync. We also know that the + // candidate doesn't conflict with this active sync since conflict + // is null. Reschedule the active sync and start the candidate. + toReschedule = oldestNonExpeditedRegular; + if (Log.isLoggable(TAG, Log.VERBOSE)) { + Log.v(TAG, "canceling and rescheduling sync since an expedited is ready to run, " + + oldestNonExpeditedRegular); } + } else if (longRunning != null + && (candidateIsInitialization + == longRunning.mSyncOperation.isInitialization())) { + // We found an active, long-running sync. Reschedule the active + // sync and start the candidate. + toReschedule = longRunning; + if (Log.isLoggable(TAG, Log.VERBOSE)) { + Log.v(TAG, "canceling and rescheduling sync since it ran roo long, " + + longRunning); + } + } else { + // we were unable to find or make space to run this candidate, go on to + // the next one + continue; } if (toReschedule != null) { @@ -2516,7 +2536,7 @@ public class SyncManager implements OnAccountsUpdateListener { return mSyncStorageEngine.insertStartSyncEvent( syncOperation.account, syncOperation.userId, syncOperation.authority, - now, source); + now, source, syncOperation.isInitialization()); } public void stopSyncEvent(long rowId, SyncOperation syncOperation, String resultMessage, diff --git a/core/java/android/content/SyncOperation.java b/core/java/android/content/SyncOperation.java index 4e86ef8..9fcc22d 100644 --- a/core/java/android/content/SyncOperation.java +++ b/core/java/android/content/SyncOperation.java @@ -116,6 +116,10 @@ public class SyncOperation implements Comparable { return extras.getBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, false); } + public boolean isExpedited() { + return extras.getBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, false); + } + public boolean ignoreBackoff() { return extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF, false); } diff --git a/core/java/android/content/SyncStorageEngine.java b/core/java/android/content/SyncStorageEngine.java index d821918..6c7e940 100644 --- a/core/java/android/content/SyncStorageEngine.java +++ b/core/java/android/content/SyncStorageEngine.java @@ -221,6 +221,7 @@ public class SyncStorageEngine extends Handler { long upstreamActivity; long downstreamActivity; String mesg; + boolean initialization; } public static class DayStats { @@ -1012,7 +1013,7 @@ public class SyncStorageEngine extends Handler { * Note that sync has started for the given account and authority. */ public long insertStartSyncEvent(Account accountName, int userId, String authorityName, - long now, int source) { + long now, int source, boolean initialization) { long id; synchronized (mAuthorities) { if (Log.isLoggable(TAG, Log.VERBOSE)) { @@ -1025,6 +1026,7 @@ public class SyncStorageEngine extends Handler { return -1; } SyncHistoryItem item = new SyncHistoryItem(); + item.initialization = initialization; item.authorityId = authority.ident; item.historyId = mNextHistoryId++; if (mNextHistoryId < 0) mNextHistoryId = 0; diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index cbabc7c..e1434b3 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -298,6 +298,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { * activity's manifest. * * Default value is false (no support for RTL). + * @hide */ public static final int FLAG_SUPPORTS_RTL = 1<<22; diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 98b40eb..ad52e13 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -1458,7 +1458,7 @@ public class PackageParser { com.android.internal.R.styleable.AndroidManifestPermissionGroup_permissionGroupFlags, 0); perm.info.priority = sa.getInt( com.android.internal.R.styleable.AndroidManifestPermissionGroup_priority, 0); - if (perm.info.priority > 0 && (flags&PARSE_IS_SYSTEM) != 0) { + if (perm.info.priority > 0 && (flags&PARSE_IS_SYSTEM) == 0) { perm.info.priority = 0; } diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java index 5ed8dd1..89068e7 100644 --- a/core/java/android/hardware/Camera.java +++ b/core/java/android/hardware/Camera.java @@ -155,7 +155,7 @@ public class Camera { private boolean mOneShot; private boolean mWithBuffer; private boolean mFaceDetectionRunning = false; - private ReentrantLock mFocusLock = new ReentrantLock(); + private Object mAutoFocusCallbackLock = new Object(); /** * Broadcast Action: A new picture is taken by the camera, and the entry of @@ -471,6 +471,11 @@ public class Camera { * Called as preview frames are displayed. This callback is invoked * on the event thread {@link #open(int)} was called from. * + * <p>If using the {@link android.graphics.ImageFormat#YV12} format, + * refer to the equations in {@link Camera.Parameters#setPreviewFormat} + * for the arrangement of the pixel data in the preview callback + * buffers. + * * @param data the contents of the preview frame in the format defined * by {@link android.graphics.ImageFormat}, which can be queried * with {@link android.hardware.Camera.Parameters#getPreviewFormat()}. @@ -508,7 +513,9 @@ public class Camera { mRawImageCallback = null; mPostviewCallback = null; mJpegCallback = null; - mAutoFocusCallback = null; + synchronized (mAutoFocusCallbackLock) { + mAutoFocusCallback = null; + } mAutoFocusMoveCallback = null; } @@ -611,12 +618,17 @@ public class Camera { * the frame is discarded. Applications should add buffers back when they * finish processing the data in them. * - * <p>The size of the buffer is determined by multiplying the preview - * image width, height, and bytes per pixel. The width and height can be - * read from {@link Camera.Parameters#getPreviewSize()}. Bytes per pixel - * can be computed from - * {@link android.graphics.ImageFormat#getBitsPerPixel(int)} / 8, - * using the image format from {@link Camera.Parameters#getPreviewFormat()}. + * <p>For formats besides YV12, the size of the buffer is determined by + * multiplying the preview image width, height, and bytes per pixel. The + * width and height can be read from + * {@link Camera.Parameters#getPreviewSize()}. Bytes per pixel can be + * computed from {@link android.graphics.ImageFormat#getBitsPerPixel(int)} / + * 8, using the image format from + * {@link Camera.Parameters#getPreviewFormat()}. + * + * <p>If using the {@link android.graphics.ImageFormat#YV12} format, the + * size can be calculated using the equations listed in + * {@link Camera.Parameters#setPreviewFormat}. * * <p>This method is only necessary when * {@link #setPreviewCallbackWithBuffer(PreviewCallback)} is used. When @@ -626,8 +638,8 @@ public class Camera { * hold the preview frame data, preview callback will return null and * the buffer will be removed from the buffer queue. * - * @param callbackBuffer the buffer to add to the queue. - * The size should be width * height * bits_per_pixel / 8. + * @param callbackBuffer the buffer to add to the queue. The size of the + * buffer must match the values described above. * @see #setPreviewCallbackWithBuffer(PreviewCallback) */ public final void addCallbackBuffer(byte[] callbackBuffer) @@ -748,14 +760,13 @@ public class Camera { return; case CAMERA_MSG_FOCUS: - mFocusLock.lock(); - try { - if (mAutoFocusCallback != null) { - boolean success = msg.arg1 == 0 ? false : true; - mAutoFocusCallback.onAutoFocus(success, mCamera); - } - } finally { - mFocusLock.unlock(); + AutoFocusCallback cb = null; + synchronized (mAutoFocusCallbackLock) { + cb = mAutoFocusCallback; + } + if (cb != null) { + boolean success = msg.arg1 == 0 ? false : true; + cb.onAutoFocus(success, mCamera); } return; @@ -880,13 +891,10 @@ public class Camera { */ public final void autoFocus(AutoFocusCallback cb) { - mFocusLock.lock(); - try { + synchronized (mAutoFocusCallbackLock) { mAutoFocusCallback = cb; - native_autoFocus(); - } finally { - mFocusLock.unlock(); } + native_autoFocus(); } private native final void native_autoFocus(); @@ -900,14 +908,26 @@ public class Camera { */ public final void cancelAutoFocus() { - mFocusLock.lock(); - try { + synchronized (mAutoFocusCallbackLock) { mAutoFocusCallback = null; - native_cancelAutoFocus(); - removePendingAFCompletionMessages(); - } finally { - mFocusLock.unlock(); } + native_cancelAutoFocus(); + // CAMERA_MSG_FOCUS should be removed here because the following + // scenario can happen: + // - An application uses the same thread for autoFocus, cancelAutoFocus + // and looper thread. + // - The application calls autoFocus. + // - HAL sends CAMERA_MSG_FOCUS, which enters the looper message queue. + // Before event handler's handleMessage() is invoked, the application + // calls cancelAutoFocus and autoFocus. + // - The application gets the old CAMERA_MSG_FOCUS and thinks autofocus + // has been completed. But in fact it is not. + // + // As documented in the beginning of the file, apps should not use + // multiple threads to call autoFocus and cancelAutoFocus at the same + // time. It is HAL's responsibility not to send a CAMERA_MSG_FOCUS + // message after native_cancelAutoFocus is called. + mEventHandler.removeMessages(CAMERA_MSG_FOCUS); } private native final void native_cancelAutoFocus(); @@ -2289,12 +2309,44 @@ public class Camera { * {@link android.graphics.ImageFormat#NV21}, which * uses the NV21 encoding format.</p> * - * @param pixel_format the desired preview picture format, defined - * by one of the {@link android.graphics.ImageFormat} constants. - * (E.g., <var>ImageFormat.NV21</var> (default), - * <var>ImageFormat.RGB_565</var>, or - * <var>ImageFormat.JPEG</var>) + * <p>Use {@link Parameters#getSupportedPreviewFormats} to get a list of + * the available preview formats. + * + * <p>It is strongly recommended that either + * {@link android.graphics.ImageFormat#NV21} or + * {@link android.graphics.ImageFormat#YV12} is used, since + * they are supported by all camera devices.</p> + * + * <p>For YV12, the image buffer that is received is not necessarily + * tightly packed, as there may be padding at the end of each row of + * pixel data, as described in + * {@link android.graphics.ImageFormat#YV12}. For camera callback data, + * it can be assumed that the stride of the Y and UV data is the + * smallest possible that meets the alignment requirements. That is, if + * the preview size is <var>width x height</var>, then the following + * equations describe the buffer index for the beginning of row + * <var>y</var> for the Y plane and row <var>c</var> for the U and V + * planes: + * + * {@code + * <pre> + * yStride = (int) ceil(width / 16.0) * 16; + * uvStride = (int) ceil( (yStride / 2) / 16.0) * 16; + * ySize = yStride * height; + * uvSize = uvStride * height / 2; + * yRowIndex = yStride * y; + * uRowIndex = ySize + uvSize + uvStride * c; + * vRowIndex = ySize + uvStride * c; + * size = ySize + uvSize * 2;</pre> + * } + * + * @param pixel_format the desired preview picture format, defined by + * one of the {@link android.graphics.ImageFormat} constants. (E.g., + * <var>ImageFormat.NV21</var> (default), or + * <var>ImageFormat.YV12</var>) + * * @see android.graphics.ImageFormat + * @see android.hardware.Camera.Parameters#getSupportedPreviewFormats */ public void setPreviewFormat(int pixel_format) { String s = cameraFormatForPixelFormat(pixel_format); @@ -2312,6 +2364,7 @@ public class Camera { * * @return the preview format. * @see android.graphics.ImageFormat + * @see #setPreviewFormat */ public int getPreviewFormat() { return pixelFormatForCameraFormat(get(KEY_PREVIEW_FORMAT)); @@ -2325,6 +2378,7 @@ public class Camera { * @return a list of supported preview formats. This method will always * return a list with at least one element. * @see android.graphics.ImageFormat + * @see #setPreviewFormat */ public List<Integer> getSupportedPreviewFormats() { String str = get(KEY_PREVIEW_FORMAT + SUPPORTED_VALUES_SUFFIX); @@ -3596,13 +3650,4 @@ public class Camera { return false; } }; - - /* - * At any time, there should be at most one pending auto focus completion - * message, but we simply remove all pending AF completion messages in - * the looper's queue. - */ - private void removePendingAFCompletionMessages() { - mEventHandler.removeMessages(CAMERA_MSG_FOCUS); - } } diff --git a/core/java/android/net/INetworkStatsService.aidl b/core/java/android/net/INetworkStatsService.aidl index 08d4c6c..b7b8731 100644 --- a/core/java/android/net/INetworkStatsService.aidl +++ b/core/java/android/net/INetworkStatsService.aidl @@ -42,5 +42,7 @@ interface INetworkStatsService { void setUidForeground(int uid, boolean uidForeground); /** Force update of statistics. */ void forceUpdate(); + /** Advise persistance threshold; may be overridden internally. */ + void advisePersistThreshold(long thresholdBytes); } diff --git a/core/java/android/net/NetworkIdentity.java b/core/java/android/net/NetworkIdentity.java index 4ac5e76..3c67bf9 100644 --- a/core/java/android/net/NetworkIdentity.java +++ b/core/java/android/net/NetworkIdentity.java @@ -158,9 +158,14 @@ public class NetworkIdentity { } } else if (type == TYPE_WIFI) { - final WifiManager wifi = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); - final WifiInfo info = wifi.getConnectionInfo(); - networkId = info != null ? info.getSSID() : null; + if (state.networkId != null) { + networkId = state.networkId; + } else { + final WifiManager wifi = (WifiManager) context.getSystemService( + Context.WIFI_SERVICE); + final WifiInfo info = wifi.getConnectionInfo(); + networkId = info != null ? info.getSSID() : null; + } } return new NetworkIdentity(type, subType, subscriberId, networkId, roaming); diff --git a/core/java/android/net/NetworkState.java b/core/java/android/net/NetworkState.java index 2fc69ad..fbe1f82 100644 --- a/core/java/android/net/NetworkState.java +++ b/core/java/android/net/NetworkState.java @@ -31,18 +31,20 @@ public class NetworkState implements Parcelable { public final LinkCapabilities linkCapabilities; /** Currently only used by testing. */ public final String subscriberId; + public final String networkId; public NetworkState(NetworkInfo networkInfo, LinkProperties linkProperties, LinkCapabilities linkCapabilities) { - this(networkInfo, linkProperties, linkCapabilities, null); + this(networkInfo, linkProperties, linkCapabilities, null, null); } public NetworkState(NetworkInfo networkInfo, LinkProperties linkProperties, - LinkCapabilities linkCapabilities, String subscriberId) { + LinkCapabilities linkCapabilities, String subscriberId, String networkId) { this.networkInfo = networkInfo; this.linkProperties = linkProperties; this.linkCapabilities = linkCapabilities; this.subscriberId = subscriberId; + this.networkId = networkId; } public NetworkState(Parcel in) { @@ -50,6 +52,7 @@ public class NetworkState implements Parcelable { linkProperties = in.readParcelable(null); linkCapabilities = in.readParcelable(null); subscriberId = in.readString(); + networkId = in.readString(); } @Override @@ -63,6 +66,7 @@ public class NetworkState implements Parcelable { out.writeParcelable(linkProperties, flags); out.writeParcelable(linkCapabilities, flags); out.writeString(subscriberId); + out.writeString(networkId); } public static final Creator<NetworkState> CREATOR = new Creator<NetworkState>() { diff --git a/core/java/android/os/ConditionVariable.java b/core/java/android/os/ConditionVariable.java index 95a9259..07b221c 100644 --- a/core/java/android/os/ConditionVariable.java +++ b/core/java/android/os/ConditionVariable.java @@ -26,7 +26,7 @@ package android.os; * block() will not block, and instead return immediately. * * <p> - * This class uses itself is at the object to wait on, so if you wait() + * This class uses itself as the object to wait on, so if you wait() * or notify() on a ConditionVariable, the results are undefined. */ public class ConditionVariable diff --git a/core/java/android/os/HandlerThread.java b/core/java/android/os/HandlerThread.java index d61b3b4..daf1f59 100644 --- a/core/java/android/os/HandlerThread.java +++ b/core/java/android/os/HandlerThread.java @@ -42,7 +42,7 @@ public class HandlerThread extends Thread { } /** - * Call back method that can be explicitly over ridden if needed to execute some + * Call back method that can be explicitly overridden if needed to execute some * setup before Looper loops. */ protected void onLooperPrepared() { diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl index 6ecc640..e7ea355 100644 --- a/core/java/android/os/INetworkManagementService.aidl +++ b/core/java/android/os/INetworkManagementService.aidl @@ -238,7 +238,8 @@ interface INetworkManagementService * Return global network statistics summarized at an interface level, * without any UID-level granularity. */ - NetworkStats getNetworkStatsSummary(); + NetworkStats getNetworkStatsSummaryDev(); + NetworkStats getNetworkStatsSummaryXt(); /** * Return detailed network statistics with UID-level granularity, diff --git a/core/java/android/os/Message.java b/core/java/android/os/Message.java index 4aa7fe2..0abc149 100644 --- a/core/java/android/os/Message.java +++ b/core/java/android/os/Message.java @@ -97,8 +97,8 @@ public final class Message implements Parcelable { private static Message sPool; private static int sPoolSize = 0; - private static final int MAX_POOL_SIZE = 10; - + private static final int MAX_POOL_SIZE = 50; + /** * Return a new Message instance from the global pool. Allows us to * avoid allocating new objects in many cases. diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java index 2a45506..05acd63 100644 --- a/core/java/android/os/Trace.java +++ b/core/java/android/os/Trace.java @@ -36,6 +36,7 @@ public final class Trace { public static final long TRACE_TAG_WEBVIEW = 1L << 4; public static final long TRACE_TAG_WINDOW_MANAGER = 1L << 5; public static final long TRACE_TAG_ACTIVITY_MANAGER = 1L << 6; + public static final long TRACE_TAG_SYNC_MANAGER = 1L << 7; private static final long sEnabledTags = nativeGetEnabledTags(); diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java index 6d14dfc..7824724 100644 --- a/core/java/android/provider/CallLog.java +++ b/core/java/android/provider/CallLog.java @@ -25,6 +25,7 @@ import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.net.Uri; +import android.provider.ContactsContract.CommonDataKinds.Callable; import android.provider.ContactsContract.CommonDataKinds.Phone; import android.provider.ContactsContract.DataUsageFeedback; import android.text.TextUtils; @@ -312,10 +313,12 @@ public class CallLog { null); } else { final String phoneNumber = ci.phoneNumber != null ? ci.phoneNumber : number; - cursor = resolver.query(Phone.CONTENT_URI, + cursor = resolver.query( + Uri.withAppendedPath(Callable.CONTENT_FILTER_URI, + Uri.encode(phoneNumber)), new String[] { Phone._ID }, - Phone.CONTACT_ID + " =? AND " + Phone.NUMBER + " =?", - new String[] { String.valueOf(ci.person_id), phoneNumber}, + Phone.CONTACT_ID + " =?", + new String[] { String.valueOf(ci.person_id) }, null); } diff --git a/core/java/android/provider/SocialContract.java b/core/java/android/provider/SocialContract.java deleted file mode 100644 index ee271ba..0000000 --- a/core/java/android/provider/SocialContract.java +++ /dev/null @@ -1,187 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package android.provider; - -import android.content.res.Resources; -import android.graphics.BitmapFactory; -import android.net.Uri; -import android.provider.ContactsContract.Contacts; -import android.provider.ContactsContract.Data; - -/** - * The contract between the social provider and applications. Contains - * definitions for the supported URIs and columns. - * - * @hide - */ -public class SocialContract { - /** The authority for the social provider */ - public static final String AUTHORITY = "com.android.social"; - - /** A content:// style uri to the authority for the contacts provider */ - public static final Uri AUTHORITY_URI = Uri.parse("content://" + AUTHORITY); - - private interface ActivitiesColumns { - /** - * The package name to use when creating {@link Resources} objects for - * this data row. This value is only designed for use when building user - * interfaces, and should not be used to infer the owner. - * <p> - * Type: TEXT - */ - public static final String RES_PACKAGE = "res_package"; - - /** - * The mime-type of this social activity. - * <p> - * Type: TEXT - */ - public static final String MIMETYPE = "mimetype"; - - /** - * Internal raw identifier for this social activity. This field is - * analogous to the <code>atom:id</code> element defined in RFC 4287. - * <p> - * Type: TEXT - */ - public static final String RAW_ID = "raw_id"; - - /** - * Reference to another {@link Activities#RAW_ID} that this social activity - * is replying to. This field is analogous to the - * <code>thr:in-reply-to</code> element defined in RFC 4685. - * <p> - * Type: TEXT - */ - public static final String IN_REPLY_TO = "in_reply_to"; - - /** - * Reference to the {@link android.provider.ContactsContract.Contacts#_ID} that authored - * this social activity. This field is analogous to the <code>atom:author</code> - * element defined in RFC 4287. - * <p> - * Type: INTEGER - */ - public static final String AUTHOR_CONTACT_ID = "author_contact_id"; - - /** - * Optional reference to the {@link android.provider.ContactsContract.Contacts#_ID} this - * social activity is targeted towards. If more than one direct target, this field may - * be left undefined. This field is analogous to the - * <code>activity:target</code> element defined in the Atom Activity - * Extensions Internet-Draft. - * <p> - * Type: INTEGER - */ - public static final String TARGET_CONTACT_ID = "target_contact_id"; - - /** - * Timestamp when this social activity was published, in a - * {@link System#currentTimeMillis()} time base. This field is analogous - * to the <code>atom:published</code> element defined in RFC 4287. - * <p> - * Type: INTEGER - */ - public static final String PUBLISHED = "published"; - - /** - * Timestamp when the original social activity in a thread was - * published. For activities that have an in-reply-to field specified, the - * content provider will automatically populate this field with the - * timestamp of the original activity. - * <p> - * This field is useful for sorting order of activities that keeps together all - * messages in each thread. - * <p> - * Type: INTEGER - */ - public static final String THREAD_PUBLISHED = "thread_published"; - - /** - * Title of this social activity. This field is analogous to the - * <code>atom:title</code> element defined in RFC 4287. - * <p> - * Type: TEXT - */ - public static final String TITLE = "title"; - - /** - * Summary of this social activity. This field is analogous to the - * <code>atom:summary</code> element defined in RFC 4287. - * <p> - * Type: TEXT - */ - public static final String SUMMARY = "summary"; - - /** - * A URI associated this social activity. This field is analogous to the - * <code>atom:link rel="alternate"</code> element defined in RFC 4287. - * <p> - * Type: TEXT - */ - public static final String LINK = "link"; - - /** - * Optional thumbnail specific to this social activity. This is the raw - * bytes of an image that could be inflated using {@link BitmapFactory}. - * <p> - * Type: BLOB - */ - public static final String THUMBNAIL = "thumbnail"; - } - - public static final class Activities implements BaseColumns, ActivitiesColumns { - /** - * This utility class cannot be instantiated - */ - private Activities() { - } - - /** - * The content:// style URI for this table - */ - public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "activities"); - - /** - * The content:// URI for this table filtered to the set of social activities - * authored by a specific {@link android.provider.ContactsContract.Contacts#_ID}. - */ - public static final Uri CONTENT_AUTHORED_BY_URI = - Uri.withAppendedPath(CONTENT_URI, "authored_by"); - - /** - * The {@link Uri} for the latest social activity performed by any - * raw contact aggregated under the specified {@link Contacts#_ID}. Will - * also join with most-present {@link Presence} for this aggregate. - */ - public static final Uri CONTENT_CONTACT_STATUS_URI = - Uri.withAppendedPath(AUTHORITY_URI, "contact_status"); - - /** - * The MIME type of {@link #CONTENT_URI} providing a directory of social - * activities. - */ - public static final String CONTENT_TYPE = "vnd.android.cursor.dir/activity"; - - /** - * The MIME type of a {@link #CONTENT_URI} subdirectory of a single - * social activity. - */ - public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/activity"; - } - -} diff --git a/core/java/android/service/textservice/SpellCheckerService.java b/core/java/android/service/textservice/SpellCheckerService.java index c579e6e..77b22ed 100644 --- a/core/java/android/service/textservice/SpellCheckerService.java +++ b/core/java/android/service/textservice/SpellCheckerService.java @@ -397,14 +397,6 @@ public abstract class SpellCheckerService extends Service { } wordStart = wordIterator.getBeginning(wordEnd); } - if (originalText.length() >= SpellChecker.WORD_ITERATOR_INTERVAL - && wordItems.size() >= 2) { - if (DBG) { - Log.w(TAG, "Remove possibly divided word: " - + wordItems.get(0).mTextInfo.getText()); - } - wordItems.remove(0); - } return new SentenceTextInfoParams(originalTextInfo, wordItems); } diff --git a/core/java/android/text/SpannableStringBuilder.java b/core/java/android/text/SpannableStringBuilder.java index 09c9438..0f30d25 100644 --- a/core/java/android/text/SpannableStringBuilder.java +++ b/core/java/android/text/SpannableStringBuilder.java @@ -308,6 +308,7 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable resizeFor(mText.length + nbNewChars - mGapLength); } + final boolean textIsRemoved = replacementLength == 0; // The removal pass needs to be done before the gap is updated in order to broadcast the // correct previous positions to the correct intersecting SpanWatchers if (replacedLength > 0) { // no need for span fixup on pure insertion @@ -319,12 +320,15 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable while (i < mSpanCount) { if ((mSpanFlags[i] & Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) == Spanned.SPAN_EXCLUSIVE_EXCLUSIVE && - mSpanStarts[i] >= start && mSpanStarts[i] < mGapStart + mGapLength && - mSpanEnds[i] >= start && mSpanEnds[i] < mGapStart + mGapLength) { + mSpanStarts[i] >= start && mSpanStarts[i] < mGapStart + mGapLength && + mSpanEnds[i] >= start && mSpanEnds[i] < mGapStart + mGapLength && + // This condition indicates that the span would become empty + (textIsRemoved || mSpanStarts[i] > start || mSpanEnds[i] < mGapStart)) { removeSpan(i); - } else { - i++; + continue; // do not increment i, spans will be shifted left in the array } + + i++; } } @@ -338,7 +342,6 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable if (replacedLength > 0) { // no need for span fixup on pure insertion final boolean atEnd = (mGapStart + mGapLength == mText.length); - final boolean textIsRemoved = replacementLength == 0; for (int i = 0; i < mSpanCount; i++) { final int startFlag = (mSpanFlags[i] & START_MASK) >> START_SHIFT; @@ -390,9 +393,9 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable return mGapStart + mGapLength; } } else { // MARK - // MARKs should be moved to the start, with the exception of a mark located at the - // end of the range (which will be < mGapStart + mGapLength since mGapLength > 0) - // which should stay 'unchanged' at the end of the replaced text. + // MARKs should be moved to the start, with the exception of a mark located at + // the end of the range (which will be < mGapStart + mGapLength since mGapLength + // is > 0, which should stay 'unchanged' at the end of the replaced text. if (textIsRemoved || offset < mGapStart - nbNewChars) { return start; } else { diff --git a/core/java/android/text/TextDirectionHeuristics.java b/core/java/android/text/TextDirectionHeuristics.java index bbaa173..be2840b 100644 --- a/core/java/android/text/TextDirectionHeuristics.java +++ b/core/java/android/text/TextDirectionHeuristics.java @@ -22,7 +22,6 @@ import android.view.View; /** * Some objects that implement TextDirectionHeuristic. - * * @hide */ public class TextDirectionHeuristics { diff --git a/core/java/android/util/LocaleUtil.java b/core/java/android/util/LocaleUtil.java index 60526e1..93f5cd3 100644 --- a/core/java/android/util/LocaleUtil.java +++ b/core/java/android/util/LocaleUtil.java @@ -24,6 +24,7 @@ import libcore.icu.ICU; /** * Various utilities for Locales * + * @hide */ public class LocaleUtil { @@ -40,7 +41,8 @@ public class LocaleUtil { * {@link View#LAYOUT_DIRECTION_LTR} or * {@link View#LAYOUT_DIRECTION_RTL}. * - * Be careful: this code will need to be updated when vertical scripts will be supported + * Warning: this code does not support vertical scripts. + * @hide */ public static int getLayoutDirectionFromLocale(Locale locale) { if (locale != null && !locale.equals(Locale.ROOT)) { @@ -66,8 +68,7 @@ public class LocaleUtil { * {@link View#LAYOUT_DIRECTION_LTR} or * {@link View#LAYOUT_DIRECTION_RTL}. * - * Be careful: this code will need to be updated when vertical scripts will be supported - * + * Warning: this code does not support vertical scripts. * @hide */ private static int getLayoutDirectionFromFirstChar(Locale locale) { diff --git a/core/java/android/util/MathUtils.java b/core/java/android/util/MathUtils.java index b35dd1e..13a692e 100644 --- a/core/java/android/util/MathUtils.java +++ b/core/java/android/util/MathUtils.java @@ -39,6 +39,10 @@ public final class MathUtils { return amount < low ? low : (amount > high ? high : amount); } + public static long constrain(long amount, long low, long high) { + return amount < low ? low : (amount > high ? high : amount); + } + public static float constrain(float amount, float low, float high) { return amount < low ? low : (amount > high ? high : amount); } diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java index bda8016..c947312 100644 --- a/core/java/android/view/Display.java +++ b/core/java/android/view/Display.java @@ -391,6 +391,15 @@ public class Display { } /** + * If the display is mirrored to an external HDMI display, returns the + * rotation of that display relative to its natural orientation. + * @hide + */ + public int getExternalRotation() { + return Surface.ROTATION_0; + } + + /** * Gets display metrics based on an explicit assumed display size. * @hide */ diff --git a/core/java/android/view/DisplayList.java b/core/java/android/view/DisplayList.java index fba73fbd..3dab174 100644 --- a/core/java/android/view/DisplayList.java +++ b/core/java/android/view/DisplayList.java @@ -30,7 +30,7 @@ import android.graphics.Matrix; public abstract class DisplayList { /** * Flag used when calling - * {@link HardwareCanvas#drawDisplayList(DisplayList, int, int, android.graphics.Rect, int)}. + * {@link HardwareCanvas#drawDisplayList(DisplayList, android.graphics.Rect, int)} * When this flag is set, draw operations lying outside of the bounds of the * display list will be culled early. It is recommeneded to always set this * flag. @@ -42,21 +42,21 @@ public abstract class DisplayList { /** * Indicates that the display list is done drawing. * - * @see HardwareCanvas#drawDisplayList(DisplayList, int, int, android.graphics.Rect, int) + * @see HardwareCanvas#drawDisplayList(DisplayList, android.graphics.Rect, int) */ public static final int STATUS_DONE = 0x0; /** * Indicates that the display list needs another drawing pass. * - * @see HardwareCanvas#drawDisplayList(DisplayList, int, int, android.graphics.Rect, int) + * @see HardwareCanvas#drawDisplayList(DisplayList, android.graphics.Rect, int) */ public static final int STATUS_DRAW = 0x1; /** * Indicates that the display list needs to re-execute its GL functors. * - * @see HardwareCanvas#drawDisplayList(DisplayList, int, int, android.graphics.Rect, int) + * @see HardwareCanvas#drawDisplayList(DisplayList, android.graphics.Rect, int) * @see HardwareCanvas#callDrawGLFunction(int) */ public static final int STATUS_INVOKE = 0x2; diff --git a/core/java/android/view/FocusFinder.java b/core/java/android/view/FocusFinder.java index 98375ae..6bf1888 100644 --- a/core/java/android/view/FocusFinder.java +++ b/core/java/android/view/FocusFinder.java @@ -62,7 +62,7 @@ public class FocusFinder { * @return The next focusable view, or null if none exists. */ public final View findNextFocus(ViewGroup root, View focused, int direction) { - return findNextFocus(root, focused, mFocusedRect, direction); + return findNextFocus(root, focused, null, direction); } /** @@ -122,34 +122,40 @@ public class FocusFinder { int direction, ArrayList<View> focusables) { final int directionMasked = (direction & ~View.FOCUS_ACCESSIBILITY); if (focused != null) { + if (focusedRect == null) { + focusedRect = mFocusedRect; + } // fill in interesting rect from focused focused.getFocusedRect(focusedRect); root.offsetDescendantRectToMyCoords(focused, focusedRect); } else { - // make up a rect at top left or bottom right of root - switch (directionMasked) { - case View.FOCUS_RIGHT: - case View.FOCUS_DOWN: - setFocusTopLeft(root, focusedRect); - break; - case View.FOCUS_FORWARD: - if (root.isLayoutRtl()) { - setFocusBottomRight(root, focusedRect); - } else { - setFocusTopLeft(root, focusedRect); - } - break; - - case View.FOCUS_LEFT: - case View.FOCUS_UP: - setFocusBottomRight(root, focusedRect); - break; - case View.FOCUS_BACKWARD: - if (root.isLayoutRtl()) { + if (focusedRect == null) { + focusedRect = mFocusedRect; + // make up a rect at top left or bottom right of root + switch (directionMasked) { + case View.FOCUS_RIGHT: + case View.FOCUS_DOWN: setFocusTopLeft(root, focusedRect); - } else { + break; + case View.FOCUS_FORWARD: + if (root.isLayoutRtl()) { + setFocusBottomRight(root, focusedRect); + } else { + setFocusTopLeft(root, focusedRect); + } + break; + + case View.FOCUS_LEFT: + case View.FOCUS_UP: setFocusBottomRight(root, focusedRect); - break; + break; + case View.FOCUS_BACKWARD: + if (root.isLayoutRtl()) { + setFocusTopLeft(root, focusedRect); + } else { + setFocusBottomRight(root, focusedRect); + break; + } } } } diff --git a/core/java/android/view/Gravity.java b/core/java/android/view/Gravity.java index f031fe7..4547aa6 100644 --- a/core/java/android/view/Gravity.java +++ b/core/java/android/view/Gravity.java @@ -155,6 +155,7 @@ public class Gravity * * @see {@link View#LAYOUT_DIRECTION_LTR} * @see {@link View#LAYOUT_DIRECTION_RTL} + * @hide */ public static void apply(int gravity, int w, int h, Rect container, Rect outRect, int layoutDirection) { @@ -292,6 +293,7 @@ public class Gravity * * @see {@link View#LAYOUT_DIRECTION_LTR} * @see {@link View#LAYOUT_DIRECTION_RTL} + * @hide */ public static void apply(int gravity, int w, int h, Rect container, int xAdj, int yAdj, Rect outRect, int layoutDirection) { @@ -372,6 +374,7 @@ public class Gravity * * @see {@link View#LAYOUT_DIRECTION_LTR} * @see {@link View#LAYOUT_DIRECTION_RTL} + * @hide */ public static void applyDisplay(int gravity, Rect display, Rect inoutObj, int layoutDirection) { int absGravity = getAbsoluteGravity(gravity, layoutDirection); @@ -408,6 +411,7 @@ public class Gravity * @param gravity The gravity to convert to absolute (horizontal) values. * @param layoutDirection The layout direction. * @return gravity converted to absolute (horizontal) values. + * @hide */ public static int getAbsoluteGravity(int gravity, int layoutDirection) { int result = gravity; diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java index c0771c5..e25e2ef 100644 --- a/core/java/android/view/HardwareRenderer.java +++ b/core/java/android/view/HardwareRenderer.java @@ -152,7 +152,7 @@ public abstract class HardwareRenderer { /** * Number of frames to profile. */ - private static final int PROFILE_MAX_FRAMES = 64; + private static final int PROFILE_MAX_FRAMES = 128; /** * Number of floats per profiled frame. diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 2f67481..5299d58 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -340,10 +340,9 @@ import java.util.concurrent.CopyOnWriteArrayList; * Padding can be used to offset the content of the view by a specific amount of * pixels. For instance, a left padding of 2 will push the view's content by * 2 pixels to the right of the left edge. Padding can be set using the - * {@link #setPadding(int, int, int, int)} or {@link #setPaddingRelative(int, int, int, int)} - * method and queried by calling {@link #getPaddingLeft()}, {@link #getPaddingTop()}, - * {@link #getPaddingRight()}, {@link #getPaddingBottom()}, {@link #getPaddingStart()}, - * {@link #getPaddingEnd()}. + * {@link #setPadding(int, int, int, int)} method and queried by calling + * {@link #getPaddingLeft()}, {@link #getPaddingTop()}, {@link #getPaddingRight()}, + * {@link #getPaddingBottom()}. * </p> * * <p> @@ -537,9 +536,32 @@ import java.util.concurrent.CopyOnWriteArrayList; * themselves rather than by putting them in a separate structure. * </p> * + * <a name="Properties"></a> + * <h3>Properties</h3> + * <p> + * The View class exposes an {@link #ALPHA} property, as well as several transform-related + * properties, such as {@link #TRANSLATION_X} and {@link #TRANSLATION_Y}. These properties are + * available both in the {@link Property} form as well as in similarly-named setter/getter + * methods (such as {@link #setAlpha(float)} for {@link #ALPHA}). These properties can + * be used to set persistent state associated with these rendering-related properties on the view. + * The properties and methods can also be used in conjunction with + * {@link android.animation.Animator Animator}-based animations, described more in the + * <a href="#Animation">Animation</a> section. + * </p> + * * <a name="Animation"></a> * <h3>Animation</h3> * <p> + * Starting with Android 3.0, the preferred way of animating views is to use the + * {@link android.animation} package APIs. These {@link android.animation.Animator Animator}-based + * classes change actual properties of the View object, such as {@link #setAlpha(float) alpha} and + * {@link #setTranslationX(float) translationX}. This behavior is contrasted to that of the pre-3.0 + * {@link android.view.animation.Animation Animation}-based classes, which instead animate only + * how the view is drawn on the display. In particular, the {@link ViewPropertyAnimator} class + * makes animating these View properties particularly easy and efficient. + * </p> + * <p> + * Alternatively, you can use the pre-3.0 animation classes to animate how Views are rendered. * You can attach an {@link Animation} object to a view using * {@link #setAnimation(Animation)} or * {@link #startAnimation(Animation)}. The animation can alter the scale, @@ -548,10 +570,6 @@ import java.util.concurrent.CopyOnWriteArrayList; * subtree rooted by that node. When an animation is started, the framework will * take care of redrawing the appropriate views until the animation completes. * </p> - * <p> - * Starting with Android 3.0, the preferred way of animating views is to use the - * {@link android.animation} package APIs. - * </p> * * <a name="Security"></a> * <h3>Security</h3> @@ -606,8 +624,6 @@ import java.util.concurrent.CopyOnWriteArrayList; * @attr ref android.R.styleable#View_paddingLeft * @attr ref android.R.styleable#View_paddingRight * @attr ref android.R.styleable#View_paddingTop - * @attr ref android.R.styleable#View_paddingStart - * @attr ref android.R.styleable#View_paddingEnd * @attr ref android.R.styleable#View_saveEnabled * @attr ref android.R.styleable#View_rotation * @attr ref android.R.styleable#View_rotationX @@ -629,7 +645,6 @@ import java.util.concurrent.CopyOnWriteArrayList; * @attr ref android.R.styleable#View_scrollbarAlwaysDrawVerticalTrack * @attr ref android.R.styleable#View_soundEffectsEnabled * @attr ref android.R.styleable#View_tag - * @attr ref android.R.styleable#View_textAlignment * @attr ref android.R.styleable#View_transformPivotX * @attr ref android.R.styleable#View_transformPivotY * @attr ref android.R.styleable#View_translationX @@ -1763,24 +1778,28 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal /** * Horizontal layout direction of this view is from Left to Right. * Use with {@link #setLayoutDirection}. + * @hide */ public static final int LAYOUT_DIRECTION_LTR = 0; /** * Horizontal layout direction of this view is from Right to Left. * Use with {@link #setLayoutDirection}. + * @hide */ public static final int LAYOUT_DIRECTION_RTL = 1; /** * Horizontal layout direction of this view is inherited from its parent. * Use with {@link #setLayoutDirection}. + * @hide */ public static final int LAYOUT_DIRECTION_INHERIT = 2; /** * Horizontal layout direction of this view is from deduced from the default language * script for the locale. Use with {@link #setLayoutDirection}. + * @hide */ public static final int LAYOUT_DIRECTION_LOCALE = 3; @@ -1845,6 +1864,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal /** * Text direction is inherited thru {@link ViewGroup} + * @hide */ public static final int TEXT_DIRECTION_INHERIT = 0; @@ -1852,6 +1872,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * Text direction is using "first strong algorithm". The first strong directional character * determines the paragraph direction. If there is no strong directional character, the * paragraph direction is the view's resolved layout direction. + * @hide */ public static final int TEXT_DIRECTION_FIRST_STRONG = 1; @@ -1859,26 +1880,31 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * Text direction is using "any-RTL" algorithm. The paragraph direction is RTL if it contains * any strong RTL character, otherwise it is LTR if it contains any strong LTR characters. * If there are neither, the paragraph direction is the view's resolved layout direction. + * @hide */ public static final int TEXT_DIRECTION_ANY_RTL = 2; /** * Text direction is forced to LTR. + * @hide */ public static final int TEXT_DIRECTION_LTR = 3; /** * Text direction is forced to RTL. + * @hide */ public static final int TEXT_DIRECTION_RTL = 4; /** * Text direction is coming from the system Locale. + * @hide */ public static final int TEXT_DIRECTION_LOCALE = 5; /** * Default text direction is inherited + * @hide */ protected static int TEXT_DIRECTION_DEFAULT = TEXT_DIRECTION_INHERIT; @@ -1936,6 +1962,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal /* * Default text alignment. The text alignment of this View is inherited from its parent. * Use with {@link #setTextAlignment(int)} + * @hide */ public static final int TEXT_ALIGNMENT_INHERIT = 0; @@ -1944,6 +1971,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * ALIGN_CENTER, or ALIGN_OPPOSITE, which are relative to each paragraph’s text direction. * * Use with {@link #setTextAlignment(int)} + * @hide */ public static final int TEXT_ALIGNMENT_GRAVITY = 1; @@ -1951,6 +1979,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * Align to the start of the paragraph, e.g. ALIGN_NORMAL. * * Use with {@link #setTextAlignment(int)} + * @hide */ public static final int TEXT_ALIGNMENT_TEXT_START = 2; @@ -1958,6 +1987,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * Align to the end of the paragraph, e.g. ALIGN_OPPOSITE. * * Use with {@link #setTextAlignment(int)} + * @hide */ public static final int TEXT_ALIGNMENT_TEXT_END = 3; @@ -1965,6 +1995,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * Center the paragraph, e.g. ALIGN_CENTER. * * Use with {@link #setTextAlignment(int)} + * @hide */ public static final int TEXT_ALIGNMENT_CENTER = 4; @@ -1973,6 +2004,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * layoutDirection is LTR, and ALIGN_RIGHT otherwise. * * Use with {@link #setTextAlignment(int)} + * @hide */ public static final int TEXT_ALIGNMENT_VIEW_START = 5; @@ -1981,11 +2013,13 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * layoutDirection is LTR, and ALIGN_LEFT otherwise. * * Use with {@link #setTextAlignment(int)} + * @hide */ public static final int TEXT_ALIGNMENT_VIEW_END = 6; /** * Default text alignment is inherited + * @hide */ protected static int TEXT_ALIGNMENT_DEFAULT = TEXT_ALIGNMENT_GRAVITY; @@ -4681,10 +4715,10 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal } if (getContentDescription() != null) { - info.addAction(AccessibilityNodeInfo.ACTION_NEXT_AT_GRANULARITY); - info.addAction(AccessibilityNodeInfo.ACTION_PREVIOUS_AT_GRANULARITY); - info.setGranularities(AccessibilityNodeInfo.GRANULARITY_CHARACTER - | AccessibilityNodeInfo.GRANULARITY_WORD); + info.addAction(AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY); + info.addAction(AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY); + info.setMovementGranularities(AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER + | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD); } } @@ -5352,7 +5386,9 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * {@link #LAYOUT_DIRECTION_RTL}, * {@link #LAYOUT_DIRECTION_INHERIT} or * {@link #LAYOUT_DIRECTION_LOCALE}. + * * @attr ref android.R.styleable#View_layoutDirection + * @hide */ @ViewDebug.ExportedProperty(category = "layout", mapping = { @ViewDebug.IntToString(from = LAYOUT_DIRECTION_LTR, to = "LTR"), @@ -5374,6 +5410,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * {@link #LAYOUT_DIRECTION_LOCALE}. * * @attr ref android.R.styleable#View_layoutDirection + * @hide */ @RemotableViewMethod public void setLayoutDirection(int layoutDirection) { @@ -5393,6 +5430,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * * @return {@link #LAYOUT_DIRECTION_RTL} if the layout direction is RTL or returns * {@link #LAYOUT_DIRECTION_LTR} if the layout direction is not RTL. + * @hide */ @ViewDebug.ExportedProperty(category = "layout", mapping = { @ViewDebug.IntToString(from = LAYOUT_DIRECTION_LTR, to = "RESOLVED_DIRECTION_LTR"), @@ -5412,6 +5450,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * layout attribute and/or the inherited value from the parent * * @return true if the layout is right-to-left. + * @hide */ @ViewDebug.ExportedProperty(category = "layout") public boolean isLayoutRtl() { @@ -6414,12 +6453,12 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal switch (action) { case AccessibilityNodeInfo.ACTION_CLICK: { if (isClickable()) { - performClick(); + return performClick(); } } break; case AccessibilityNodeInfo.ACTION_LONG_CLICK: { if (isLongClickable()) { - performLongClick(); + return performLongClick(); } } break; case AccessibilityNodeInfo.ACTION_FOCUS: { @@ -9148,7 +9187,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal } public final boolean getLocalVisibleRect(Rect r) { - Point offset = new Point(); + final Point offset = mAttachInfo != null ? mAttachInfo.mPoint : new Point(); if (getGlobalVisibleRect(r, offset)) { r.offset(-offset.x, -offset.y); // make r local return true; @@ -10878,6 +10917,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * Resolve and cache the layout direction. LTR is set initially. This is implicitly supposing * that the parent directionality can and will be resolved before its children. * Will call {@link View#onResolvedLayoutDirectionChanged} when resolution is done. + * @hide */ public void resolveLayoutDirection() { // Clear any previous layout direction resolution @@ -10922,12 +10962,14 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * Called when layout direction has been resolved. * * The default implementation does nothing. + * @hide */ public void onResolvedLayoutDirectionChanged() { } /** * Resolve padding depending on layout direction. + * @hide */ public void resolvePadding() { // If the user specified the absolute padding (either with android:padding or @@ -10987,6 +11029,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * * @see {@link #LAYOUT_DIRECTION_LTR} * @see {@link #LAYOUT_DIRECTION_RTL} + * @hide */ public void onPaddingChanged(int layoutDirection) { } @@ -10995,6 +11038,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * Check if layout direction resolution can be done. * * @return true if layout direction resolution can be done otherwise return false. + * @hide */ public boolean canResolveLayoutDirection() { switch (getLayoutDirection()) { @@ -11008,6 +11052,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal /** * Reset the resolved layout direction. Will call {@link View#onResolvedLayoutDirectionReset} * when reset is done. + * @hide */ public void resetResolvedLayoutDirection() { // Reset the current resolved bits @@ -11024,6 +11069,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * resolved layout direction, or to inform child views that inherit their layout direction. * * The default implementation does nothing. + * @hide */ public void onResolvedLayoutDirectionReset() { } @@ -11033,6 +11079,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * * @param locale Locale to check * @return true if the Locale uses an RTL script. + * @hide */ protected static boolean isLayoutDirectionRtl(Locale locale) { return (LAYOUT_DIRECTION_RTL == LocaleUtil.getLayoutDirectionFromLocale(locale)); @@ -13337,6 +13384,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * Return the layout direction of a given Drawable. * * @param who the Drawable to query + * @hide */ public int getResolvedLayoutDirection(Drawable who) { return (who == mBackground) ? getResolvedLayoutDirection() : LAYOUT_DIRECTION_DEFAULT; @@ -13772,19 +13820,13 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal /** * Sets the relative padding. The view may add on the space required to display * the scrollbars, depending on the style and visibility of the scrollbars. - * So the values returned from {@link #getPaddingStart}, {@link #getPaddingTop}, - * {@link #getPaddingEnd} and {@link #getPaddingBottom} may be different * from the values set in this call. * - * @attr ref android.R.styleable#View_padding - * @attr ref android.R.styleable#View_paddingBottom - * @attr ref android.R.styleable#View_paddingStart - * @attr ref android.R.styleable#View_paddingEnd - * @attr ref android.R.styleable#View_paddingTop * @param start the start padding in pixels * @param top the top padding in pixels * @param end the end padding in pixels * @param bottom the bottom padding in pixels + * @hide */ public void setPaddingRelative(int start, int top, int end, int bottom) { mUserPaddingStart = start; @@ -13838,6 +13880,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * required to display the scrollbars as well. * * @return the start padding in pixels + * @hide */ public int getPaddingStart() { return (getResolvedLayoutDirection() == LAYOUT_DIRECTION_RTL) ? @@ -13861,6 +13904,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * required to display the scrollbars as well. * * @return the end padding in pixels + * @hide */ public int getPaddingEnd() { return (getResolvedLayoutDirection() == LAYOUT_DIRECTION_RTL) ? @@ -13869,11 +13913,10 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal /** * Return if the padding as been set thru relative values - * {@link #setPaddingRelative(int, int, int, int)} or thru - * @attr ref android.R.styleable#View_paddingStart or - * @attr ref android.R.styleable#View_paddingEnd + * {@link #setPaddingRelative(int, int, int, int)} * * @return true if the padding is relative or false if it is not. + * @hide */ public boolean isPaddingRelative() { return mUserPaddingRelative; @@ -15193,6 +15236,18 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal } } + /** @hide */ + public void setDisabledSystemUiVisibility(int flags) { + if (mAttachInfo != null) { + if (mAttachInfo.mDisabledSystemUiVisibility != flags) { + mAttachInfo.mDisabledSystemUiVisibility = flags; + if (mParent != null) { + mParent.recomputeViewAttributes(this); + } + } + } + } + /** * Creates an image that the system displays during the drag and drop * operation. This is called a "drag shadow". The default implementation @@ -15717,6 +15772,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * {@link #TEXT_DIRECTION_LTR}, * {@link #TEXT_DIRECTION_RTL}, * {@link #TEXT_DIRECTION_LOCALE} + * @hide */ @ViewDebug.ExportedProperty(category = "text", mapping = { @ViewDebug.IntToString(from = TEXT_DIRECTION_INHERIT, to = "INHERIT"), @@ -15741,6 +15797,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * {@link #TEXT_DIRECTION_LTR}, * {@link #TEXT_DIRECTION_RTL}, * {@link #TEXT_DIRECTION_LOCALE} + * @hide */ public void setTextDirection(int textDirection) { if (getTextDirection() != textDirection) { @@ -15770,6 +15827,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * {@link #TEXT_DIRECTION_LTR}, * {@link #TEXT_DIRECTION_RTL}, * {@link #TEXT_DIRECTION_LOCALE} + * @hide */ public int getResolvedTextDirection() { // The text direction will be resolved only if needed @@ -15782,6 +15840,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal /** * Resolve the text direction. Will call {@link View#onResolvedTextDirectionChanged} when * resolution is done. + * @hide */ public void resolveTextDirection() { // Reset any previous text direction resolution @@ -15842,6 +15901,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * resolution should override this method. * * The default implementation does nothing. + * @hide */ public void onResolvedTextDirectionChanged() { } @@ -15850,6 +15910,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * Check if text direction resolution can be done. * * @return true if text direction resolution can be done otherwise return false. + * @hide */ public boolean canResolveTextDirection() { switch (getTextDirection()) { @@ -15864,6 +15925,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * Reset resolved text direction. Text direction can be resolved with a call to * getResolvedTextDirection(). Will call {@link View#onResolvedTextDirectionReset} when * reset is done. + * @hide */ public void resetResolvedTextDirection() { mPrivateFlags2 &= ~(TEXT_DIRECTION_RESOLVED | TEXT_DIRECTION_RESOLVED_MASK); @@ -15874,6 +15936,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * Called when text direction is reset. Subclasses that care about text direction reset should * override this method and do a reset of the text direction of their children. The default * implementation does nothing. + * @hide */ public void onResolvedTextDirectionReset() { } @@ -15891,6 +15954,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * {@link #TEXT_ALIGNMENT_TEXT_END}, * {@link #TEXT_ALIGNMENT_VIEW_START}, * {@link #TEXT_ALIGNMENT_VIEW_END} + * @hide */ @ViewDebug.ExportedProperty(category = "text", mapping = { @ViewDebug.IntToString(from = TEXT_ALIGNMENT_INHERIT, to = "INHERIT"), @@ -15919,6 +15983,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * {@link #TEXT_ALIGNMENT_VIEW_END} * * @attr ref android.R.styleable#View_textAlignment + * @hide */ public void setTextAlignment(int textAlignment) { if (textAlignment != getTextAlignment()) { @@ -15948,6 +16013,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * {@link #TEXT_ALIGNMENT_TEXT_END}, * {@link #TEXT_ALIGNMENT_VIEW_START}, * {@link #TEXT_ALIGNMENT_VIEW_END} + * @hide */ @ViewDebug.ExportedProperty(category = "text", mapping = { @ViewDebug.IntToString(from = TEXT_ALIGNMENT_INHERIT, to = "INHERIT"), @@ -15969,6 +16035,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal /** * Resolve the text alignment. Will call {@link View#onResolvedTextAlignmentChanged} when * resolution is done. + * @hide */ public void resolveTextAlignment() { // Reset any previous text alignment resolution @@ -16033,6 +16100,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * Check if text alignment resolution can be done. * * @return true if text alignment resolution can be done otherwise return false. + * @hide */ public boolean canResolveTextAlignment() { switch (getTextAlignment()) { @@ -16048,6 +16116,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * resolution should override this method. * * The default implementation does nothing. + * @hide */ public void onResolvedTextAlignmentChanged() { } @@ -16056,6 +16125,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * Reset resolved text alignment. Text alignment can be resolved with a call to * getResolvedTextAlignment(). Will call {@link View#onResolvedTextAlignmentReset} when * reset is done. + * @hide */ public void resetResolvedTextAlignment() { // Reset any previous text alignment resolution @@ -16067,6 +16137,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * Called when text alignment is reset. Subclasses that care about text alignment reset should * override this method and do a reset of the text alignment of their children. The default * implementation does nothing. + * @hide */ public void onResolvedTextAlignmentReset() { } @@ -16762,6 +16833,12 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal boolean mUse32BitDrawingCache; /** + * Describes the parts of the window that are currently completely + * obscured by system UI elements. + */ + final Rect mSystemInsets = new Rect(); + + /** * For windows that are full-screen but using insets to layout inside * of the screen decorations, these are the current insets for the * content of the window. @@ -16848,6 +16925,11 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal int mSystemUiVisibility; /** + * Hack to force certain system UI visibility flags to be cleared. + */ + int mDisabledSystemUiVisibility; + + /** * True if a view in this hierarchy has an OnSystemUiVisibilityChangeListener * attached. */ @@ -16942,6 +17024,11 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal final boolean mDebugLayout = SystemProperties.getBoolean(DEBUG_LAYOUT_PROPERTY, false); /** + * Point used to compute visible regions. + */ + final Point mPoint = new Point(); + + /** * Creates a new set of attachment information with the specified * events handler and thread. * diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 89ec51f..29613d5 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -348,6 +348,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * This constant is a {@link #setLayoutMode(int) layoutMode}. * Clip bounds are the raw values of {@link #getLeft() left}, {@link #getTop() top}, * {@link #getRight() right} and {@link #getBottom() bottom}. + * + * @hide */ public static final int CLIP_BOUNDS = 0; @@ -356,6 +358,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * Optical bounds describe where a widget appears to be. They sit inside the clip * bounds which need to cover a larger area to allow other effects, * such as shadows and glows, to be drawn. + * + * @hide */ public static final int OPTICAL_BOUNDS = 1; @@ -4539,6 +4543,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * @return the layout mode to use during layout operations * * @see #setLayoutMode(int) + * + * @hide */ public int getLayoutMode() { return mLayoutMode; @@ -4553,6 +4559,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * @param layoutMode the layout mode to use during layout operations * * @see #getLayoutMode() + * + * @hide */ public void setLayoutMode(int layoutMode) { if (mLayoutMode != layoutMode) { @@ -5229,6 +5237,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } } + /** + * @hide + */ @Override public void onResolvedLayoutDirectionReset() { // Take care of resetting the children resolution too @@ -5241,6 +5252,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } } + /** + * @hide + */ @Override public void onResolvedTextDirectionReset() { // Take care of resetting the children resolution too @@ -5253,6 +5267,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } } + /** + * @hide + */ @Override public void onResolvedTextAlignmentReset() { // Take care of resetting the children resolution too @@ -5443,6 +5460,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * * {@link View#LAYOUT_DIRECTION_LTR} * {@link View#LAYOUT_DIRECTION_RTL} + * @hide */ public void onResolveLayoutDirection(int layoutDirection) { } @@ -5534,6 +5552,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * The start margin in pixels of the child. * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value * to this field. + * @hide */ @ViewDebug.ExportedProperty(category = "layout") public int startMargin = DEFAULT_RELATIVE; @@ -5542,6 +5561,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * The end margin in pixels of the child. * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value * to this field. + * @hide */ @ViewDebug.ExportedProperty(category = "layout") public int endMargin = DEFAULT_RELATIVE; @@ -5660,7 +5680,6 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginTop * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginEnd * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginBottom - * * @hide */ public void setMarginsRelative(int start, int top, int end, int bottom) { @@ -5676,6 +5695,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginStart * * @return the start margin in pixels. + * @hide */ public int getMarginStart() { return startMargin; @@ -5687,6 +5707,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginEnd * * @return the end margin in pixels. + * @hide */ public int getMarginEnd() { return endMargin; @@ -5699,6 +5720,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginEnd * * @return true if either marginStart or marginEnd has been set + * @hide */ public boolean isMarginRelative() { return (startMargin != DEFAULT_RELATIVE) || (endMargin != DEFAULT_RELATIVE); @@ -5707,6 +5729,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager /** * This will be called by {@link android.view.View#requestLayout()}. Left and Right margins * may be overridden depending on layout direction. + * @hide */ @Override public void onResolveLayoutDirection(int layoutDirection) { diff --git a/core/java/android/view/ViewPropertyAnimator.java b/core/java/android/view/ViewPropertyAnimator.java index ec37acf..2012db2 100644 --- a/core/java/android/view/ViewPropertyAnimator.java +++ b/core/java/android/view/ViewPropertyAnimator.java @@ -624,14 +624,20 @@ public class ViewPropertyAnimator { /** * The View associated with this ViewPropertyAnimator will have its * {@link View#setLayerType(int, android.graphics.Paint) layer type} set to - * {@link View#LAYER_TYPE_HARDWARE} for the duration of the next animation. This state - * is not persistent, either on the View or on this ViewPropertyAnimator: the layer type - * of the View will be restored when the animation ends to what it was when this method was - * called, and this setting on ViewPropertyAnimator is only valid for the next animation. - * Note that calling this method and then independently setting the layer type of the View - * (by a direct call to {@link View#setLayerType(int, android.graphics.Paint)}) will result - * in some inconsistency, including having the layer type restored to its pre-withLayer() - * value when the animation ends. + * {@link View#LAYER_TYPE_HARDWARE} for the duration of the next animation. + * As stated in the documentation for {@link View#LAYER_TYPE_HARDWARE}, + * the actual type of layer used internally depends on the runtime situation of the + * view. If the activity and this view are hardware-accelerated, then the layer will be + * accelerated as well. If the activity or the view is not accelerated, then the layer will + * effectively be the same as {@link View#LAYER_TYPE_SOFTWARE}. + * + * <p>This state is not persistent, either on the View or on this ViewPropertyAnimator: the + * layer type of the View will be restored when the animation ends to what it was when this + * method was called, and this setting on ViewPropertyAnimator is only valid for the next + * animation. Note that calling this method and then independently setting the layer type of + * the View (by a direct call to {@link View#setLayerType(int, android.graphics.Paint)}) will + * result in some inconsistency, including having the layer type restored to its pre-withLayer() + * value when the animation ends.</p> * * @see View#setLayerType(int, android.graphics.Paint) * @return This object, allowing calls to methods in this class to be chained. diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index e1d821f..ec6bd81 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -230,6 +230,7 @@ public final class ViewRootImpl implements ViewParent, boolean mHasHadWindowFocus; boolean mLastWasImTarget; boolean mWindowsAnimating; + boolean mIsDrawing; int mLastSystemUiVisibility; // Pool of queued input events. @@ -262,6 +263,7 @@ public final class ViewRootImpl implements ViewParent, final Rect mPendingVisibleInsets = new Rect(); final Rect mPendingContentInsets = new Rect(); final Rect mPendingSystemInsets = new Rect(); + final Rect mActiveRect = new Rect(); final ViewTreeObserver.InternalInsetsInfo mLastGivenInsets = new ViewTreeObserver.InternalInsetsInfo(); @@ -271,7 +273,8 @@ public final class ViewRootImpl implements ViewParent, final Configuration mPendingConfiguration = new Configuration(); class ResizedInfo { - Rect coveredInsets; + Rect systemInsets; + Rect contentInsets; Rect visibleInsets; Configuration newConfig; } @@ -567,6 +570,7 @@ public final class ViewRootImpl implements ViewParent, if (mTranslator != null) { mTranslator.translateRectInScreenToAppWindow(mAttachInfo.mContentInsets); } + mPendingSystemInsets.set(0, 0, 0, 0); mPendingContentInsets.set(mAttachInfo.mContentInsets); mPendingVisibleInsets.set(0, 0, 0, 0); if (DEBUG_LAYOUT) Log.v(TAG, "Added window " + mWindow); @@ -763,6 +767,9 @@ public final class ViewRootImpl implements ViewParent, // preserve compatible window flag if exists. int compatibleWindowFlag = mWindowAttributes.flags & WindowManager.LayoutParams.FLAG_COMPATIBLE_WINDOW; + // transfer over system UI visibility values as they carry current state. + attrs.systemUiVisibility = mWindowAttributes.systemUiVisibility; + attrs.subtreeSystemUiVisibility = mWindowAttributes.subtreeSystemUiVisibility; mWindowAttributesChangesFlag = mWindowAttributes.copyFrom(attrs); mWindowAttributes.flags |= compatibleWindowFlag; @@ -1019,6 +1026,7 @@ public final class ViewRootImpl implements ViewParent, attachInfo.mSystemUiVisibility = 0; attachInfo.mHasSystemUiListeners = false; mView.dispatchCollectViewAttributes(attachInfo, 0); + attachInfo.mSystemUiVisibility &= ~attachInfo.mDisabledSystemUiVisibility; if (attachInfo.mKeepScreenOn != oldScreenOn || attachInfo.mSystemUiVisibility != oldVis || attachInfo.mHasSystemUiListeners != oldHasSystemUiListeners) { @@ -1219,6 +1227,7 @@ public final class ViewRootImpl implements ViewParent, getRunQueue().executeActions(attachInfo.mHandler); boolean insetsChanged = false; + boolean activeRectChanged = false; boolean layoutRequested = mLayoutRequested && !mStopped; if (layoutRequested) { @@ -1230,7 +1239,12 @@ public final class ViewRootImpl implements ViewParent, // to opposite of the added touch mode. mAttachInfo.mInTouchMode = !mAddedTouchMode; ensureTouchModeLocally(mAddedTouchMode); + activeRectChanged = true; } else { + if (!mPendingSystemInsets.equals(mAttachInfo.mSystemInsets)) { + mAttachInfo.mSystemInsets.set(mPendingSystemInsets); + activeRectChanged = true; + } if (!mPendingContentInsets.equals(mAttachInfo.mContentInsets)) { insetsChanged = true; } @@ -1383,7 +1397,11 @@ public final class ViewRootImpl implements ViewParent, updateConfiguration(mPendingConfiguration, !mFirst); mPendingConfiguration.seq = 0; } - + + if (!mPendingSystemInsets.equals(mAttachInfo.mSystemInsets)) { + activeRectChanged = true; + mAttachInfo.mSystemInsets.set(mPendingSystemInsets); + } contentInsetsChanged = !mPendingContentInsets.equals( mAttachInfo.mContentInsets); visibleInsetsChanged = !mPendingVisibleInsets.equals( @@ -1486,6 +1504,7 @@ public final class ViewRootImpl implements ViewParent, // before actually drawing them, so it can display then // all at once. newSurface = true; + activeRectChanged = true; mFullRedrawNeeded = true; mPreviousTransparentRegion.setEmpty(); @@ -1550,8 +1569,11 @@ public final class ViewRootImpl implements ViewParent, // !!FIXME!! This next section handles the case where we did not get the // window size we asked for. We should avoid this by getting a maximum size from // the window session beforehand. - mWidth = frame.width(); - mHeight = frame.height(); + if (mWidth != frame.width() || mHeight != frame.height()) { + activeRectChanged = true; + mWidth = frame.width(); + mHeight = frame.height(); + } if (mSurfaceHolder != null) { // The app owns the surface; tell it about what is going on. @@ -1667,6 +1689,14 @@ public final class ViewRootImpl implements ViewParent, } } + if (activeRectChanged && mSurface.isValid()) { + mActiveRect.set(attachInfo.mSystemInsets.left, attachInfo.mSystemInsets.top, + mWidth - attachInfo.mSystemInsets.right, + mHeight - attachInfo.mSystemInsets.bottom); + //Log.i(TAG, "Active rect " + mWindowAttributes.getTitle() + ": " + mActiveRect); + mSurface.setActiveRect(mActiveRect); + } + final boolean didLayout = layoutRequested && !mStopped; boolean triggerGlobalLayoutListener = didLayout || attachInfo.mRecomputeGlobalAttributes; @@ -2009,10 +2039,12 @@ public final class ViewRootImpl implements ViewParent, final boolean fullRedrawNeeded = mFullRedrawNeeded; mFullRedrawNeeded = false; + mIsDrawing = true; Trace.traceBegin(Trace.TRACE_TAG_VIEW, "draw"); try { draw(fullRedrawNeeded); } finally { + mIsDrawing = false; Trace.traceEnd(Trace.TRACE_TAG_VIEW); } @@ -2781,7 +2813,8 @@ public final class ViewRootImpl implements ViewParent, ResizedInfo ri = (ResizedInfo)msg.obj; if (mWinFrame.width() == msg.arg1 && mWinFrame.height() == msg.arg2 - && mPendingContentInsets.equals(ri.coveredInsets) + && mPendingSystemInsets.equals(ri.systemInsets) + && mPendingContentInsets.equals(ri.contentInsets) && mPendingVisibleInsets.equals(ri.visibleInsets) && ((ResizedInfo)msg.obj).newConfig == null) { break; @@ -2797,7 +2830,8 @@ public final class ViewRootImpl implements ViewParent, mWinFrame.right = msg.arg1; mWinFrame.top = 0; mWinFrame.bottom = msg.arg2; - mPendingContentInsets.set(((ResizedInfo)msg.obj).coveredInsets); + mPendingSystemInsets.set(((ResizedInfo)msg.obj).systemInsets); + mPendingContentInsets.set(((ResizedInfo)msg.obj).contentInsets); mPendingVisibleInsets.set(((ResizedInfo)msg.obj).visibleInsets); if (msg.what == MSG_RESIZED_REPORT) { mReportNextDraw = true; @@ -3932,7 +3966,12 @@ public final class ViewRootImpl implements ViewParent, if (immediate) { doDie(); } else { - destroyHardwareRenderer(); + if (!mIsDrawing) { + destroyHardwareRenderer(); + } else { + Log.e(TAG, "Attempting to destroy the window while drawing!\n" + + " window=" + this + ", title=" + mWindowAttributes.getTitle()); + } mHandler.sendEmptyMessage(MSG_DIE); } } @@ -4006,15 +4045,16 @@ public final class ViewRootImpl implements ViewParent, mHandler.sendMessage(msg); } - public void dispatchResized(int w, int h, Rect coveredInsets, + public void dispatchResized(int w, int h, Rect systemInsets, Rect contentInsets, Rect visibleInsets, boolean reportDraw, Configuration newConfig) { if (DEBUG_LAYOUT) Log.v(TAG, "Resizing " + this + ": w=" + w - + " h=" + h + " coveredInsets=" + coveredInsets.toShortString() + + " h=" + h + " systemInsets=" + systemInsets.toShortString() + + " contentInsets=" + contentInsets.toShortString() + " visibleInsets=" + visibleInsets.toShortString() + " reportDraw=" + reportDraw); Message msg = mHandler.obtainMessage(reportDraw ? MSG_RESIZED_REPORT :MSG_RESIZED); if (mTranslator != null) { - mTranslator.translateRectInScreenToAppWindow(coveredInsets); + mTranslator.translateRectInScreenToAppWindow(contentInsets); mTranslator.translateRectInScreenToAppWindow(visibleInsets); w *= mTranslator.applicationInvertedScale; h *= mTranslator.applicationInvertedScale; @@ -4022,7 +4062,8 @@ public final class ViewRootImpl implements ViewParent, msg.arg1 = w; msg.arg2 = h; ResizedInfo ri = new ResizedInfo(); - ri.coveredInsets = new Rect(coveredInsets); + ri.systemInsets = new Rect(systemInsets); + ri.contentInsets = new Rect(contentInsets); ri.visibleInsets = new Rect(visibleInsets); ri.newConfig = newConfig; msg.obj = ri; @@ -4673,8 +4714,8 @@ public final class ViewRootImpl implements ViewParent, Rect visibleInsets, boolean reportDraw, Configuration newConfig) { final ViewRootImpl viewAncestor = mViewAncestor.get(); if (viewAncestor != null) { - viewAncestor.dispatchResized(w, h, contentInsets, visibleInsets, reportDraw, - newConfig); + viewAncestor.dispatchResized(w, h, systemInsets, contentInsets, + visibleInsets, reportDraw, newConfig); } } diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java index 6d1166e..f70ffa9 100644 --- a/core/java/android/view/accessibility/AccessibilityEvent.java +++ b/core/java/android/view/accessibility/AccessibilityEvent.java @@ -226,9 +226,9 @@ import java.util.List; * <li>{@link #getContentDescription()} - The content description of the source.</li> * </ul> * </p> - * <b>View text traversed at granularity</b> - represents the event of traversing the + * <b>View text traversed at movement granularity</b> - represents the event of traversing the * text of a view at a given granularity. For example, moving to the next word.</br> - * <em>Type:</em> {@link #TYPE_VIEW_TEXT_TRAVERSED_AT_GRANULARITY} </br> + * <em>Type:</em> {@link #TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY} </br> * <em>Properties:</em></br> * <ul> * <li>{@link #getEventType()} - The type of the event.</li> @@ -236,11 +236,12 @@ import java.util.List; * <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 current text at the granularity.</li> + * <li>{@link #getText()} - The text of the current text at the movement granularity.</li> * <li>{@link #isPassword()} - Whether the source is password.</li> * <li>{@link #isEnabled()} - Whether the source is enabled.</li> * <li>{@link #getContentDescription()} - The content description of the source.</li> - * <li>{@link #getGranularity()} - Sets the granularity at which a view's text was traversed.</li> + * <li>{@link #getMovementGranularity()} - Sets the granularity at which a view's text + * was traversed.</li> * </ul> * </p> * <p> @@ -597,9 +598,9 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par public static final int TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED = 0x00010000; /** - * Represents the event of traversing the text of a view at a given granularity. + * Represents the event of traversing the text of a view at a given movement granularity. */ - public static final int TYPE_VIEW_TEXT_TRAVERSED_AT_GRANULARITY = 0x00020000; + public static final int TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY = 0x00020000; /** * Mask for {@link AccessibilityEvent} all types. @@ -619,7 +620,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par * @see #TYPE_VIEW_SCROLLED * @see #TYPE_VIEW_TEXT_SELECTION_CHANGED * @see #TYPE_ANNOUNCEMENT - * @see #TYPE_VIEW_TEXT_TRAVERSED_AT_GRANULARITY + * @see #TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY */ public static final int TYPES_ALL_MASK = 0xFFFFFFFF; @@ -633,7 +634,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par private int mEventType; private CharSequence mPackageName; private long mEventTime; - int mGranularity; + int mMovementGranularity; private final ArrayList<AccessibilityRecord> mRecords = new ArrayList<AccessibilityRecord>(); @@ -651,7 +652,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par void init(AccessibilityEvent event) { super.init(event); mEventType = event.mEventType; - mGranularity = event.mGranularity; + mMovementGranularity = event.mMovementGranularity; mEventTime = event.mEventTime; mPackageName = event.mPackageName; } @@ -769,24 +770,24 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par } /** - * Sets the text granularity that was traversed. + * Sets the movement granularity that was traversed. * * @param granularity The granularity. * * @throws IllegalStateException If called from an AccessibilityService. */ - public void setGranularity(int granularity) { + public void setMovementGranularity(int granularity) { enforceNotSealed(); - mGranularity = granularity; + mMovementGranularity = granularity; } /** - * Gets the text granularity that was traversed. + * Gets the movement granularity that was traversed. * * @return The granularity. */ - public int getGranularity() { - return mGranularity; + public int getMovementGranularity() { + return mMovementGranularity; } /** @@ -877,7 +878,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par protected void clear() { super.clear(); mEventType = 0; - mGranularity = 0; + mMovementGranularity = 0; mPackageName = null; mEventTime = 0; while (!mRecords.isEmpty()) { @@ -894,7 +895,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par public void initFromParcel(Parcel parcel) { mSealed = (parcel.readInt() == 1); mEventType = parcel.readInt(); - mGranularity = parcel.readInt(); + mMovementGranularity = parcel.readInt(); mPackageName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel); mEventTime = parcel.readLong(); mConnectionId = parcel.readInt(); @@ -945,7 +946,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par public void writeToParcel(Parcel parcel, int flags) { parcel.writeInt(isSealed() ? 1 : 0); parcel.writeInt(mEventType); - parcel.writeInt(mGranularity); + parcel.writeInt(mMovementGranularity); TextUtils.writeToParcel(mPackageName, parcel, 0); parcel.writeLong(mEventTime); parcel.writeInt(mConnectionId); @@ -1002,7 +1003,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par builder.append("EventType: ").append(eventTypeToString(mEventType)); builder.append("; EventTime: ").append(mEventTime); builder.append("; PackageName: ").append(mPackageName); - builder.append("; Granularity: ").append(mGranularity); + builder.append("; MovementGranularity: ").append(mMovementGranularity); builder.append(super.toString()); if (DEBUG) { builder.append("\n"); @@ -1083,8 +1084,8 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par return "TYPE_VIEW_ACCESSIBILITY_FOCUSED"; case TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED: return "TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED"; - case TYPE_VIEW_TEXT_TRAVERSED_AT_GRANULARITY: - return "TYPE_CURRENT_AT_GRANULARITY_CHANGED"; + case TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY: + return "TYPE_CURRENT_AT_GRANULARITY_MOVEMENT_CHANGED"; default: return null; } diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java index 5266c83..c0696a9 100644 --- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java +++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java @@ -123,53 +123,56 @@ public class AccessibilityNodeInfo implements Parcelable { /** * Action that requests to go to the next entity in this node's text - * at a given granularity. For example, move to the next character, word, etc. + * at a given movement granularity. For example, move to the next character, + * word, etc. * <p> - * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_GRANULARITY_INT}<br> + * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT}<br> * <strong>Example:</strong> * <code><pre><p> * Bundle arguments = new Bundle(); - * arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_GRANULARITY_INT, - * AccessibilityNodeInfo.GRANULARITY_CHARACTER); - * info.performAction(AccessibilityNodeInfo.ACTION_NEXT_AT_GRANULARITY, arguments); + * arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT, + * AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER); + * info.performAction(AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, arguments); * </code></pre></p> * </p> * - * @see #setGranularities(int) - * @see #getGranularities() + * @see #setMovementGranularities(int) + * @see #getMovementGranularities() * - * @see #GRANULARITY_CHARACTER - * @see #GRANULARITY_WORD - * @see #GRANULARITY_LINE - * @see #GRANULARITY_PARAGRAPH - * @see #GRANULARITY_PAGE + * @see #MOVEMENT_GRANULARITY_CHARACTER + * @see #MOVEMENT_GRANULARITY_WORD + * @see #MOVEMENT_GRANULARITY_LINE + * @see #MOVEMENT_GRANULARITY_PARAGRAPH + * @see #MOVEMENT_GRANULARITY_PAGE */ - public static final int ACTION_NEXT_AT_GRANULARITY = 0x00000100; + public static final int ACTION_NEXT_AT_MOVEMENT_GRANULARITY = 0x00000100; /** * Action that requests to go to the previous entity in this node's text - * at a given granularity. For example, move to the next character, word, etc. + * at a given movement granularity. For example, move to the next character, + * word, etc. * <p> - * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_GRANULARITY_INT}<br> + * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT}<br> * <strong>Example:</strong> * <code><pre><p> * Bundle arguments = new Bundle(); - * arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_GRANULARITY_INT, - * AccessibilityNodeInfo.GRANULARITY_CHARACTER); - * info.performAction(AccessibilityNodeInfo.ACTION_PREVIOUS_AT_GRANULARITY, arguments); + * arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT, + * AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER); + * info.performAction(AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, + * arguments); * </code></pre></p> * </p> * - * @see #setGranularities(int) - * @see #getGranularities() + * @see #setMovementGranularities(int) + * @see #getMovementGranularities() * - * @see #GRANULARITY_CHARACTER - * @see #GRANULARITY_WORD - * @see #GRANULARITY_LINE - * @see #GRANULARITY_PARAGRAPH - * @see #GRANULARITY_PAGE + * @see #MOVEMENT_GRANULARITY_CHARACTER + * @see #MOVEMENT_GRANULARITY_WORD + * @see #MOVEMENT_GRANULARITY_LINE + * @see #MOVEMENT_GRANULARITY_PARAGRAPH + * @see #MOVEMENT_GRANULARITY_PAGE */ - public static final int ACTION_PREVIOUS_AT_GRANULARITY = 0x00000200; + public static final int ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY = 0x00000200; /** * Action to move to the next HTML element of a given type. For example, move @@ -202,14 +205,15 @@ public class AccessibilityNodeInfo implements Parcelable { public static final int ACTION_PREVIOUS_HTML_ELEMENT = 0x00000800; /** - * Argument for which text granularity to be used when traversing the node text. + * Argument for which movement granularity to be used when traversing the node text. * <p> * <strong>Type:</strong> int<br> - * <strong>Actions:</strong> {@link #ACTION_NEXT_AT_GRANULARITY}, - * {@link #ACTION_PREVIOUS_AT_GRANULARITY} + * <strong>Actions:</strong> {@link #ACTION_NEXT_AT_MOVEMENT_GRANULARITY}, + * {@link #ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY} * </p> */ - public static final String ACTION_ARGUMENT_GRANULARITY_INT = "ACTION_ARGUMENT_GRANULARITY_INT"; + public static final String ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT = + "ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT"; /** * Argument for which HTML element to get moving to the next/previous HTML element. @@ -232,32 +236,32 @@ public class AccessibilityNodeInfo implements Parcelable { */ public static final int FOCUS_ACCESSIBILITY = 2; - // Granularities + // Movement granularities /** - * Granularity bit for traversing the text of a node by character. + * Movement granularity bit for traversing the text of a node by character. */ - public static final int GRANULARITY_CHARACTER = 0x00000001; + public static final int MOVEMENT_GRANULARITY_CHARACTER = 0x00000001; /** - * Granularity bit for traversing the text of a node by word. + * Movement granularity bit for traversing the text of a node by word. */ - public static final int GRANULARITY_WORD = 0x00000002; + public static final int MOVEMENT_GRANULARITY_WORD = 0x00000002; /** - * Granularity bit for traversing the text of a node by line. + * Movement granularity bit for traversing the text of a node by line. */ - public static final int GRANULARITY_LINE = 0x00000004; + public static final int MOVEMENT_GRANULARITY_LINE = 0x00000004; /** - * Granularity bit for traversing the text of a node by paragraph. + * Movement granularity bit for traversing the text of a node by paragraph. */ - public static final int GRANULARITY_PARAGRAPH = 0x00000008; + public static final int MOVEMENT_GRANULARITY_PARAGRAPH = 0x00000008; /** - * Granularity bit for traversing the text of a node by page. + * Movement granularity bit for traversing the text of a node by page. */ - public static final int GRANULARITY_PAGE = 0x00000010; + public static final int MOVEMENT_GRANULARITY_PAGE = 0x00000010; // Boolean attributes. @@ -362,7 +366,7 @@ public class AccessibilityNodeInfo implements Parcelable { private final SparseLongArray mChildNodeIds = new SparseLongArray(); private int mActions; - private int mGranularities; + private int mMovementGranularities; private int mConnectionId = UNDEFINED; @@ -586,7 +590,7 @@ public class AccessibilityNodeInfo implements Parcelable { } /** - * Sets the text granularities for traversing the text of this node. + * Sets the movement granularities for traversing the text of this node. * <p> * <strong>Note:</strong> Cannot be called from an * {@link android.accessibilityservice.AccessibilityService}. @@ -597,18 +601,18 @@ public class AccessibilityNodeInfo implements Parcelable { * * @throws IllegalStateException If called from an AccessibilityService. */ - public void setGranularities(int granularities) { + public void setMovementGranularities(int granularities) { enforceNotSealed(); - mGranularities = granularities; + mMovementGranularities = granularities; } /** - * Gets the granularities for traversing the text of this node. + * Gets the movement granularities for traversing the text of this node. * * @return The bit mask with granularities. */ - public int getGranularities() { - return mGranularities; + public int getMovementGranularities() { + return mMovementGranularities; } /** @@ -1412,7 +1416,7 @@ public class AccessibilityNodeInfo implements Parcelable { parcel.writeInt(mActions); - parcel.writeInt(mGranularities); + parcel.writeInt(mMovementGranularities); parcel.writeInt(mBooleanProperties); @@ -1446,7 +1450,7 @@ public class AccessibilityNodeInfo implements Parcelable { mContentDescription = other.mContentDescription; mActions= other.mActions; mBooleanProperties = other.mBooleanProperties; - mGranularities = other.mGranularities; + mMovementGranularities = other.mMovementGranularities; final int otherChildIdCount = other.mChildNodeIds.size(); for (int i = 0; i < otherChildIdCount; i++) { mChildNodeIds.put(i, other.mChildNodeIds.valueAt(i)); @@ -1484,7 +1488,7 @@ public class AccessibilityNodeInfo implements Parcelable { mActions = parcel.readInt(); - mGranularities = parcel.readInt(); + mMovementGranularities = parcel.readInt(); mBooleanProperties = parcel.readInt(); @@ -1503,7 +1507,7 @@ public class AccessibilityNodeInfo implements Parcelable { mParentNodeId = ROOT_NODE_ID; mWindowId = UNDEFINED; mConnectionId = UNDEFINED; - mGranularities = 0; + mMovementGranularities = 0; mChildNodeIds.clear(); mBoundsInParent.set(0, 0, 0, 0); mBoundsInScreen.set(0, 0, 0, 0); @@ -1539,10 +1543,10 @@ public class AccessibilityNodeInfo implements Parcelable { return "ACTION_ACCESSIBILITY_FOCUS"; case ACTION_CLEAR_ACCESSIBILITY_FOCUS: return "ACTION_CLEAR_ACCESSIBILITY_FOCUS"; - case ACTION_NEXT_AT_GRANULARITY: - return "ACTION_NEXT_AT_GRANULARITY"; - case ACTION_PREVIOUS_AT_GRANULARITY: - return "ACTION_PREVIOUS_AT_GRANULARITY"; + case ACTION_NEXT_AT_MOVEMENT_GRANULARITY: + return "ACTION_NEXT_AT_MOVEMENT_GRANULARITY"; + case ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY: + return "ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY"; case ACTION_NEXT_HTML_ELEMENT: return "ACTION_NEXT_HTML_ELEMENT"; case ACTION_PREVIOUS_HTML_ELEMENT: @@ -1553,25 +1557,25 @@ public class AccessibilityNodeInfo implements Parcelable { } /** - * Gets the human readable granularity symbolic name. + * Gets the human readable movement granularity symbolic name. * - * @param granularity The action. + * @param granularity The granularity. * @return The symbolic name. */ - private static String getGranularitySymbolicName(int granularity) { + private static String getMovementGranularitySymbolicName(int granularity) { switch (granularity) { - case GRANULARITY_CHARACTER: - return "GRANULARITY_CHARACTER"; - case GRANULARITY_WORD: - return "GRANULARITY_WORD"; - case GRANULARITY_LINE: - return "GRANULARITY_LINE"; - case GRANULARITY_PARAGRAPH: - return "GRANULARITY_PARAGRAPH"; - case GRANULARITY_PAGE: - return "GRANULARITY_PAGE"; + case MOVEMENT_GRANULARITY_CHARACTER: + return "MOVEMENT_GRANULARITY_CHARACTER"; + case MOVEMENT_GRANULARITY_WORD: + return "MOVEMENT_GRANULARITY_WORD"; + case MOVEMENT_GRANULARITY_LINE: + return "MOVEMENT_GRANULARITY_LINE"; + case MOVEMENT_GRANULARITY_PARAGRAPH: + return "MOVEMENT_GRANULARITY_PARAGRAPH"; + case MOVEMENT_GRANULARITY_PAGE: + return "MOVEMENT_GRANULARITY_PAGE"; default: - throw new IllegalArgumentException("Unknown granularity: " + granularity); + throw new IllegalArgumentException("Unknown movement granularity: " + granularity); } } @@ -1622,12 +1626,12 @@ public class AccessibilityNodeInfo implements Parcelable { builder.append("; virtualDescendantId: " + getVirtualDescendantId(mSourceNodeId)); builder.append("; mParentNodeId: " + mParentNodeId); - int granularities = mGranularities; - builder.append("; granularities: ["); + int granularities = mMovementGranularities; + builder.append("; MovementGranularities: ["); while (granularities != 0) { final int granularity = 1 << Integer.numberOfTrailingZeros(granularities); granularities &= ~granularity; - builder.append(getGranularitySymbolicName(granularity)); + builder.append(getMovementGranularitySymbolicName(granularity)); if (granularities != 0) { builder.append(", "); } diff --git a/core/java/android/webkit/AutoCompletePopup.java b/core/java/android/webkit/AutoCompletePopup.java index 21d5e02..87e878b 100644 --- a/core/java/android/webkit/AutoCompletePopup.java +++ b/core/java/android/webkit/AutoCompletePopup.java @@ -28,12 +28,15 @@ import android.widget.Filter; import android.widget.Filterable; import android.widget.ListAdapter; import android.widget.ListPopupWindow; +import android.widget.PopupWindow.OnDismissListener; -class AutoCompletePopup implements OnItemClickListener, Filter.FilterListener { +class AutoCompletePopup implements OnItemClickListener, Filter.FilterListener, + OnDismissListener{ private static class AnchorView extends View { AnchorView(Context context) { super(context); setFocusable(false); + setVisibility(INVISIBLE); } } private static final int AUTOFILL_FORM = 100; @@ -48,17 +51,10 @@ class AutoCompletePopup implements OnItemClickListener, Filter.FilterListener { private WebViewClassic.WebViewInputConnection mInputConnection; private WebViewClassic mWebView; - public AutoCompletePopup(Context context, - WebViewClassic webView, + public AutoCompletePopup(WebViewClassic webView, WebViewClassic.WebViewInputConnection inputConnection) { mInputConnection = inputConnection; mWebView = webView; - mPopup = new ListPopupWindow(context); - mAnchor = new AnchorView(context); - mWebView.getWebView().addView(mAnchor); - mPopup.setOnItemClickListener(this); - mPopup.setAnchorView(mAnchor); - mPopup.setPromptPosition(ListPopupWindow.POSITION_PROMPT_BELOW); mHandler = new Handler() { @Override public void handleMessage(Message msg) { @@ -72,6 +68,9 @@ class AutoCompletePopup implements OnItemClickListener, Filter.FilterListener { } public boolean onKeyPreIme(int keyCode, KeyEvent event) { + if (mPopup == null) { + return false; + } if (keyCode == KeyEvent.KEYCODE_BACK && mPopup.isShowing()) { // special case for the back key, we do not even try to send it // to the drop down list but instead, consume it immediately @@ -112,11 +111,14 @@ class AutoCompletePopup implements OnItemClickListener, Filter.FilterListener { public void clearAdapter() { mAdapter = null; mFilter = null; - mPopup.dismiss(); - mPopup.setAdapter(null); + if (mPopup != null) { + mPopup.dismiss(); + mPopup.setAdapter(null); + } } public <T extends ListAdapter & Filterable> void setAdapter(T adapter) { + ensurePopup(); mPopup.setAdapter(adapter); mAdapter = adapter; if (adapter != null) { @@ -129,6 +131,7 @@ class AutoCompletePopup implements OnItemClickListener, Filter.FilterListener { } public void resetRect() { + ensurePopup(); int left = mWebView.contentToViewX(mWebView.mEditTextContentBounds.left); int right = mWebView.contentToViewX(mWebView.mEditTextContentBounds.right); int width = right - left; @@ -164,6 +167,9 @@ class AutoCompletePopup implements OnItemClickListener, Filter.FilterListener { // AdapterView.OnItemClickListener implementation @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { + if (mPopup == null) { + return; + } if (id == 0 && position == 0 && mInputConnection.getIsAutoFillable()) { mText = ""; pushTextToInputConnection(); @@ -206,6 +212,7 @@ class AutoCompletePopup implements OnItemClickListener, Filter.FilterListener { @Override public void onFilterComplete(int count) { + ensurePopup(); boolean showDropDown = (count > 0) && (mInputConnection.getIsAutoFillable() || mText.length() > 0); if (showDropDown) { @@ -219,5 +226,23 @@ class AutoCompletePopup implements OnItemClickListener, Filter.FilterListener { mPopup.dismiss(); } } + + @Override + public void onDismiss() { + mWebView.getWebView().removeView(mAnchor); + } + + private void ensurePopup() { + if (mPopup == null) { + mPopup = new ListPopupWindow(mWebView.getContext()); + mAnchor = new AnchorView(mWebView.getContext()); + mWebView.getWebView().addView(mAnchor); + mPopup.setOnItemClickListener(this); + mPopup.setAnchorView(mAnchor); + mPopup.setPromptPosition(ListPopupWindow.POSITION_PROMPT_BELOW); + } else if (mWebView.getWebView().indexOfChild(mAnchor) < 0) { + mWebView.getWebView().addView(mAnchor); + } + } } diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java index c169de4..fe812af 100644 --- a/core/java/android/webkit/BrowserFrame.java +++ b/core/java/android/webkit/BrowserFrame.java @@ -56,6 +56,8 @@ import java.util.Map; import java.util.Set; import org.apache.harmony.security.provider.cert.X509CertImpl; +import org.apache.harmony.xnet.provider.jsse.OpenSSLDSAPrivateKey; +import org.apache.harmony.xnet.provider.jsse.OpenSSLRSAPrivateKey; class BrowserFrame extends Handler { @@ -1104,12 +1106,23 @@ class BrowserFrame extends Handler { SslClientCertLookupTable table = SslClientCertLookupTable.getInstance(); if (table.IsAllowed(hostAndPort)) { // previously allowed - nativeSslClientCert(handle, - table.PrivateKey(hostAndPort), - table.CertificateChain(hostAndPort)); + PrivateKey pkey = table.PrivateKey(hostAndPort); + if (pkey instanceof OpenSSLRSAPrivateKey) { + nativeSslClientCert(handle, + ((OpenSSLRSAPrivateKey)pkey).getPkeyContext(), + table.CertificateChain(hostAndPort)); + } else if (pkey instanceof OpenSSLDSAPrivateKey) { + nativeSslClientCert(handle, + ((OpenSSLDSAPrivateKey)pkey).getPkeyContext(), + table.CertificateChain(hostAndPort)); + } else { + nativeSslClientCert(handle, + pkey.getEncoded(), + table.CertificateChain(hostAndPort)); + } } else if (table.IsDenied(hostAndPort)) { // previously denied - nativeSslClientCert(handle, null, null); + nativeSslClientCert(handle, 0, null); } else { // previously ignored or new mCallbackProxy.onReceivedClientCertRequest( @@ -1296,7 +1309,11 @@ class BrowserFrame extends Handler { private native void nativeSslCertErrorCancel(int handle, int certError); native void nativeSslClientCert(int handle, - byte[] pkcs8EncodedPrivateKey, + int ctx, + byte[][] asn1DerEncodedCertificateChain); + + native void nativeSslClientCert(int handle, + byte[] pkey, byte[][] asn1DerEncodedCertificateChain); /** diff --git a/core/java/android/webkit/ClientCertRequestHandler.java b/core/java/android/webkit/ClientCertRequestHandler.java index f862613..6570a9b8 100644 --- a/core/java/android/webkit/ClientCertRequestHandler.java +++ b/core/java/android/webkit/ClientCertRequestHandler.java @@ -21,6 +21,8 @@ import java.security.PrivateKey; import java.security.cert.CertificateEncodingException; import java.security.cert.X509Certificate; import org.apache.harmony.xnet.provider.jsse.NativeCrypto; +import org.apache.harmony.xnet.provider.jsse.OpenSSLDSAPrivateKey; +import org.apache.harmony.xnet.provider.jsse.OpenSSLRSAPrivateKey; /** * ClientCertRequestHandler: class responsible for handling client @@ -50,20 +52,23 @@ public final class ClientCertRequestHandler extends Handler { * Proceed with the specified private key and client certificate chain. */ public void proceed(PrivateKey privateKey, X509Certificate[] chain) { - final byte[] privateKeyBytes = privateKey.getEncoded(); - final byte[][] chainBytes; try { - chainBytes = NativeCrypto.encodeCertificates(chain); - mTable.Allow(mHostAndPort, privateKeyBytes, chainBytes); - post(new Runnable() { - public void run() { - mBrowserFrame.nativeSslClientCert(mHandle, privateKeyBytes, chainBytes); - } - }); + byte[][] chainBytes = NativeCrypto.encodeCertificates(chain); + mTable.Allow(mHostAndPort, privateKey, chainBytes); + + if (privateKey instanceof OpenSSLRSAPrivateKey) { + setSslClientCertFromCtx(((OpenSSLRSAPrivateKey)privateKey).getPkeyContext(), + chainBytes); + } else if (privateKey instanceof OpenSSLDSAPrivateKey) { + setSslClientCertFromCtx(((OpenSSLDSAPrivateKey)privateKey).getPkeyContext(), + chainBytes); + } else { + setSslClientCertFromPKCS8(privateKey.getEncoded(),chainBytes); + } } catch (CertificateEncodingException e) { post(new Runnable() { public void run() { - mBrowserFrame.nativeSslClientCert(mHandle, null, null); + mBrowserFrame.nativeSslClientCert(mHandle, 0, null); return; } }); @@ -71,12 +76,34 @@ public final class ClientCertRequestHandler extends Handler { } /** + * Proceed with the specified private key bytes and client certificate chain. + */ + private void setSslClientCertFromCtx(final int ctx, final byte[][] chainBytes) { + post(new Runnable() { + public void run() { + mBrowserFrame.nativeSslClientCert(mHandle, ctx, chainBytes); + } + }); + } + + /** + * Proceed with the specified private key context and client certificate chain. + */ + private void setSslClientCertFromPKCS8(final byte[] key, final byte[][] chainBytes) { + post(new Runnable() { + public void run() { + mBrowserFrame.nativeSslClientCert(mHandle, key, chainBytes); + } + }); + } + + /** * Igore the request for now, the user may be prompted again. */ public void ignore() { post(new Runnable() { public void run() { - mBrowserFrame.nativeSslClientCert(mHandle, null, null); + mBrowserFrame.nativeSslClientCert(mHandle, 0, null); } }); } @@ -88,7 +115,7 @@ public final class ClientCertRequestHandler extends Handler { mTable.Deny(mHostAndPort); post(new Runnable() { public void run() { - mBrowserFrame.nativeSslClientCert(mHandle, null, null); + mBrowserFrame.nativeSslClientCert(mHandle, 0, null); } }); } diff --git a/core/java/android/webkit/SslClientCertLookupTable.java b/core/java/android/webkit/SslClientCertLookupTable.java index 630debd..c52b7e8 100644 --- a/core/java/android/webkit/SslClientCertLookupTable.java +++ b/core/java/android/webkit/SslClientCertLookupTable.java @@ -16,6 +16,7 @@ package android.webkit; +import java.security.PrivateKey; import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -26,7 +27,7 @@ import java.util.Set; */ final class SslClientCertLookupTable { private static SslClientCertLookupTable sTable; - private final Map<String, byte[]> privateKeys; + private final Map<String, PrivateKey> privateKeys; private final Map<String, byte[][]> certificateChains; private final Set<String> denied; @@ -38,12 +39,12 @@ final class SslClientCertLookupTable { } private SslClientCertLookupTable() { - privateKeys = new HashMap<String, byte[]>(); + privateKeys = new HashMap<String, PrivateKey>(); certificateChains = new HashMap<String, byte[][]>(); denied = new HashSet<String>(); } - public void Allow(String host_and_port, byte[] privateKey, byte[][] chain) { + public void Allow(String host_and_port, PrivateKey privateKey, byte[][] chain) { privateKeys.put(host_and_port, privateKey); certificateChains.put(host_and_port, chain); denied.remove(host_and_port); @@ -63,7 +64,7 @@ final class SslClientCertLookupTable { return denied.contains(host_and_port); } - public byte[] PrivateKey(String host_and_port) { + public PrivateKey PrivateKey(String host_and_port) { return privateKeys.get(host_and_port); } diff --git a/core/java/android/webkit/WebViewClassic.java b/core/java/android/webkit/WebViewClassic.java index f0a9cc2..eb5f835 100644 --- a/core/java/android/webkit/WebViewClassic.java +++ b/core/java/android/webkit/WebViewClassic.java @@ -153,7 +153,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc @Override public void onGlobalLayout() { if (mWebView.isShown()) { - setGLRectViewport(); + setInvScreenRect(); } } } @@ -162,7 +162,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc @Override public void onScrollChanged() { if (mWebView.isShown()) { - setGLRectViewport(); + setInvScreenRect(); } } } @@ -644,8 +644,8 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc private ZoomManager mZoomManager; - private final Rect mGLRectViewport = new Rect(); - private final Rect mViewRectViewport = new Rect(); + private final Rect mInvScreenRect = new Rect(); + private final Rect mScreenRect = new Rect(); private final RectF mVisibleContentRect = new RectF(); private boolean mGLViewportEmpty = false; WebViewInputConnection mInputConnection = null; @@ -935,7 +935,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc private Paint mTouchCrossHairColor; private int mTouchHighlightX; private int mTouchHighlightY; - private long mTouchHighlightRequested; + private boolean mShowTapHighlight; // Basically this proxy is used to tell the Video to update layer tree at // SetBaseLayer time and to pause when WebView paused. @@ -4224,11 +4224,11 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc calcOurContentVisibleRectF(mVisibleContentRect); if (canvas.isHardwareAccelerated()) { - Rect glRectViewport = mGLViewportEmpty ? null : mGLRectViewport; - Rect viewRectViewport = mGLViewportEmpty ? null : mViewRectViewport; + Rect invScreenRect = mGLViewportEmpty ? null : mInvScreenRect; + Rect screenRect = mGLViewportEmpty ? null : mScreenRect; - int functor = nativeCreateDrawGLFunction(mNativeClass, glRectViewport, - viewRectViewport, mVisibleContentRect, getScale(), extras); + int functor = nativeCreateDrawGLFunction(mNativeClass, invScreenRect, + screenRect, mVisibleContentRect, getScale(), extras); ((HardwareCanvas) canvas).callDrawGLFunction(functor); if (mHardwareAccelSkia != getSettings().getHardwareAccelSkiaEnabled()) { mHardwareAccelSkia = getSettings().getHardwareAccelSkiaEnabled(); @@ -4696,8 +4696,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc public InputConnection onCreateInputConnection(EditorInfo outAttrs) { if (mInputConnection == null) { mInputConnection = new WebViewInputConnection(); - mAutoCompletePopup = new AutoCompletePopup(mContext, this, - mInputConnection); + mAutoCompletePopup = new AutoCompletePopup(this, mInputConnection); } mInputConnection.setupEditorInfo(outAttrs); return mInputConnection; @@ -5548,26 +5547,26 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc } } - void setGLRectViewport() { + void setInvScreenRect() { // Use the getGlobalVisibleRect() to get the intersection among the parents // visible == false means we're clipped - send a null rect down to indicate that // we should not draw - boolean visible = mWebView.getGlobalVisibleRect(mGLRectViewport); + boolean visible = mWebView.getGlobalVisibleRect(mInvScreenRect); if (visible) { // Then need to invert the Y axis, just for GL View rootView = mWebView.getRootView(); int rootViewHeight = rootView.getHeight(); - mViewRectViewport.set(mGLRectViewport); - int savedWebViewBottom = mGLRectViewport.bottom; - mGLRectViewport.bottom = rootViewHeight - mGLRectViewport.top - getVisibleTitleHeightImpl(); - mGLRectViewport.top = rootViewHeight - savedWebViewBottom; + mScreenRect.set(mInvScreenRect); + int savedWebViewBottom = mInvScreenRect.bottom; + mInvScreenRect.bottom = rootViewHeight - mInvScreenRect.top - getVisibleTitleHeightImpl(); + mInvScreenRect.top = rootViewHeight - savedWebViewBottom; mGLViewportEmpty = false; } else { mGLViewportEmpty = true; } calcOurContentVisibleRectF(mVisibleContentRect); - nativeUpdateDrawGLFunction(mNativeClass, mGLViewportEmpty ? null : mGLRectViewport, - mGLViewportEmpty ? null : mViewRectViewport, + nativeUpdateDrawGLFunction(mNativeClass, mGLViewportEmpty ? null : mInvScreenRect, + mGLViewportEmpty ? null : mScreenRect, mVisibleContentRect, getScale()); } @@ -5583,7 +5582,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc // notify the WebKit about the new dimensions. sendViewSizeZoom(false); } - setGLRectViewport(); + setInvScreenRect(); return changed; } @@ -5754,6 +5753,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc if (mInputDispatcher.postPointerEvent(ev, getScrollX(), getScrollY() - getTitleHeight(), mZoomManager.getInvScale())) { + mInputDispatcher.dispatchUiEvents(); return true; } else { Log.w(LOGTAG, "mInputDispatcher rejected the event!"); @@ -5827,8 +5827,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc } data.mSlop = viewToContentDimension(mNavSlop); removeTouchHighlight(); - if (!mBlockWebkitViewMessages) { - mTouchHighlightRequested = SystemClock.uptimeMillis(); + if (!mBlockWebkitViewMessages && mWebViewCore != null) { mWebViewCore.sendMessageAtFrontOfQueue( EventHub.HIT_TEST, data); } @@ -6023,10 +6022,8 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc } if (deltaX * deltaX + deltaY * deltaY > mTouchSlopSquare) { mHeldMotionless = MOTIONLESS_FALSE; - nativeSetIsScrolling(true); } else { mHeldMotionless = MOTIONLESS_TRUE; - nativeSetIsScrolling(false); keepScrollBarsVisible = true; } @@ -7647,6 +7644,14 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc } return isPressingHandle; } + + @Override + public void showTapHighlight(boolean show) { + if (mShowTapHighlight != show) { + mShowTapHighlight = show; + invalidate(); + } + } } private void setHitTestTypeFromUrl(String url) { @@ -7709,16 +7714,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc if (mFocusedNode.mHasFocus && mFocusedNode.mEditable) { return false; } - long delay = SystemClock.uptimeMillis() - mTouchHighlightRequested; - if (delay < ViewConfiguration.getTapTimeout()) { - Rect r = mTouchHighlightRegion.getBounds(); - mWebView.postInvalidateDelayed(delay, r.left, r.top, r.right, r.bottom); - return false; - } - if (mInputDispatcher == null) { - return false; - } - return mInputDispatcher.shouldShowTapHighlight(); + return mShowTapHighlight; } @@ -8245,6 +8241,12 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc @Override public void run() { + if (mWebViewCore == null + || getWebView().getWindowToken() == null + || getWebView().getViewRootImpl() == null) { + // We've been detached and/or destroyed since this was posted + return; + } final ListView listView = (ListView) LayoutInflater.from(mContext) .inflate(com.android.internal.R.layout.select_dialog, null); final MyArrayListAdapter adapter = new MyArrayListAdapter(); @@ -8604,11 +8606,11 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc int color, int extra); private native void nativeDumpDisplayTree(String urlOrNull); private native boolean nativeEvaluateLayersAnimations(int nativeInstance); - private native int nativeCreateDrawGLFunction(int nativeInstance, Rect rect, - Rect viewRect, RectF visibleRect, float scale, int extras); + private native int nativeCreateDrawGLFunction(int nativeInstance, Rect invScreenRect, + Rect screenRect, RectF visibleContentRect, float scale, int extras); private native int nativeGetDrawGLFunction(int nativeInstance); - private native void nativeUpdateDrawGLFunction(int nativeInstance, Rect rect, Rect viewRect, - RectF visibleRect, float scale); + private native void nativeUpdateDrawGLFunction(int nativeInstance, Rect invScreenRect, + Rect screenRect, RectF visibleContentRect, float scale); private native String nativeGetSelection(); private native Rect nativeLayerBounds(int layer); private native void nativeSetHeightCanMeasure(boolean measure); diff --git a/core/java/android/webkit/WebViewInputDispatcher.java b/core/java/android/webkit/WebViewInputDispatcher.java index ef23a11..9541435 100644 --- a/core/java/android/webkit/WebViewInputDispatcher.java +++ b/core/java/android/webkit/WebViewInputDispatcher.java @@ -99,6 +99,8 @@ final class WebViewInputDispatcher { private boolean mPostDoNotSendTouchEventsToWebKitUntilNextGesture; private boolean mPostLongPressScheduled; private boolean mPostClickScheduled; + private boolean mPostShowTapHighlightScheduled; + private boolean mPostHideTapHighlightScheduled; private int mPostLastWebKitXOffset; private int mPostLastWebKitYOffset; private float mPostLastWebKitScale; @@ -133,6 +135,7 @@ final class WebViewInputDispatcher { private static final int LONG_PRESS_TIMEOUT = ViewConfiguration.getLongPressTimeout() + TAP_TIMEOUT; private static final int DOUBLE_TAP_TIMEOUT = ViewConfiguration.getDoubleTapTimeout(); + private static final int PRESSED_STATE_DURATION = ViewConfiguration.getPressedStateDuration(); /** * Event type: Indicates a touch event type. @@ -310,6 +313,7 @@ final class WebViewInputDispatcher { } unscheduleLongPressLocked(); unscheduleClickLocked(); + hideTapCandidateLocked(); return false; } @@ -349,12 +353,6 @@ final class WebViewInputDispatcher { } } - public boolean shouldShowTapHighlight() { - synchronized (mLock) { - return mPostLongPressScheduled || mPostClickScheduled; - } - } - private void postLongPress() { synchronized (mLock) { if (!mPostLongPressScheduled) { @@ -385,6 +383,64 @@ final class WebViewInputDispatcher { } } + private void hideTapCandidateLocked() { + unscheduleHideTapHighlightLocked(); + unscheduleShowTapHighlightLocked(); + mUiCallbacks.showTapHighlight(false); + } + + private void showTapCandidateLocked() { + unscheduleHideTapHighlightLocked(); + unscheduleShowTapHighlightLocked(); + mUiCallbacks.showTapHighlight(true); + scheduleHideTapHighlightLocked(); + } + + private void scheduleShowTapHighlightLocked() { + unscheduleShowTapHighlightLocked(); + mPostShowTapHighlightScheduled = true; + mUiHandler.sendEmptyMessageDelayed(UiHandler.MSG_SHOW_TAP_HIGHLIGHT, + TAP_TIMEOUT); + } + + private void unscheduleShowTapHighlightLocked() { + if (mPostShowTapHighlightScheduled) { + mPostShowTapHighlightScheduled = false; + mUiHandler.removeMessages(UiHandler.MSG_SHOW_TAP_HIGHLIGHT); + } + } + + private void scheduleHideTapHighlightLocked() { + unscheduleHideTapHighlightLocked(); + mPostHideTapHighlightScheduled = true; + mUiHandler.sendEmptyMessageDelayed(UiHandler.MSG_HIDE_TAP_HIGHLIGHT, + PRESSED_STATE_DURATION); + } + + private void unscheduleHideTapHighlightLocked() { + if (mPostHideTapHighlightScheduled) { + mPostHideTapHighlightScheduled = false; + mUiHandler.removeMessages(UiHandler.MSG_HIDE_TAP_HIGHLIGHT); + } + } + + private void postShowTapHighlight(boolean show) { + synchronized (mLock) { + if (show) { + if (!mPostShowTapHighlightScheduled) { + return; + } + mPostShowTapHighlightScheduled = false; + } else { + if (!mPostHideTapHighlightScheduled) { + return; + } + mPostHideTapHighlightScheduled = false; + } + mUiCallbacks.showTapHighlight(show); + } + } + private void scheduleClickLocked() { unscheduleClickLocked(); mPostClickScheduled = true; @@ -404,6 +460,7 @@ final class WebViewInputDispatcher { return; } mPostClickScheduled = false; + showTapCandidateLocked(); MotionEvent event = mPostTouchStream.getLastEvent(); if (event == null || event.getAction() != MotionEvent.ACTION_UP) { @@ -442,6 +499,7 @@ final class WebViewInputDispatcher { private void enqueueDoubleTapLocked(MotionEvent event) { unscheduleClickLocked(); + hideTapCandidateLocked(); MotionEvent eventToEnqueue = MotionEvent.obtainNoHistory(event); DispatchEvent d = obtainDispatchEventLocked(eventToEnqueue, EVENT_TYPE_DOUBLE_TAP, 0, mPostLastWebKitXOffset, mPostLastWebKitYOffset, mPostLastWebKitScale); @@ -458,6 +516,7 @@ final class WebViewInputDispatcher { if ((deltaX * deltaX + deltaY * deltaY) > mTouchSlopSquared) { unscheduleLongPressLocked(); mIsTapCandidate = false; + hideTapCandidateLocked(); } } @@ -474,14 +533,17 @@ final class WebViewInputDispatcher { || event.getPointerCount() > 1) { unscheduleLongPressLocked(); unscheduleClickLocked(); + hideTapCandidateLocked(); mIsDoubleTapCandidate = false; mIsTapCandidate = false; + hideTapCandidateLocked(); } else if (action == MotionEvent.ACTION_DOWN) { checkForDoubleTapOnDownLocked(event); scheduleLongPressLocked(); mIsTapCandidate = true; mInitialDownX = event.getX(); mInitialDownY = event.getY(); + scheduleShowTapHighlightLocked(); } else if (action == MotionEvent.ACTION_UP) { unscheduleLongPressLocked(); if (isClickCandidateLocked(event)) { @@ -490,6 +552,8 @@ final class WebViewInputDispatcher { } else { scheduleClickLocked(); } + } else { + hideTapCandidateLocked(); } } else if (action == MotionEvent.ACTION_MOVE) { checkForSlopLocked(event); @@ -959,6 +1023,12 @@ final class WebViewInputDispatcher { * through webkit or false otherwise. */ public boolean shouldInterceptTouchEvent(MotionEvent event); + + /** + * Inform's the UI that it should show the tap highlight + * @param show True if it should show the highlight, false if it should hide it + */ + public void showTapHighlight(boolean show); } /* Implemented by {@link WebViewCore} to perform operations on the web kit thread. */ @@ -985,6 +1055,8 @@ final class WebViewInputDispatcher { public static final int MSG_WEBKIT_TIMEOUT = 2; public static final int MSG_LONG_PRESS = 3; public static final int MSG_CLICK = 4; + public static final int MSG_SHOW_TAP_HIGHLIGHT = 5; + public static final int MSG_HIDE_TAP_HIGHLIGHT = 6; public UiHandler(Looper looper) { super(looper); @@ -1005,6 +1077,12 @@ final class WebViewInputDispatcher { case MSG_CLICK: postClick(); break; + case MSG_SHOW_TAP_HIGHLIGHT: + postShowTapHighlight(true); + break; + case MSG_HIDE_TAP_HIGHLIGHT: + postShowTapHighlight(false); + break; default: throw new IllegalStateException("Unknown message type: " + msg.what); } diff --git a/core/java/android/widget/ActivityChooserModel.java b/core/java/android/widget/ActivityChooserModel.java index c53b5f6..fba8d3a 100644 --- a/core/java/android/widget/ActivityChooserModel.java +++ b/core/java/android/widget/ActivityChooserModel.java @@ -761,6 +761,16 @@ public class ActivityChooserModel extends DataSetObservable { } /** + * Gets whether the given observer is already registered. + * + * @param observer The observer. + * @return True if already registered. + */ + public boolean isRegisteredObserver(DataSetObserver observer) { + return mObservers.contains(observer); + } + + /** * Represents a record in the history. */ public final static class HistoricalRecord { diff --git a/core/java/android/widget/ActivityChooserView.java b/core/java/android/widget/ActivityChooserView.java index be6b4e2..0c0bb1e 100644 --- a/core/java/android/widget/ActivityChooserView.java +++ b/core/java/android/widget/ActivityChooserView.java @@ -20,8 +20,10 @@ import com.android.internal.R; import android.content.Context; import android.content.Intent; +import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; +import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.TypedArray; import android.database.DataSetObserver; @@ -174,6 +176,11 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod private int mDefaultActionButtonContentDescription; /** + * Whether this view has a default activity affordance. + */ + private boolean mHasDefaultActivity; + + /** * Create a new instance. * * @param context The application environment. @@ -245,6 +252,8 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod Resources resources = context.getResources(); mListPopupMaxWidth = Math.max(resources.getDisplayMetrics().widthPixels / 2, resources.getDimensionPixelSize(com.android.internal.R.dimen.config_prefDialogWidth)); + + updateHasDefaultActivity(); } /** @@ -258,6 +267,21 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod } } + @Override + protected void onConfigurationChanged(Configuration newConfig) { + Configuration oldConfig = mContext.getResources().getConfiguration(); + final int changed = oldConfig.diff(newConfig); + if ((changed & ActivityInfo.CONFIG_SCREEN_SIZE) != 0 + || (changed & ActivityInfo.CONFIG_ORIENTATION) != 0) { + updateHasDefaultActivity(); + } + } + + private void updateHasDefaultActivity() { + mHasDefaultActivity = mContext.getResources().getBoolean( + R.bool.activity_chooser_view_has_default_activity); + } + /** * Sets the background for the button that expands the activity * overflow list. @@ -383,7 +407,8 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod protected void onAttachedToWindow() { super.onAttachedToWindow(); ActivityChooserModel dataModel = mAdapter.getDataModel(); - if (dataModel != null) { + if (dataModel != null + && !dataModel.isRegisteredObserver(mModelDataSetOberver)) { dataModel.registerObserver(mModelDataSetOberver); } mIsAttachedToWindow = true; @@ -393,7 +418,8 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod protected void onDetachedFromWindow() { super.onDetachedFromWindow(); ActivityChooserModel dataModel = mAdapter.getDataModel(); - if (dataModel != null) { + if (dataModel != null + && dataModel.isRegisteredObserver(mModelDataSetOberver)) { dataModel.unregisterObserver(mModelDataSetOberver); } ViewTreeObserver viewTreeObserver = getViewTreeObserver(); @@ -496,7 +522,7 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod // Default activity button. final int activityCount = mAdapter.getActivityCount(); final int historySize = mAdapter.getHistorySize(); - if (activityCount > 0 && historySize > 0) { + if (mHasDefaultActivity && activityCount > 0 && historySize > 0) { mDefaultActivityButton.setVisibility(VISIBLE); ResolveInfo activity = mAdapter.getDefaultActivity(); PackageManager packageManager = mContext.getPackageManager(); @@ -512,9 +538,9 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod } // Activity chooser content. if (mDefaultActivityButton.getVisibility() == VISIBLE) { - mActivityChooserContent.setBackgroundDrawable(mActivityChooserContentBackground); + mActivityChooserContent.setBackground(mActivityChooserContentBackground); } else { - mActivityChooserContent.setBackgroundDrawable(null); + mActivityChooserContent.setBackground(null); } } @@ -577,7 +603,7 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod // OnLongClickListener#onLongClick @Override public boolean onLongClick(View view) { - if (view == mDefaultActivityButton) { + if (mHasDefaultActivity && view == mDefaultActivityButton) { if (mAdapter.getCount() > 0) { mIsSelectingDefaultActivity = true; showPopupUnchecked(mInitialActivityCount); @@ -630,14 +656,16 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod public void setDataModel(ActivityChooserModel dataModel) { ActivityChooserModel oldDataModel = mAdapter.getDataModel(); - if (oldDataModel != null && isShown()) { + if (oldDataModel != null) { oldDataModel.unregisterObserver(mModelDataSetOberver); } mDataModel = dataModel; - if (dataModel != null && isShown()) { + if (dataModel != null) { dataModel.registerObserver(mModelDataSetOberver); + notifyDataSetChanged(); + } else { + notifyDataSetInvalidated(); } - notifyDataSetChanged(); } @Override diff --git a/core/java/android/widget/AppSecurityPermissions.java b/core/java/android/widget/AppSecurityPermissions.java index b409e26..64f6c07 100755 --- a/core/java/android/widget/AppSecurityPermissions.java +++ b/core/java/android/widget/AppSecurityPermissions.java @@ -18,7 +18,9 @@ package android.widget; import com.android.internal.R; +import android.app.AlertDialog; import android.content.Context; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; @@ -27,14 +29,13 @@ import android.content.pm.PermissionGroupInfo; import android.content.pm.PermissionInfo; import android.graphics.drawable.Drawable; import android.os.Parcel; -import android.text.Spannable; -import android.text.SpannableString; import android.text.SpannableStringBuilder; import android.text.TextUtils; -import android.text.style.ForegroundColorSpan; +import android.util.AttributeSet; import android.util.Log; import android.view.LayoutInflater; import android.view.View; +import android.view.ViewGroup; import java.text.Collator; import java.util.ArrayList; @@ -42,7 +43,6 @@ import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; -import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; @@ -58,16 +58,65 @@ import java.util.Set; * * {@hide} */ -public class AppSecurityPermissions implements View.OnClickListener { +public class AppSecurityPermissions { - private enum State { - NO_PERMS, - DANGEROUS_ONLY, - NORMAL_ONLY, - BOTH + public static final int WHICH_PERSONAL = 1<<0; + public static final int WHICH_DEVICE = 1<<1; + public static final int WHICH_NEW = 1<<2; + public static final int WHICH_ALL = 0xffff; + + private final static String TAG = "AppSecurityPermissions"; + private boolean localLOGV = false; + private Context mContext; + private LayoutInflater mInflater; + private PackageManager mPm; + private PackageInfo mInstalledPackageInfo; + private final Map<String, MyPermissionGroupInfo> mPermGroups + = new HashMap<String, MyPermissionGroupInfo>(); + private final List<MyPermissionGroupInfo> mPermGroupsList + = new ArrayList<MyPermissionGroupInfo>(); + private final PermissionGroupInfoComparator mPermGroupComparator; + private final PermissionInfoComparator mPermComparator; + private List<MyPermissionInfo> mPermsList; + private CharSequence mNewPermPrefix; + private Drawable mNormalIcon; + private Drawable mDangerousIcon; + + static class MyPermissionGroupInfo extends PermissionGroupInfo { + CharSequence mLabel; + + final ArrayList<MyPermissionInfo> mNewPermissions = new ArrayList<MyPermissionInfo>(); + final ArrayList<MyPermissionInfo> mPersonalPermissions = new ArrayList<MyPermissionInfo>(); + final ArrayList<MyPermissionInfo> mDevicePermissions = new ArrayList<MyPermissionInfo>(); + final ArrayList<MyPermissionInfo> mAllPermissions = new ArrayList<MyPermissionInfo>(); + + MyPermissionGroupInfo(PermissionInfo perm) { + name = perm.packageName; + packageName = perm.packageName; + } + + MyPermissionGroupInfo(PermissionGroupInfo info) { + super(info); + } + + public Drawable loadGroupIcon(PackageManager pm) { + if (icon != 0) { + return loadIcon(pm); + } else { + ApplicationInfo appInfo; + try { + appInfo = pm.getApplicationInfo(packageName, 0); + return appInfo.loadIcon(pm); + } catch (NameNotFoundException e) { + } + } + return null; + } } static class MyPermissionInfo extends PermissionInfo { + CharSequence mLabel; + /** * PackageInfo.requestedPermissionsFlags for the new package being installed. */ @@ -99,46 +148,108 @@ public class AppSecurityPermissions implements View.OnClickListener { } } - private final static String TAG = "AppSecurityPermissions"; - private boolean localLOGV = false; - private Context mContext; - private LayoutInflater mInflater; - private PackageManager mPm; - private LinearLayout mPermsView; - private Map<String, CharSequence> mNewMap; - private Map<String, CharSequence> mDangerousMap; - private Map<String, CharSequence> mNormalMap; - private List<MyPermissionInfo> mPermsList; - private String mDefaultGrpLabel; - private String mDefaultGrpName="DefaultGrp"; - private String mPermFormat; - private CharSequence mNewPermPrefix; - private Drawable mNormalIcon; - private Drawable mDangerousIcon; - private boolean mExpanded; - private Drawable mShowMaxIcon; - private Drawable mShowMinIcon; - private View mShowMore; - private TextView mShowMoreText; - private ImageView mShowMoreIcon; - private State mCurrentState; - private LinearLayout mNonDangerousList; - private LinearLayout mDangerousList; - private LinearLayout mNewList; - private HashMap<String, CharSequence> mGroupLabelCache; - private View mNoPermsView; + public static class PermissionItemView extends LinearLayout implements View.OnClickListener { + MyPermissionGroupInfo mGroup; + MyPermissionInfo mPerm; + AlertDialog mDialog; + + public PermissionItemView(Context context, AttributeSet attrs) { + super(context, attrs); + setClickable(true); + } + + public void setPermission(MyPermissionGroupInfo grp, MyPermissionInfo perm, + boolean first, CharSequence newPermPrefix) { + mGroup = grp; + mPerm = perm; + + ImageView permGrpIcon = (ImageView) findViewById(R.id.perm_icon); + TextView permNameView = (TextView) findViewById(R.id.perm_name); + + PackageManager pm = getContext().getPackageManager(); + Drawable icon = null; + if (first) { + icon = grp.loadGroupIcon(pm); + } + CharSequence label = perm.mLabel; + if (perm.mNew && newPermPrefix != null) { + // If this is a new permission, format it appropriately. + SpannableStringBuilder builder = new SpannableStringBuilder(); + Parcel parcel = Parcel.obtain(); + TextUtils.writeToParcel(newPermPrefix, parcel, 0); + parcel.setDataPosition(0); + CharSequence newStr = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel); + parcel.recycle(); + builder.append(newStr); + builder.append(label); + label = builder; + } + + permGrpIcon.setImageDrawable(icon); + permNameView.setText(label); + setOnClickListener(this); + } + + @Override + public void onClick(View v) { + if (mGroup != null && mPerm != null) { + if (mDialog != null) { + mDialog.dismiss(); + } + PackageManager pm = getContext().getPackageManager(); + AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); + builder.setTitle(mGroup.mLabel); + if (mPerm.descriptionRes != 0) { + builder.setMessage(mPerm.loadDescription(pm)); + } else { + CharSequence appName; + try { + ApplicationInfo app = pm.getApplicationInfo(mPerm.packageName, 0); + appName = app.loadLabel(pm); + } catch (NameNotFoundException e) { + appName = mPerm.packageName; + } + StringBuilder sbuilder = new StringBuilder(128); + sbuilder.append(getContext().getString( + R.string.perms_description_app, appName)); + sbuilder.append("\n\n"); + sbuilder.append(mPerm.name); + builder.setMessage(sbuilder.toString()); + } + builder.setCancelable(true); + builder.setIcon(mGroup.loadGroupIcon(pm)); + mDialog = builder.show(); + mDialog.setCanceledOnTouchOutside(true); + } + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + if (mDialog != null) { + mDialog.dismiss(); + } + } + } public AppSecurityPermissions(Context context, List<PermissionInfo> permList) { mContext = context; mPm = mContext.getPackageManager(); + loadResources(); + mPermComparator = new PermissionInfoComparator(); + mPermGroupComparator = new PermissionGroupInfoComparator(); for (PermissionInfo pi : permList) { mPermsList.add(new MyPermissionInfo(pi)); } + setPermissions(mPermsList); } public AppSecurityPermissions(Context context, String packageName) { mContext = context; mPm = mContext.getPackageManager(); + loadResources(); + mPermComparator = new PermissionInfoComparator(); + mPermGroupComparator = new PermissionGroupInfoComparator(); mPermsList = new ArrayList<MyPermissionInfo>(); Set<MyPermissionInfo> permSet = new HashSet<MyPermissionInfo>(); PackageInfo pkgInfo; @@ -155,11 +266,15 @@ public class AppSecurityPermissions implements View.OnClickListener { for(MyPermissionInfo tmpInfo : permSet) { mPermsList.add(tmpInfo); } + setPermissions(mPermsList); } - + public AppSecurityPermissions(Context context, PackageParser.Package pkg) { mContext = context; mPm = mContext.getPackageManager(); + loadResources(); + mPermComparator = new PermissionInfoComparator(); + mPermGroupComparator = new PermissionGroupInfoComparator(); mPermsList = new ArrayList<MyPermissionInfo>(); Set<MyPermissionInfo> permSet = new HashSet<MyPermissionInfo>(); if(pkg == null) { @@ -193,10 +308,20 @@ public class AppSecurityPermissions implements View.OnClickListener { for (MyPermissionInfo tmpInfo : permSet) { mPermsList.add(tmpInfo); } + setPermissions(mPermsList); } - + + private void loadResources() { + // Pick up from framework resources instead. + mNewPermPrefix = mContext.getText(R.string.perms_new_perm_prefix); + mNormalIcon = mContext.getResources().getDrawable(R.drawable.ic_text_dot); + mDangerousIcon = mContext.getResources().getDrawable(R.drawable.ic_bullet_key_permission); + } + /** - * Utility to retrieve a view displaying a single permission. + * Utility to retrieve a view displaying a single permission. This provides + * the old UI layout for permissions; it is only here for the device admin + * settings to continue to use. */ public static View getPermissionItemView(Context context, CharSequence grpName, CharSequence description, boolean dangerous) { @@ -204,10 +329,14 @@ public class AppSecurityPermissions implements View.OnClickListener { Context.LAYOUT_INFLATER_SERVICE); Drawable icon = context.getResources().getDrawable(dangerous ? R.drawable.ic_bullet_key_permission : R.drawable.ic_text_dot); - return getPermissionItemView(context, inflater, grpName, + return getPermissionItemViewOld(context, inflater, grpName, description, dangerous, icon); } + public PackageInfo getInstalledPackageInfo() { + return mInstalledPackageInfo; + } + private void getAllUsedPermissions(int sharedUid, Set<MyPermissionInfo> permSet) { String sharedPkgList[] = mPm.getPackagesForUid(sharedUid); if(sharedPkgList == null || (sharedPkgList.length == 0)) { @@ -239,6 +368,7 @@ public class AppSecurityPermissions implements View.OnClickListener { if ((strList == null) || (strList.length == 0)) { return; } + mInstalledPackageInfo = installedPkgInfo; for (int i=0; i<strList.length; i++) { String permName = strList[i]; // If we are only looking at an existing app, then we only @@ -270,13 +400,42 @@ public class AppSecurityPermissions implements View.OnClickListener { // to see, so skip it. continue; } + final String origGroupName = tmpPermInfo.group; + String groupName = origGroupName; + if (groupName == null) { + groupName = tmpPermInfo.packageName; + tmpPermInfo.group = groupName; + } + MyPermissionGroupInfo group = mPermGroups.get(groupName); + if (group == null) { + PermissionGroupInfo grp = null; + if (origGroupName != null) { + grp = mPm.getPermissionGroupInfo(origGroupName, 0); + } + if (grp != null) { + group = new MyPermissionGroupInfo(grp); + } else { + // We could be here either because the permission + // didn't originally specify a group or the group it + // gave couldn't be found. In either case, we consider + // its group to be the permission's package name. + tmpPermInfo.group = tmpPermInfo.packageName; + group = mPermGroups.get(tmpPermInfo.group); + if (group == null) { + group = new MyPermissionGroupInfo(tmpPermInfo); + } + group = new MyPermissionGroupInfo(tmpPermInfo); + } + mPermGroups.put(tmpPermInfo.group, group); + } + final boolean newPerm = installedPkgInfo != null + && (existingFlags&PackageInfo.REQUESTED_PERMISSION_GRANTED) == 0; MyPermissionInfo myPerm = new MyPermissionInfo(tmpPermInfo); myPerm.mNewReqFlags = flagsList[i]; myPerm.mExistingReqFlags = existingFlags; // This is a new permission if the app is already installed and // doesn't currently hold this permission. - myPerm.mNew = installedPkgInfo != null - && (existingFlags&PackageInfo.REQUESTED_PERMISSION_GRANTED) == 0; + myPerm.mNew = newPerm; permSet.add(myPerm); } catch (NameNotFoundException e) { Log.i(TAG, "Ignoring unknown permission:"+permName); @@ -285,149 +444,99 @@ public class AppSecurityPermissions implements View.OnClickListener { } public int getPermissionCount() { - return mPermsList.size(); + return getPermissionCount(WHICH_ALL); } - public View getPermissionsView() { - - mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - mPermsView = (LinearLayout) mInflater.inflate(R.layout.app_perms_summary, null); - mShowMore = mPermsView.findViewById(R.id.show_more); - mShowMoreIcon = (ImageView) mShowMore.findViewById(R.id.show_more_icon); - mShowMoreText = (TextView) mShowMore.findViewById(R.id.show_more_text); - mNewList = (LinearLayout) mPermsView.findViewById(R.id.new_perms_list); - mDangerousList = (LinearLayout) mPermsView.findViewById(R.id.dangerous_perms_list); - mNonDangerousList = (LinearLayout) mPermsView.findViewById(R.id.non_dangerous_perms_list); - mNoPermsView = mPermsView.findViewById(R.id.no_permissions); - - // Set up the LinearLayout that acts like a list item. - mShowMore.setClickable(true); - mShowMore.setOnClickListener(this); - mShowMore.setFocusable(true); - - // Pick up from framework resources instead. - mDefaultGrpLabel = mContext.getString(R.string.default_permission_group); - mPermFormat = mContext.getString(R.string.permissions_format); - mNewPermPrefix = mContext.getText(R.string.perms_new_perm_prefix); - mNormalIcon = mContext.getResources().getDrawable(R.drawable.ic_text_dot); - mDangerousIcon = mContext.getResources().getDrawable(R.drawable.ic_bullet_key_permission); - mShowMaxIcon = mContext.getResources().getDrawable(R.drawable.expander_close_holo_dark); - mShowMinIcon = mContext.getResources().getDrawable(R.drawable.expander_open_holo_dark); - - // Set permissions view - setPermissions(mPermsList); - return mPermsView; + private List<MyPermissionInfo> getPermissionList(MyPermissionGroupInfo grp, int which) { + if (which == WHICH_NEW) { + return grp.mNewPermissions; + } else if (which == WHICH_PERSONAL) { + return grp.mPersonalPermissions; + } else if (which == WHICH_DEVICE) { + return grp.mDevicePermissions; + } else { + return grp.mAllPermissions; + } } - /** - * Utility method that concatenates two strings defined by mPermFormat. - * a null value is returned if both str1 and str2 are null, if one of the strings - * is null the other non null value is returned without formatting - * this is to placate initial error checks - */ - private CharSequence formatPermissions(CharSequence groupDesc, CharSequence permDesc, - boolean newPerms) { - if (permDesc == null) { - return groupDesc; - } - // Sometimes people write permission names with a trailing period; - // strip that if it appears. - int len = permDesc.length(); - if (len > 0 && permDesc.charAt(len-1) == '.') { - permDesc = (permDesc.toString()).substring(0, len-1); - } - if (newPerms) { - if (true) { - // If this is a new permission, format it appropriately. - SpannableStringBuilder builder = new SpannableStringBuilder(); - if (groupDesc != null) { - // The previous permissions go in front, with a newline - // separating them. - builder.append(groupDesc); - builder.append("\n"); - } - Parcel parcel = Parcel.obtain(); - TextUtils.writeToParcel(mNewPermPrefix, parcel, 0); - parcel.setDataPosition(0); - CharSequence newStr = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel); - parcel.recycle(); - builder.append(newStr); - builder.append(permDesc); - return builder; - } else { - // If this is a new permission, format it appropriately. - SpannableStringBuilder builder = new SpannableStringBuilder(permDesc); - builder.insert(0, mNewPermPrefix); - if (groupDesc != null) { - // The previous permissions go in front, with a newline - // separating them. - builder.insert(0, "\n"); - builder.insert(0, groupDesc); - } - return builder; - } - } - if (groupDesc == null) { - return permDesc; + public int getPermissionCount(int which) { + int N = 0; + for (int i=0; i<mPermGroupsList.size(); i++) { + N += getPermissionList(mPermGroupsList.get(i), which).size(); } - // groupDesc and permDesc are non null - return String.format(mPermFormat, groupDesc, permDesc.toString()); + return N; } - private CharSequence getGroupLabel(String grpName) { - if (grpName == null) { - //return default label - return mDefaultGrpLabel; - } - CharSequence cachedLabel = mGroupLabelCache.get(grpName); - if (cachedLabel != null) { - return cachedLabel; - } - PermissionGroupInfo pgi; - try { - pgi = mPm.getPermissionGroupInfo(grpName, 0); - } catch (NameNotFoundException e) { - Log.i(TAG, "Invalid group name:" + grpName); - return null; + public View getPermissionsView() { + return getPermissionsView(WHICH_ALL); + } + + public View getPermissionsView(int which) { + mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + + LinearLayout permsView = (LinearLayout) mInflater.inflate(R.layout.app_perms_summary, null); + LinearLayout displayList = (LinearLayout) permsView.findViewById(R.id.perms_list); + View noPermsView = permsView.findViewById(R.id.no_permissions); + + displayPermissions(mPermGroupsList, displayList, which); + if (displayList.getChildCount() <= 0) { + noPermsView.setVisibility(View.VISIBLE); } - CharSequence label = pgi.loadLabel(mPm).toString(); - mGroupLabelCache.put(grpName, label); - return label; + + return permsView; } /** * Utility method that displays permissions from a map containing group name and * list of permission descriptions. */ - private void displayPermissions(Map<String, CharSequence> permInfoMap, - LinearLayout permListView, boolean dangerous) { + private void displayPermissions(List<MyPermissionGroupInfo> groups, + LinearLayout permListView, int which) { permListView.removeAllViews(); - Set<String> permInfoStrSet = permInfoMap.keySet(); - for (String loopPermGrpInfoStr : permInfoStrSet) { - CharSequence grpLabel = getGroupLabel(loopPermGrpInfoStr); - //guaranteed that grpLabel wont be null since permissions without groups - //will belong to the default group - if(localLOGV) Log.i(TAG, "Adding view group:" + grpLabel + ", desc:" - + permInfoMap.get(loopPermGrpInfoStr)); - permListView.addView(getPermissionItemView(grpLabel, - permInfoMap.get(loopPermGrpInfoStr), dangerous)); + int spacing = (int)(8*mContext.getResources().getDisplayMetrics().density); + + for (int i=0; i<groups.size(); i++) { + MyPermissionGroupInfo grp = groups.get(i); + final List<MyPermissionInfo> perms = getPermissionList(grp, which); + for (int j=0; j<perms.size(); j++) { + MyPermissionInfo perm = perms.get(j); + View view = getPermissionItemView(grp, perm, j == 0, + which != WHICH_NEW ? mNewPermPrefix : null); + LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.WRAP_CONTENT); + if (j == 0) { + lp.topMargin = spacing; + } + if (j == grp.mAllPermissions.size()-1) { + lp.bottomMargin = spacing; + } + if (permListView.getChildCount() == 0) { + lp.topMargin *= 2; + } + permListView.addView(view, lp); + } } } - private void displayNoPermissions() { - mNoPermsView.setVisibility(View.VISIBLE); + private PermissionItemView getPermissionItemView(MyPermissionGroupInfo grp, + MyPermissionInfo perm, boolean first, CharSequence newPermPrefix) { + return getPermissionItemView(mContext, mInflater, grp, perm, first, newPermPrefix); } - private View getPermissionItemView(CharSequence grpName, CharSequence permList, - boolean dangerous) { - return getPermissionItemView(mContext, mInflater, grpName, permList, - dangerous, dangerous ? mDangerousIcon : mNormalIcon); + private static PermissionItemView getPermissionItemView(Context context, LayoutInflater inflater, + MyPermissionGroupInfo grp, MyPermissionInfo perm, boolean first, + CharSequence newPermPrefix) { + PermissionItemView permView = (PermissionItemView)inflater.inflate( + R.layout.app_permission_item, null); + permView.setPermission(grp, perm, first, newPermPrefix); + return permView; } - private static View getPermissionItemView(Context context, LayoutInflater inflater, + private static View getPermissionItemViewOld(Context context, LayoutInflater inflater, CharSequence grpName, CharSequence permList, boolean dangerous, Drawable icon) { - View permView = inflater.inflate(R.layout.app_permission_item, null); + View permView = inflater.inflate(R.layout.app_permission_item_old, null); TextView permGrpView = (TextView) permView.findViewById(R.id.permission_group); TextView permDescView = (TextView) permView.findViewById(R.id.permission_list); @@ -444,41 +553,6 @@ public class AppSecurityPermissions implements View.OnClickListener { return permView; } - private void showPermissions() { - - switch(mCurrentState) { - case NO_PERMS: - displayNoPermissions(); - break; - - case DANGEROUS_ONLY: - displayPermissions(mNewMap, mNewList, true); - displayPermissions(mDangerousMap, mDangerousList, true); - break; - - case NORMAL_ONLY: - displayPermissions(mNewMap, mNewList, true); - displayPermissions(mNormalMap, mNonDangerousList, false); - break; - - case BOTH: - displayPermissions(mNewMap, mNewList, true); - displayPermissions(mDangerousMap, mDangerousList, true); - if (mExpanded) { - displayPermissions(mNormalMap, mNonDangerousList, false); - mShowMoreIcon.setImageDrawable(mShowMaxIcon); - mShowMoreText.setText(R.string.perms_hide); - mNonDangerousList.setVisibility(View.VISIBLE); - } else { - mShowMoreIcon.setImageDrawable(mShowMinIcon); - mShowMoreText.setText(R.string.perms_show_all); - mNonDangerousList.setVisibility(View.GONE); - } - mShowMore.setVisibility(View.VISIBLE); - break; - } - } - private boolean isDisplayablePermission(PermissionInfo pInfo, int newReqFlags, int existingReqFlags) { final int base = pInfo.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE; @@ -496,79 +570,45 @@ public class AppSecurityPermissions implements View.OnClickListener { } return false; } - - /* - * Utility method that aggregates all permission descriptions categorized by group - * Say group1 has perm11, perm12, perm13, the group description will be - * perm11_Desc, perm12_Desc, perm13_Desc - */ - private void aggregateGroupDescs(Map<String, List<MyPermissionInfo> > map, - Map<String, CharSequence> retMap, boolean newPerms) { - if(map == null) { - return; + + private static class PermissionGroupInfoComparator implements Comparator<MyPermissionGroupInfo> { + private final Collator sCollator = Collator.getInstance(); + PermissionGroupInfoComparator() { } - if(retMap == null) { - return; - } - Set<String> grpNames = map.keySet(); - Iterator<String> grpNamesIter = grpNames.iterator(); - while(grpNamesIter.hasNext()) { - CharSequence grpDesc = null; - String grpNameKey = grpNamesIter.next(); - List<MyPermissionInfo> grpPermsList = map.get(grpNameKey); - if(grpPermsList == null) { - continue; - } - for(PermissionInfo permInfo: grpPermsList) { - CharSequence permDesc = permInfo.loadLabel(mPm); - grpDesc = formatPermissions(grpDesc, permDesc, newPerms); + public final int compare(MyPermissionGroupInfo a, MyPermissionGroupInfo b) { + if (((a.flags^b.flags)&PermissionGroupInfo.FLAG_PERSONAL_INFO) != 0) { + return ((a.flags&PermissionGroupInfo.FLAG_PERSONAL_INFO) != 0) ? -1 : 1; } - // Insert grpDesc into map - if(grpDesc != null) { - if(localLOGV) Log.i(TAG, "Group:"+grpNameKey+" description:"+grpDesc.toString()); - retMap.put(grpNameKey, grpDesc); + if (a.priority != b.priority) { + return a.priority > b.priority ? -1 : 1; } + return sCollator.compare(a.mLabel, b.mLabel); } } - private static class PermissionInfoComparator implements Comparator<PermissionInfo> { - private PackageManager mPm; + private static class PermissionInfoComparator implements Comparator<MyPermissionInfo> { private final Collator sCollator = Collator.getInstance(); - PermissionInfoComparator(PackageManager pm) { - mPm = pm; + PermissionInfoComparator() { } - public final int compare(PermissionInfo a, PermissionInfo b) { - CharSequence sa = a.loadLabel(mPm); - CharSequence sb = b.loadLabel(mPm); - return sCollator.compare(sa, sb); + public final int compare(MyPermissionInfo a, MyPermissionInfo b) { + return sCollator.compare(a.mLabel, b.mLabel); } } - + + private void addPermToList(List<MyPermissionInfo> permList, + MyPermissionInfo pInfo) { + if (pInfo.mLabel == null) { + pInfo.mLabel = pInfo.loadLabel(mPm); + } + int idx = Collections.binarySearch(permList, pInfo, mPermComparator); + if(localLOGV) Log.i(TAG, "idx="+idx+", list.size="+permList.size()); + if (idx < 0) { + idx = -idx-1; + permList.add(idx, pInfo); + } + } + private void setPermissions(List<MyPermissionInfo> permList) { - mGroupLabelCache = new HashMap<String, CharSequence>(); - //add the default label so that uncategorized permissions can go here - mGroupLabelCache.put(mDefaultGrpName, mDefaultGrpLabel); - - // Map containing group names and a list of permissions under that group - // that are new from the current install - mNewMap = new HashMap<String, CharSequence>(); - // Map containing group names and a list of permissions under that group - // categorized as dangerous - mDangerousMap = new HashMap<String, CharSequence>(); - // Map containing group names and a list of permissions under that group - // categorized as normal - mNormalMap = new HashMap<String, CharSequence>(); - - // Additional structures needed to ensure that permissions are unique under - // each group - Map<String, List<MyPermissionInfo>> newMap = - new HashMap<String, List<MyPermissionInfo>>(); - Map<String, List<MyPermissionInfo>> dangerousMap = - new HashMap<String, List<MyPermissionInfo>>(); - Map<String, List<MyPermissionInfo> > normalMap = - new HashMap<String, List<MyPermissionInfo>>(); - PermissionInfoComparator permComparator = new PermissionInfoComparator(mPm); - if (permList != null) { // First pass to group permissions for (MyPermissionInfo pInfo : permList) { @@ -577,51 +617,43 @@ public class AppSecurityPermissions implements View.OnClickListener { if(localLOGV) Log.i(TAG, "Permission:"+pInfo.name+" is not displayable"); continue; } - Map<String, List<MyPermissionInfo> > permInfoMap; - if (pInfo.mNew) { - permInfoMap = newMap; - } else if ((pInfo.protectionLevel&PermissionInfo.PROTECTION_MASK_BASE) - == PermissionInfo.PROTECTION_DANGEROUS) { - permInfoMap = dangerousMap; - } else { - permInfoMap = normalMap; - } - String grpName = (pInfo.group == null) ? mDefaultGrpName : pInfo.group; - if(localLOGV) Log.i(TAG, "Permission:"+pInfo.name+" belongs to group:"+grpName); - List<MyPermissionInfo> grpPermsList = permInfoMap.get(grpName); - if(grpPermsList == null) { - grpPermsList = new ArrayList<MyPermissionInfo>(); - permInfoMap.put(grpName, grpPermsList); - grpPermsList.add(pInfo); - } else { - int idx = Collections.binarySearch(grpPermsList, pInfo, permComparator); - if(localLOGV) Log.i(TAG, "idx="+idx+", list.size="+grpPermsList.size()); - if (idx < 0) { - idx = -idx-1; - grpPermsList.add(idx, pInfo); + MyPermissionGroupInfo group = mPermGroups.get(pInfo.group); + if (group != null) { + pInfo.mLabel = pInfo.loadLabel(mPm); + addPermToList(group.mAllPermissions, pInfo); + if (pInfo.mNew) { + addPermToList(group.mNewPermissions, pInfo); + } + if ((group.flags&PermissionGroupInfo.FLAG_PERSONAL_INFO) != 0) { + addPermToList(group.mPersonalPermissions, pInfo); + } else { + addPermToList(group.mDevicePermissions, pInfo); } } } - // Second pass to actually form the descriptions - // Look at dangerous permissions first - aggregateGroupDescs(newMap, mNewMap, true); - aggregateGroupDescs(dangerousMap, mDangerousMap, false); - aggregateGroupDescs(normalMap, mNormalMap, false); } - mCurrentState = State.NO_PERMS; - if (mNewMap.size() > 0 || mDangerousMap.size() > 0) { - mCurrentState = (mNormalMap.size() > 0) ? State.BOTH : State.DANGEROUS_ONLY; - } else if(mNormalMap.size() > 0) { - mCurrentState = State.NORMAL_ONLY; + for (MyPermissionGroupInfo pgrp : mPermGroups.values()) { + if (pgrp.labelRes != 0 || pgrp.nonLocalizedLabel != null) { + pgrp.mLabel = pgrp.loadLabel(mPm); + } else { + ApplicationInfo app; + try { + app = mPm.getApplicationInfo(pgrp.packageName, 0); + pgrp.mLabel = app.loadLabel(mPm); + } catch (NameNotFoundException e) { + pgrp.mLabel = pgrp.loadLabel(mPm); + } + } + mPermGroupsList.add(pgrp); + } + Collections.sort(mPermGroupsList, mPermGroupComparator); + if (false) { + for (MyPermissionGroupInfo grp : mPermGroupsList) { + Log.i("foo", "Group " + grp.name + " personal=" + + ((grp.flags&PermissionGroupInfo.FLAG_PERSONAL_INFO) != 0) + + " priority=" + grp.priority); + } } - if(localLOGV) Log.i(TAG, "mCurrentState=" + mCurrentState); - showPermissions(); - } - - public void onClick(View v) { - if(localLOGV) Log.i(TAG, "mExpanded="+mExpanded); - mExpanded = !mExpanded; - showPermissions(); } } diff --git a/core/java/android/widget/CalendarView.java b/core/java/android/widget/CalendarView.java index 2a74f6a..b06da06 100644 --- a/core/java/android/widget/CalendarView.java +++ b/core/java/android/widget/CalendarView.java @@ -421,6 +421,8 @@ public class CalendarView extends FrameLayout { * Sets the number of weeks to be shown. * * @param count The shown week count. + * + * @attr ref android.R.styleable#CalendarView_shownWeekCount */ public void setShownWeekCount(int count) { if (mShownWeekCount != count) { @@ -433,6 +435,8 @@ public class CalendarView extends FrameLayout { * Gets the number of weeks to be shown. * * @return The shown week count. + * + * @attr ref android.R.styleable#CalendarView_shownWeekCount */ public int getShownWeekCount() { return mShownWeekCount; @@ -442,6 +446,8 @@ public class CalendarView extends FrameLayout { * Sets the background color for the selected week. * * @param color The week background color. + * + * @attr ref android.R.styleable#CalendarView_selectedWeekBackgroundColor */ public void setSelectedWeekBackgroundColor(int color) { if (mSelectedWeekBackgroundColor != color) { @@ -460,6 +466,8 @@ public class CalendarView extends FrameLayout { * Gets the background color for the selected week. * * @return The week background color. + * + * @attr ref android.R.styleable#CalendarView_selectedWeekBackgroundColor */ public int getSelectedWeekBackgroundColor() { return mSelectedWeekBackgroundColor; @@ -469,6 +477,8 @@ public class CalendarView extends FrameLayout { * Sets the color for the dates of the focused month. * * @param color The focused month date color. + * + * @attr ref android.R.styleable#CalendarView_focusedMonthDateColor */ public void setFocusedMonthDateColor(int color) { if (mFocusedMonthDateColor != color) { @@ -487,6 +497,8 @@ public class CalendarView extends FrameLayout { * Gets the color for the dates in the focused month. * * @return The focused month date color. + * + * @attr ref android.R.styleable#CalendarView_focusedMonthDateColor */ public int getFocusedMonthDateColor() { return mFocusedMonthDateColor; @@ -496,6 +508,8 @@ public class CalendarView extends FrameLayout { * Sets the color for the dates of a not focused month. * * @param color A not focused month date color. + * + * @attr ref android.R.styleable#CalendarView_unfocusedMonthDateColor */ public void setUnfocusedMonthDateColor(int color) { if (mUnfocusedMonthDateColor != color) { @@ -514,6 +528,8 @@ public class CalendarView extends FrameLayout { * Gets the color for the dates in a not focused month. * * @return A not focused month date color. + * + * @attr ref android.R.styleable#CalendarView_unfocusedMonthDateColor */ public int getUnfocusedMonthDateColor() { return mFocusedMonthDateColor; @@ -523,6 +539,8 @@ public class CalendarView extends FrameLayout { * Sets the color for the week numbers. * * @param color The week number color. + * + * @attr ref android.R.styleable#CalendarView_weekNumberColor */ public void setWeekNumberColor(int color) { if (mWeekNumberColor != color) { @@ -537,6 +555,8 @@ public class CalendarView extends FrameLayout { * Gets the color for the week numbers. * * @return The week number color. + * + * @attr ref android.R.styleable#CalendarView_weekNumberColor */ public int getWeekNumberColor() { return mWeekNumberColor; @@ -546,6 +566,8 @@ public class CalendarView extends FrameLayout { * Sets the color for the separator line between weeks. * * @param color The week separator color. + * + * @attr ref android.R.styleable#CalendarView_weekSeparatorLineColor */ public void setWeekSeparatorLineColor(int color) { if (mWeekSeparatorLineColor != color) { @@ -558,6 +580,8 @@ public class CalendarView extends FrameLayout { * Gets the color for the separator line between weeks. * * @return The week separator color. + * + * @attr ref android.R.styleable#CalendarView_weekSeparatorLineColor */ public int getWeekSeparatorLineColor() { return mWeekSeparatorLineColor; @@ -568,6 +592,8 @@ public class CalendarView extends FrameLayout { * the end of the selected date. * * @param resourceId The vertical bar drawable resource id. + * + * @attr ref android.R.styleable#CalendarView_selectedDateVerticalBar */ public void setSelectedDateVerticalBar(int resourceId) { Drawable drawable = getResources().getDrawable(resourceId); @@ -579,6 +605,8 @@ public class CalendarView extends FrameLayout { * the end of the selected date. * * @param drawable The vertical bar drawable. + * + * @attr ref android.R.styleable#CalendarView_selectedDateVerticalBar */ public void setSelectedDateVerticalBar(Drawable drawable) { if (mSelectedDateVerticalBar != drawable) { @@ -607,6 +635,8 @@ public class CalendarView extends FrameLayout { * Sets the text appearance for the week day abbreviation of the calendar header. * * @param resourceId The text appearance resource id. + * + * @attr ref android.R.styleable#CalendarView_weekDayTextAppearance */ public void setWeekDayTextAppearance(int resourceId) { if (mWeekDayTextAppearanceResId != resourceId) { @@ -619,6 +649,8 @@ public class CalendarView extends FrameLayout { * Gets the text appearance for the week day abbreviation of the calendar header. * * @return The text appearance resource id. + * + * @attr ref android.R.styleable#CalendarView_weekDayTextAppearance */ public int getWeekDayTextAppearance() { return mWeekDayTextAppearanceResId; @@ -628,6 +660,8 @@ public class CalendarView extends FrameLayout { * Sets the text appearance for the calendar dates. * * @param resourceId The text appearance resource id. + * + * @attr ref android.R.styleable#CalendarView_dateTextAppearance */ public void setDateTextAppearance(int resourceId) { if (mDateTextAppearanceResId != resourceId) { @@ -641,6 +675,8 @@ public class CalendarView extends FrameLayout { * Gets the text appearance for the calendar dates. * * @return The text appearance resource id. + * + * @attr ref android.R.styleable#CalendarView_dateTextAppearance */ public int getDateTextAppearance() { return mDateTextAppearanceResId; @@ -683,6 +719,8 @@ public class CalendarView extends FrameLayout { * <p> * * @return The minimal supported date. + * + * @attr ref android.R.styleable#CalendarView_minDate */ public long getMinDate() { return mMinDate.getTimeInMillis(); @@ -694,6 +732,8 @@ public class CalendarView extends FrameLayout { * zone. * * @param minDate The minimal supported date. + * + * @attr ref android.R.styleable#CalendarView_minDate */ public void setMinDate(long minDate) { mTempDate.setTimeInMillis(minDate); @@ -731,6 +771,8 @@ public class CalendarView extends FrameLayout { * <p> * * @return The maximal supported date. + * + * @attr ref android.R.styleable#CalendarView_maxDate */ public long getMaxDate() { return mMaxDate.getTimeInMillis(); @@ -742,6 +784,8 @@ public class CalendarView extends FrameLayout { * zone. * * @param maxDate The maximal supported date. + * + * @attr ref android.R.styleable#CalendarView_maxDate */ public void setMaxDate(long maxDate) { mTempDate.setTimeInMillis(maxDate); @@ -767,6 +811,8 @@ public class CalendarView extends FrameLayout { * Sets whether to show the week number. * * @param showWeekNumber True to show the week number. + * + * @attr ref android.R.styleable#CalendarView_showWeekNumber */ public void setShowWeekNumber(boolean showWeekNumber) { if (mShowWeekNumber == showWeekNumber) { @@ -781,6 +827,8 @@ public class CalendarView extends FrameLayout { * Gets whether to show the week number. * * @return True if showing the week number. + * + * @attr ref android.R.styleable#CalendarView_showWeekNumber */ public boolean getShowWeekNumber() { return mShowWeekNumber; @@ -798,6 +846,8 @@ public class CalendarView extends FrameLayout { * @see Calendar#FRIDAY * @see Calendar#SATURDAY * @see Calendar#SUNDAY + * + * @attr ref android.R.styleable#CalendarView_firstDayOfWeek */ public int getFirstDayOfWeek() { return mFirstDayOfWeek; @@ -815,6 +865,8 @@ public class CalendarView extends FrameLayout { * @see Calendar#FRIDAY * @see Calendar#SATURDAY * @see Calendar#SUNDAY + * + * @attr ref android.R.styleable#CalendarView_firstDayOfWeek */ public void setFirstDayOfWeek(int firstDayOfWeek) { if (mFirstDayOfWeek == firstDayOfWeek) { diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java index 7593bff..cf28da4 100644 --- a/core/java/android/widget/ImageView.java +++ b/core/java/android/widget/ImageView.java @@ -193,6 +193,9 @@ public class ImageView extends View { } } + /** + * @hide + */ @Override public int getResolvedLayoutDirection(Drawable dr) { return (dr == mDrawable) ? diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java index 5ed005f..2391898 100644 --- a/core/java/android/widget/LinearLayout.java +++ b/core/java/android/widget/LinearLayout.java @@ -867,7 +867,7 @@ public class LinearLayout extends ViewGroup { // We have no limit, so make all weighted views as tall as the largest child. // Children will have already been measured once. - if (useLargestChild && widthMode == MeasureSpec.UNSPECIFIED) { + if (useLargestChild && heightMode != MeasureSpec.EXACTLY) { for (int i = 0; i < count; i++) { final View child = getVirtualChildAt(i); @@ -1283,7 +1283,7 @@ public class LinearLayout extends ViewGroup { // We have no limit, so make all weighted views as wide as the largest child. // Children will have already been measured once. - if (useLargestChild && widthMode == MeasureSpec.UNSPECIFIED) { + if (useLargestChild && widthMode != MeasureSpec.EXACTLY) { for (int i = 0; i < count; i++) { final View child = getVirtualChildAt(i); diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java index 0b49404..f3f18d5 100644 --- a/core/java/android/widget/ProgressBar.java +++ b/core/java/android/widget/ProgressBar.java @@ -211,6 +211,7 @@ public class ProgressBar extends View { private boolean mOnlyIndeterminate; private Transformation mTransformation; private AlphaAnimation mAnimation; + private boolean mHasAnimation; private Drawable mIndeterminateDrawable; private Drawable mProgressDrawable; private Drawable mCurrentDrawable; @@ -670,18 +671,14 @@ public class ProgressBar extends View { if (mUiThreadId == Thread.currentThread().getId()) { doRefreshProgress(id, progress, fromUser, true); } else { - RefreshProgressRunnable r; - if (mRefreshProgressRunnable != null) { - // Use cached RefreshProgressRunnable if available - r = mRefreshProgressRunnable; - } else { - // Make a new one - r = new RefreshProgressRunnable(); + if (mRefreshProgressRunnable == null) { + mRefreshProgressRunnable = new RefreshProgressRunnable(); } + final RefreshData rd = RefreshData.obtain(id, progress, fromUser); mRefreshData.add(rd); if (mAttached && !mRefreshIsPosted) { - post(r); + post(mRefreshProgressRunnable); mRefreshIsPosted = true; } } @@ -860,14 +857,26 @@ public class ProgressBar extends View { if (mIndeterminateDrawable instanceof Animatable) { mShouldStartAnimationDrawable = true; - mAnimation = null; + mHasAnimation = false; } else { + mHasAnimation = true; + if (mInterpolator == null) { mInterpolator = new LinearInterpolator(); } - mTransformation = new Transformation(); - mAnimation = new AlphaAnimation(0.0f, 1.0f); + if (mTransformation == null) { + mTransformation = new Transformation(); + } else { + mTransformation.clear(); + } + + if (mAnimation == null) { + mAnimation = new AlphaAnimation(0.0f, 1.0f); + } else { + mAnimation.reset(); + } + mAnimation.setRepeatMode(mBehavior); mAnimation.setRepeatCount(Animation.INFINITE); mAnimation.setDuration(mDuration); @@ -881,8 +890,7 @@ public class ProgressBar extends View { * <p>Stop the indeterminate progress animation.</p> */ void stopAnimation() { - mAnimation = null; - mTransformation = null; + mHasAnimation = false; if (mIndeterminateDrawable instanceof Animatable) { ((Animatable) mIndeterminateDrawable).stop(); mShouldStartAnimationDrawable = false; @@ -967,6 +975,9 @@ public class ProgressBar extends View { } } + /** + * @hide + */ @Override public int getResolvedLayoutDirection(Drawable who) { return (who == mProgressDrawable || who == mIndeterminateDrawable) ? @@ -1027,7 +1038,7 @@ public class ProgressBar extends View { canvas.save(); canvas.translate(mPaddingLeft, mPaddingTop); long time = getDrawingTime(); - if (mAnimation != null) { + if (mHasAnimation) { mAnimation.getTransformation(time, mTransformation); float scale = mTransformation.getAlpha(); try { diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index d8c23a1..56c4bd8 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -1556,15 +1556,13 @@ public class RemoteViews implements Parcelable, Filter { } /** - * Equivalent to calling {@link - * TextView#setCompoundDrawablesRelativeWithIntrinsicBounds(int, int, int, int)}. - * * @param viewId The id of the view whose text should change - * @param start The id of a drawable to place before the text (relative to the + * @param start The id of a drawable to place before the text (relative to the * layout direction), or 0 * @param top The id of a drawable to place above the text, or 0 * @param end The id of a drawable to place after the text, or 0 - * @param bottom The id of a drawable to place below the text, or 0 + * @param bottom The id of a drawable to place below the text, or 0 + * @hide */ public void setTextViewCompoundDrawablesRelative(int viewId, int start, int top, int end, int bottom) { addAction(new TextViewDrawableAction(viewId, true, start, top, end, bottom)); diff --git a/core/java/android/widget/SearchView.java b/core/java/android/widget/SearchView.java index 561326e..521597b 100644 --- a/core/java/android/widget/SearchView.java +++ b/core/java/android/widget/SearchView.java @@ -576,6 +576,8 @@ public class SearchView extends LinearLayout implements CollapsibleActionView { /** * Returns the default iconified state of the search field. * @return + * + * @attr ref android.R.styleable#SearchView_iconifiedByDefault */ public boolean isIconfiedByDefault() { return mIconifiedByDefault; @@ -696,6 +698,8 @@ public class SearchView extends LinearLayout implements CollapsibleActionView { * Gets the specified maximum width in pixels, if set. Returns zero if * no maximum width was specified. * @return the maximum width of the view + * + * @attr ref android.R.styleable#SearchView_maxWidth */ public int getMaxWidth() { return mMaxWidth; diff --git a/core/java/android/widget/ShareActionProvider.java b/core/java/android/widget/ShareActionProvider.java index 080b87d..367561e 100644 --- a/core/java/android/widget/ShareActionProvider.java +++ b/core/java/android/widget/ShareActionProvider.java @@ -44,6 +44,7 @@ import com.android.internal.R; * <code> * // In Activity#onCreateOptionsMenu * public boolean onCreateOptionsMenu(Menu menu) { + * getManuInflater().inflate(R.menu.my_menu, menu); * // Get the menu item. * MenuItem menuItem = menu.findItem(R.id.my_menu_item); * // Get the provider and hold onto it to set/change the share intent. @@ -239,7 +240,11 @@ public class ShareActionProvider extends ActionProvider { * <p> * <strong>Note:</strong> The history file name can be set any time, however * only the action views created by {@link #onCreateActionView()} after setting - * the file name will be backed by the provided file. + * the file name will be backed by the provided file. Hence, if you are using + * a share action provider on a menu item and want to change the history file + * based on the type of the currently selected item, you need to call + * {@link android.app.Activity#invalidateOptionsMenu()} to force the system + * to recreate the menu UI. * <p> * * @param shareHistoryFile The share history file name. diff --git a/core/java/android/widget/SpellChecker.java b/core/java/android/widget/SpellChecker.java index 98e45fb..e1103dd 100644 --- a/core/java/android/widget/SpellChecker.java +++ b/core/java/android/widget/SpellChecker.java @@ -21,10 +21,12 @@ import android.text.Editable; import android.text.Selection; import android.text.SpannableStringBuilder; import android.text.Spanned; +import android.text.TextUtils; import android.text.method.WordIterator; import android.text.style.SpellCheckSpan; import android.text.style.SuggestionSpan; import android.util.Log; +import android.util.LruCache; import android.view.textservice.SentenceSuggestionsInfo; import android.view.textservice.SpellCheckerSession; import android.view.textservice.SpellCheckerSession.SpellCheckerSessionListener; @@ -95,6 +97,10 @@ public class SpellChecker implements SpellCheckerSessionListener { private Runnable mSpellRunnable; + private static final int SUGGESTION_SPAN_CACHE_SIZE = 10; + private final LruCache<Long, SuggestionSpan> mSuggestionSpanCache = + new LruCache<Long, SuggestionSpan>(SUGGESTION_SPAN_CACHE_SIZE); + public SpellChecker(TextView textView) { mTextView = textView; @@ -126,6 +132,7 @@ public class SpellChecker implements SpellCheckerSessionListener { // Restore SpellCheckSpans in pool for (int i = 0; i < mLength; i++) { + // Resets id and progress to invalidate spell check span mSpellCheckSpans[i].setSpellCheckInProgress(false); mIds[i] = -1; } @@ -133,6 +140,7 @@ public class SpellChecker implements SpellCheckerSessionListener { // Remove existing misspelled SuggestionSpans mTextView.removeMisspelledSpans((Editable) mTextView.getText()); + mSuggestionSpanCache.evictAll(); } private void setLocale(Locale locale) { @@ -199,6 +207,7 @@ public class SpellChecker implements SpellCheckerSessionListener { public void removeSpellCheckSpan(SpellCheckSpan spellCheckSpan) { for (int i = 0; i < mLength; i++) { if (mSpellCheckSpans[i] == spellCheckSpan) { + // Resets id and progress to invalidate spell check span mSpellCheckSpans[i].setSpellCheckInProgress(false); mIds[i] = -1; return; @@ -211,6 +220,9 @@ public class SpellChecker implements SpellCheckerSessionListener { } public void spellCheck(int start, int end) { + if (DBG) { + Log.d(TAG, "Start spell-checking: " + start + ", " + end); + } final Locale locale = mTextView.getTextServicesLocale(); final boolean isSessionActive = isSessionActive(); if (mCurrentLocale == null || (!(mCurrentLocale.equals(locale)))) { @@ -238,6 +250,9 @@ public class SpellChecker implements SpellCheckerSessionListener { } } + if (DBG) { + Log.d(TAG, "new spell parser."); + } // No available parser found in pool, create a new one SpellParser[] newSpellParsers = new SpellParser[length + 1]; System.arraycopy(mSpellParsers, 0, newSpellParsers, 0, length); @@ -260,13 +275,22 @@ public class SpellChecker implements SpellCheckerSessionListener { for (int i = 0; i < mLength; i++) { final SpellCheckSpan spellCheckSpan = mSpellCheckSpans[i]; - if (spellCheckSpan.isSpellCheckInProgress()) continue; + if (mIds[i] < 0 || spellCheckSpan.isSpellCheckInProgress()) continue; final int start = editable.getSpanStart(spellCheckSpan); final int end = editable.getSpanEnd(spellCheckSpan); // Do not check this word if the user is currently editing it - if (start >= 0 && end > start && (selectionEnd < start || selectionStart > end)) { + final boolean isEditing; + if (mIsSentenceSpellCheckSupported) { + // Allow the overlap of the cursor and the first boundary of the spell check span + // no to skip the spell check of the following word because the + // following word will never be spell-checked even if the user finishes composing + isEditing = selectionEnd <= start || selectionStart > end; + } else { + isEditing = selectionEnd < start || selectionStart > end; + } + if (start >= 0 && end > start && isEditing) { final String word = (editable instanceof SpannableStringBuilder) ? ((SpannableStringBuilder) editable).substring(start, end) : editable.subSequence(start, end).toString(); @@ -372,6 +396,9 @@ public class SpellChecker implements SpellCheckerSessionListener { } private void scheduleNewSpellCheck() { + if (DBG) { + Log.i(TAG, "schedule new spell check."); + } if (mSpellRunnable == null) { mSpellRunnable = new Runnable() { @Override @@ -423,6 +450,20 @@ public class SpellChecker implements SpellCheckerSessionListener { SuggestionSpan suggestionSpan = new SuggestionSpan(mTextView.getContext(), suggestions, SuggestionSpan.FLAG_EASY_CORRECT | SuggestionSpan.FLAG_MISSPELLED); + // TODO: Remove mIsSentenceSpellCheckSupported by extracting an interface + // to share the logic of word level spell checker and sentence level spell checker + if (mIsSentenceSpellCheckSupported) { + final long key = TextUtils.packRangeInLong(start, end); + final SuggestionSpan tempSuggestionSpan = mSuggestionSpanCache.get(key); + if (tempSuggestionSpan != null) { + if (DBG) { + Log.i(TAG, "Cached span on the same position is cleard. " + + editable.subSequence(start, end)); + } + editable.removeSpan(tempSuggestionSpan); + } + mSuggestionSpanCache.put(key, suggestionSpan); + } editable.setSpan(suggestionSpan, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); mTextView.invalidateRegion(start, end, false /* No cursor involved */); @@ -447,10 +488,17 @@ public class SpellChecker implements SpellCheckerSessionListener { } private void setRangeSpan(Editable editable, int start, int end) { + if (DBG) { + Log.d(TAG, "set next range span: " + start + ", " + end); + } editable.setSpan(mRange, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } private void removeRangeSpan(Editable editable) { + if (DBG) { + Log.d(TAG, "Remove range span." + editable.getSpanStart(editable) + + editable.getSpanEnd(editable)); + } editable.removeSpan(mRange); } @@ -484,6 +532,9 @@ public class SpellChecker implements SpellCheckerSessionListener { wordEnd = mWordIterator.getEnd(wordStart); } if (wordEnd == BreakIterator.DONE) { + if (DBG) { + Log.i(TAG, "No more spell check."); + } removeRangeSpan(editable); return; } @@ -499,47 +550,72 @@ public class SpellChecker implements SpellCheckerSessionListener { boolean scheduleOtherSpellCheck = false; if (mIsSentenceSpellCheckSupported) { - int regionEnd; if (wordIteratorWindowEnd < end) { + if (DBG) { + Log.i(TAG, "schedule other spell check."); + } // Several batches needed on that region. Cut after last previous word - regionEnd = mWordIterator.preceding(wordIteratorWindowEnd); scheduleOtherSpellCheck = true; - } else { - regionEnd = mWordIterator.preceding(end); } - boolean correct = regionEnd != BreakIterator.DONE; + int spellCheckEnd = mWordIterator.preceding(wordIteratorWindowEnd); + boolean correct = spellCheckEnd != BreakIterator.DONE; if (correct) { - regionEnd = mWordIterator.getEnd(regionEnd); - correct = regionEnd != BreakIterator.DONE; + spellCheckEnd = mWordIterator.getEnd(spellCheckEnd); + correct = spellCheckEnd != BreakIterator.DONE; } if (!correct) { - editable.removeSpan(mRange); - return; - } - // Stop spell checking when there are no characters in the range. - if (wordEnd < start) { - return; - } - // TODO: Find the start position of the sentence. - final int spellCheckStart = wordStart; - if (regionEnd <= spellCheckStart) { + if (DBG) { + Log.i(TAG, "Incorrect range span."); + } + removeRangeSpan(editable); return; } - final int selectionStart = Selection.getSelectionStart(editable); - final int selectionEnd = Selection.getSelectionEnd(editable); - if (DBG) { - Log.d(TAG, "addSpellCheckSpan: " - + editable.subSequence(spellCheckStart, regionEnd) - + ", regionEnd = " + regionEnd + ", spellCheckStart = " - + spellCheckStart + ", sel start = " + selectionStart + ", sel end =" - + selectionEnd); - } - // Do not check this word if the user is currently editing it - if (spellCheckStart >= 0 && regionEnd > spellCheckStart - && (selectionEnd < spellCheckStart || selectionStart > regionEnd)) { - addSpellCheckSpan(editable, spellCheckStart, regionEnd); - } - wordStart = regionEnd; + do { + // TODO: Find the start position of the sentence. + int spellCheckStart = wordStart; + boolean createSpellCheckSpan = true; + // Cancel or merge overlapped spell check spans + for (int i = 0; i < mLength; ++i) { + final SpellCheckSpan spellCheckSpan = mSpellCheckSpans[i]; + if (mIds[i] < 0 || spellCheckSpan.isSpellCheckInProgress()) { + continue; + } + final int spanStart = editable.getSpanStart(spellCheckSpan); + final int spanEnd = editable.getSpanEnd(spellCheckSpan); + if (spanEnd < spellCheckStart || spellCheckEnd < spanStart) { + // No need to merge + continue; + } + if (spanStart <= spellCheckStart && spellCheckEnd <= spanEnd) { + // There is a completely overlapped spell check span + // skip this span + createSpellCheckSpan = false; + if (DBG) { + Log.i(TAG, "The range is overrapped. Skip spell check."); + } + break; + } + removeSpellCheckSpan(spellCheckSpan); + spellCheckStart = Math.min(spanStart, spellCheckStart); + spellCheckEnd = Math.max(spanEnd, spellCheckEnd); + } + + if (DBG) { + Log.d(TAG, "addSpellCheckSpan: " + + ", End = " + spellCheckEnd + ", Start = " + spellCheckStart + + ", next = " + scheduleOtherSpellCheck + "\n" + + editable.subSequence(spellCheckStart, spellCheckEnd)); + } + + // Stop spell checking when there are no characters in the range. + if (spellCheckEnd < start) { + break; + } + if (createSpellCheckSpan) { + addSpellCheckSpan(editable, spellCheckStart, spellCheckEnd); + } + } while (false); + wordStart = spellCheckEnd; } else { while (wordStart <= end) { if (wordEnd >= start && wordEnd > wordStart) { diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index ba814d3..8c81343 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -1512,6 +1512,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener /** * Returns the start padding of the view, plus space for the start * Drawable if any. + * @hide */ public int getCompoundPaddingStart() { resolveDrawables(); @@ -1527,6 +1528,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener /** * Returns the end padding of the view, plus space for the end * Drawable if any. + * @hide */ public int getCompoundPaddingEnd() { resolveDrawables(); @@ -1624,6 +1626,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener /** * Returns the total start padding of the view, including the start * Drawable if any. + * @hide */ public int getTotalPaddingStart() { return getCompoundPaddingStart(); @@ -1632,6 +1635,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener /** * Returns the total end padding of the view, including the end * Drawable if any. + * @hide */ public int getTotalPaddingEnd() { return getCompoundPaddingEnd(); @@ -1834,6 +1838,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * @attr ref android.R.styleable#TextView_drawableTop * @attr ref android.R.styleable#TextView_drawableEnd * @attr ref android.R.styleable#TextView_drawableBottom + * @hide */ public void setCompoundDrawablesRelative(Drawable start, Drawable top, Drawable end, Drawable bottom) { @@ -1955,6 +1960,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * @attr ref android.R.styleable#TextView_drawableTop * @attr ref android.R.styleable#TextView_drawableEnd * @attr ref android.R.styleable#TextView_drawableBottom + * @hide */ @android.view.RemotableViewMethod public void setCompoundDrawablesRelativeWithIntrinsicBounds(int start, int top, int end, @@ -1978,6 +1984,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * @attr ref android.R.styleable#TextView_drawableTop * @attr ref android.R.styleable#TextView_drawableEnd * @attr ref android.R.styleable#TextView_drawableBottom + * @hide */ public void setCompoundDrawablesRelativeWithIntrinsicBounds(Drawable start, Drawable top, Drawable end, Drawable bottom) { @@ -2024,6 +2031,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * @attr ref android.R.styleable#TextView_drawableTop * @attr ref android.R.styleable#TextView_drawableEnd * @attr ref android.R.styleable#TextView_drawableBottom + * @hide */ public Drawable[] getCompoundDrawablesRelative() { final Drawables dr = mDrawables; @@ -4543,6 +4551,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } + /** + * @hide + */ @Override public int getResolvedLayoutDirection(Drawable who) { if (who == null) return View.LAYOUT_DIRECTION_LTR; diff --git a/core/java/com/android/internal/app/ActionBarImpl.java b/core/java/com/android/internal/app/ActionBarImpl.java index 805c0a9..2cbd9cc 100644 --- a/core/java/com/android/internal/app/ActionBarImpl.java +++ b/core/java/com/android/internal/app/ActionBarImpl.java @@ -39,8 +39,8 @@ import android.content.Context; import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.drawable.Drawable; -import android.os.Build; import android.os.Handler; +import android.util.Log; import android.util.TypedValue; import android.view.ActionMode; import android.view.ContextThemeWrapper; @@ -110,10 +110,14 @@ public class ActionBarImpl extends ActionBar { private int mCurWindowVisibility = View.VISIBLE; + private boolean mHiddenByApp; + private boolean mHiddenBySystem; + private boolean mShowingForMode; + + private boolean mNowShowing = true; + private Animator mCurrentShowAnim; - private Animator mCurrentModeAnim; private boolean mShowHideAnimationEnabled; - boolean mWasHiddenBeforeMode; final AnimatorListener mHideListener = new AnimatorListenerAdapter() { @Override @@ -129,6 +133,9 @@ public class ActionBarImpl extends ActionBar { mContainerView.setTransitioning(false); mCurrentShowAnim = null; completeDeferredDestroyActionMode(); + if (mOverlayLayout != null) { + mOverlayLayout.requestFitSystemWindows(); + } } }; @@ -430,16 +437,13 @@ public class ActionBarImpl extends ActionBar { } public ActionMode startActionMode(ActionMode.Callback callback) { - boolean wasHidden = false; if (mActionMode != null) { - wasHidden = mWasHiddenBeforeMode; mActionMode.finish(); } mContextView.killMode(); ActionModeImpl mode = new ActionModeImpl(callback); if (mode.dispatchOnCreate()) { - mWasHiddenBeforeMode = !isShowing() || wasHidden; mode.invalidate(); mContextView.initForMode(mode); animateToMode(true); @@ -584,21 +588,91 @@ public class ActionBarImpl extends ActionBar { @Override public void show() { - show(true, false); + if (mHiddenByApp) { + mHiddenByApp = false; + updateVisibility(false); + } } - public void show(boolean markHiddenBeforeMode, boolean alwaysAnimate) { + private void showForActionMode() { + if (!mShowingForMode) { + mShowingForMode = true; + if (mOverlayLayout != null) { + mOverlayLayout.setShowingForActionMode(true); + } + updateVisibility(false); + } + } + + public void showForSystem() { + if (mHiddenBySystem) { + mHiddenBySystem = false; + updateVisibility(true); + } + } + + @Override + public void hide() { + if (!mHiddenByApp) { + mHiddenByApp = true; + updateVisibility(false); + } + } + + private void hideForActionMode() { + if (mShowingForMode) { + mShowingForMode = false; + if (mOverlayLayout != null) { + mOverlayLayout.setShowingForActionMode(false); + } + updateVisibility(false); + } + } + + public void hideForSystem() { + if (!mHiddenBySystem) { + mHiddenBySystem = true; + updateVisibility(true); + } + } + + private static boolean checkShowingFlags(boolean hiddenByApp, boolean hiddenBySystem, + boolean showingForMode) { + if (showingForMode) { + return true; + } else if (hiddenByApp || hiddenBySystem) { + return false; + } else { + return true; + } + } + + private void updateVisibility(boolean fromSystem) { + // Based on the current state, should we be hidden or shown? + final boolean shown = checkShowingFlags(mHiddenByApp, mHiddenBySystem, + mShowingForMode); + + if (shown) { + if (!mNowShowing) { + mNowShowing = true; + doShow(fromSystem); + } + } else { + if (mNowShowing) { + mNowShowing = false; + doHide(fromSystem); + } + } + } + + public void doShow(boolean fromSystem) { if (mCurrentShowAnim != null) { mCurrentShowAnim.end(); } - if (mTopVisibilityView.getVisibility() == View.VISIBLE) { - if (markHiddenBeforeMode) mWasHiddenBeforeMode = false; - return; - } mTopVisibilityView.setVisibility(View.VISIBLE); if (mCurWindowVisibility == View.VISIBLE && (mShowHideAnimationEnabled - || alwaysAnimate)) { + || fromSystem)) { mTopVisibilityView.setAlpha(0); mTopVisibilityView.setTranslationY(-mTopVisibilityView.getHeight()); AnimatorSet anim = new AnimatorSet(); @@ -617,6 +691,18 @@ public class ActionBarImpl extends ActionBar { } anim.setInterpolator(AnimationUtils.loadInterpolator(mContext, com.android.internal.R.interpolator.decelerate_quad)); + anim.setDuration(mContext.getResources().getInteger( + com.android.internal.R.integer.config_mediumAnimTime)); + // If this is being shown from the system, add a small delay. + // This is because we will also be animating in the status bar, + // and these two elements can't be done in lock-step. So we give + // a little time for the status bar to start its animation before + // the action bar animates. (This corresponds to the corresponding + // case when hiding, where the status bar has a small delay before + // starting.) + if (fromSystem) { + anim.setStartDelay(100); + } anim.addListener(mShowListener); mCurrentShowAnim = anim; anim.start(); @@ -625,23 +711,18 @@ public class ActionBarImpl extends ActionBar { mContainerView.setTranslationY(0); mShowListener.onAnimationEnd(null); } + if (mOverlayLayout != null) { + mOverlayLayout.requestFitSystemWindows(); + } } - @Override - public void hide() { - hide(false); - } - - public void hide(boolean alwaysAnimate) { + public void doHide(boolean fromSystem) { if (mCurrentShowAnim != null) { mCurrentShowAnim.end(); } - if (mTopVisibilityView.getVisibility() == View.GONE) { - return; - } if (mCurWindowVisibility == View.VISIBLE && (mShowHideAnimationEnabled - || alwaysAnimate)) { + || fromSystem)) { mTopVisibilityView.setAlpha(1); mContainerView.setTransitioning(true); AnimatorSet anim = new AnimatorSet(); @@ -660,6 +741,8 @@ public class ActionBarImpl extends ActionBar { } anim.setInterpolator(AnimationUtils.loadInterpolator(mContext, com.android.internal.R.interpolator.accelerate_quad)); + anim.setDuration(mContext.getResources().getInteger( + com.android.internal.R.integer.config_mediumAnimTime)); anim.addListener(mHideListener); mCurrentShowAnim = anim; anim.start(); @@ -669,15 +752,18 @@ public class ActionBarImpl extends ActionBar { } public boolean isShowing() { - return mTopVisibilityView.getVisibility() == View.VISIBLE; + return mNowShowing; + } + + public boolean isSystemShowing() { + return !mHiddenBySystem; } void animateToMode(boolean toActionMode) { if (toActionMode) { - show(false, false); - } - if (mCurrentModeAnim != null) { - mCurrentModeAnim.end(); + showForActionMode(); + } else { + hideForActionMode(); } mActionView.animateToVisibility(toActionMode ? View.GONE : View.VISIBLE); @@ -736,11 +822,13 @@ public class ActionBarImpl extends ActionBar { return; } - // If we were hidden before the mode was shown, defer the onDestroy - // callback until the animation is finished and associated relayout - // is about to happen. This lets apps better anticipate visibility - // and layout behavior. - if (mWasHiddenBeforeMode) { + // If this change in state is going to cause the action bar + // to be hidden, defer the onDestroy callback until the animation + // is finished and associated relayout is about to happen. This lets + // apps better anticipate visibility and layout behavior. + if (!checkShowingFlags(mHiddenByApp, mHiddenBySystem, false)) { + // With the current state but the action bar hidden, our + // overall showing state is going to be false. mDeferredDestroyActionMode = this; mDeferredModeDestroyCallback = mCallback; } else { @@ -754,10 +842,6 @@ public class ActionBarImpl extends ActionBar { mActionView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); mActionMode = null; - - if (mWasHiddenBeforeMode) { - hide(); - } } @Override diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java index 51bbdf1..4f2afa7 100644 --- a/core/java/com/android/internal/app/ResolverActivity.java +++ b/core/java/com/android/internal/app/ResolverActivity.java @@ -19,15 +19,17 @@ package com.android.internal.app; import com.android.internal.R; import com.android.internal.content.PackageMonitor; +import android.app.ActivityManager; import android.content.ComponentName; import android.content.Context; -import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ActivityInfo; import android.content.pm.LabeledIntent; import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; +import android.content.res.Resources; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Bundle; @@ -38,8 +40,8 @@ import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.BaseAdapter; -import android.widget.CheckBox; -import android.widget.CompoundButton; +import android.widget.Button; +import android.widget.GridView; import android.widget.ImageView; import android.widget.ListView; import android.widget.TextView; @@ -56,12 +58,19 @@ import java.util.Set; * which there is more than one matching activity, allowing the user to decide * which to go to. It is not normally used directly by application developers. */ -public class ResolverActivity extends AlertActivity implements - DialogInterface.OnClickListener, CheckBox.OnCheckedChangeListener { +public class ResolverActivity extends AlertActivity implements AdapterView.OnItemClickListener { + private static final String TAG = "ResolverActivity"; + private ResolveListAdapter mAdapter; - private CheckBox mAlwaysCheck; - private TextView mClearDefaultHint; private PackageManager mPm; + private boolean mAlwaysUseOption; + private boolean mShowExtended; + private GridView mGrid; + private Button mAlwaysButton; + private Button mOnceButton; + private int mIconDpi; + private int mIconSize; + private int mMaxColumns; private boolean mRegistered; private final PackageMonitor mPackageMonitor = new PackageMonitor() { @@ -91,33 +100,38 @@ public class ResolverActivity extends AlertActivity implements protected void onCreate(Bundle savedInstanceState, Intent intent, CharSequence title, Intent[] initialIntents, List<ResolveInfo> rList, boolean alwaysUseOption) { + setTheme(R.style.Theme_DeviceDefault_Light_Dialog_Alert); super.onCreate(savedInstanceState); mPm = getPackageManager(); + mAlwaysUseOption = alwaysUseOption; + mMaxColumns = getResources().getInteger(R.integer.config_maxResolverActivityColumns); intent.setComponent(null); AlertController.AlertParams ap = mAlertParams; ap.mTitle = title; - ap.mOnClickListener = this; mPackageMonitor.register(this, getMainLooper(), false); mRegistered = true; - if (alwaysUseOption) { - LayoutInflater inflater = (LayoutInflater) getSystemService( - Context.LAYOUT_INFLATER_SERVICE); - ap.mView = inflater.inflate(R.layout.always_use_checkbox, null); - mAlwaysCheck = (CheckBox)ap.mView.findViewById(com.android.internal.R.id.alwaysUse); - mAlwaysCheck.setText(R.string.alwaysUse); - mAlwaysCheck.setOnCheckedChangeListener(this); - mClearDefaultHint = (TextView)ap.mView.findViewById( - com.android.internal.R.id.clearDefaultHint); - mClearDefaultHint.setVisibility(View.GONE); - } + final ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE); + mIconDpi = am.getLauncherLargeIconDensity(); + mIconSize = am.getLauncherLargeIconSize(); + mAdapter = new ResolveListAdapter(this, intent, initialIntents, rList); int count = mAdapter.getCount(); if (count > 1) { - ap.mAdapter = mAdapter; + ap.mView = getLayoutInflater().inflate(R.layout.resolver_grid, null); + mGrid = (GridView) ap.mView.findViewById(R.id.resolver_grid); + mGrid.setAdapter(mAdapter); + mGrid.setOnItemClickListener(this); + mGrid.setOnItemLongClickListener(new ItemLongClickListener()); + + if (alwaysUseOption) { + mGrid.setChoiceMode(ListView.CHOICE_MODE_SINGLE); + } + + resizeGrid(); } else if (count == 1) { startActivity(mAdapter.intentForPosition(0)); mPackageMonitor.unregister(); @@ -125,17 +139,57 @@ public class ResolverActivity extends AlertActivity implements finish(); return; } else { - ap.mMessage = getResources().getText(com.android.internal.R.string.noApplications); + ap.mMessage = getResources().getText(R.string.noApplications); } setupAlert(); - ListView lv = mAlert.getListView(); - if (lv != null) { - lv.setOnItemLongClickListener(new ItemLongClickListener()); + if (alwaysUseOption) { + final ViewGroup buttonLayout = (ViewGroup) findViewById(R.id.button_bar); + buttonLayout.setVisibility(View.VISIBLE); + mAlwaysButton = (Button) buttonLayout.findViewById(R.id.button_always); + mOnceButton = (Button) buttonLayout.findViewById(R.id.button_once); } } + void resizeGrid() { + final int itemCount = mAdapter.getCount(); + mGrid.setNumColumns(Math.min(itemCount, mMaxColumns)); + } + + Drawable getIcon(Resources res, int resId) { + Drawable result; + try { + result = res.getDrawableForDensity(resId, mIconDpi); + } catch (Resources.NotFoundException e) { + result = null; + } + + return result; + } + + Drawable loadIconForResolveInfo(ResolveInfo ri) { + Drawable dr; + try { + if (ri.resolvePackageName != null && ri.icon != 0) { + dr = getIcon(mPm.getResourcesForApplication(ri.resolvePackageName), ri.icon); + if (dr != null) { + return dr; + } + } + final int iconRes = ri.getIconResource(); + if (iconRes != 0) { + dr = getIcon(mPm.getResourcesForApplication(ri.activityInfo.packageName), iconRes); + if (dr != null) { + return dr; + } + } + } catch (NameNotFoundException e) { + Log.e(TAG, "Couldn't find resources for package", e); + } + return ri.loadIcon(mPm); + } + @Override protected void onRestart() { super.onRestart(); @@ -155,11 +209,28 @@ public class ResolverActivity extends AlertActivity implements } } - public void onClick(DialogInterface dialog, int which) { + @Override + public void onItemClick(AdapterView<?> parent, View view, int position, long id) { + if (mAlwaysUseOption) { + final int checkedPos = mGrid.getCheckedItemPosition(); + final boolean enabled = checkedPos != GridView.INVALID_POSITION; + mAlwaysButton.setEnabled(enabled); + mOnceButton.setEnabled(enabled); + } else { + startSelected(position, false); + } + } + + public void onButtonClick(View v) { + final int id = v.getId(); + startSelected(mGrid.getCheckedItemPosition(), id == R.id.button_always); + dismiss(); + } + + void startSelected(int which, boolean always) { ResolveInfo ri = mAdapter.resolveInfoForPosition(which); Intent intent = mAdapter.intentForPosition(which); - boolean alwaysCheck = (mAlwaysCheck != null && mAlwaysCheck.isChecked()); - onIntentSelected(ri, intent, alwaysCheck); + onIntentSelected(ri, intent, always); finish(); } @@ -249,6 +320,12 @@ public class ResolverActivity extends AlertActivity implements } } + void showAppDetails(ResolveInfo ri) { + Intent in = new Intent().setAction("android.settings.APPLICATION_DETAILS_SETTINGS") + .setData(Uri.fromParts("package", ri.activityInfo.packageName, null)); + startActivity(in); + } + private final class DisplayResolveInfo { ResolveInfo ri; CharSequence displayLabel; @@ -285,12 +362,18 @@ public class ResolverActivity extends AlertActivity implements } public void handlePackagesChanged() { + final int oldItemCount = getCount(); rebuildList(); notifyDataSetChanged(); if (mList.size() <= 0) { // We no longer have any items... just finish the activity. finish(); } + + final int newItemCount = getCount(); + if (newItemCount != oldItemCount) { + resizeGrid(); + } } private void rebuildList() { @@ -299,7 +382,7 @@ public class ResolverActivity extends AlertActivity implements } else { mCurrentResolveList = mPm.queryIntentActivities( mIntent, PackageManager.MATCH_DEFAULT_ONLY - | (mAlwaysCheck != null ? PackageManager.GET_RESOLVED_FILTER : 0)); + | (mAlwaysUseOption ? PackageManager.GET_RESOLVED_FILTER : 0)); } int N; if ((mCurrentResolveList != null) && ((N = mCurrentResolveList.size()) > 0)) { @@ -363,6 +446,7 @@ public class ResolverActivity extends AlertActivity implements r0 = mCurrentResolveList.get(0); int start = 0; CharSequence r0Label = r0.loadLabel(mPm); + mShowExtended = false; for (int i = 1; i < N; i++) { if (r0Label == null) { r0Label = r0.activityInfo.packageName; @@ -393,6 +477,7 @@ public class ResolverActivity extends AlertActivity implements // No duplicate labels. Use label for entry at start mList.add(new DisplayResolveInfo(ro, roLabel, null, null)); } else { + mShowExtended = true; boolean usePkg = false; CharSequence startApp = ro.activityInfo.applicationInfo.loadLabel(mPm); if (startApp == null) { @@ -473,6 +558,11 @@ public class ResolverActivity extends AlertActivity implements if (convertView == null) { view = mInflater.inflate( com.android.internal.R.layout.resolve_list_item, parent, false); + + // Fix the icon size even if we have different sized resources + ImageView icon = (ImageView)view.findViewById(R.id.icon); + ViewGroup.LayoutParams lp = (ViewGroup.LayoutParams) icon.getLayoutParams(); + lp.width = lp.height = mIconSize; } else { view = convertView; } @@ -485,37 +575,25 @@ public class ResolverActivity extends AlertActivity implements TextView text2 = (TextView)view.findViewById(com.android.internal.R.id.text2); ImageView icon = (ImageView)view.findViewById(R.id.icon); text.setText(info.displayLabel); - if (info.extendedInfo != null) { + if (mShowExtended) { text2.setVisibility(View.VISIBLE); text2.setText(info.extendedInfo); } else { text2.setVisibility(View.GONE); } if (info.displayIcon == null) { - info.displayIcon = info.ri.loadIcon(mPm); + info.displayIcon = loadIconForResolveInfo(info.ri); } icon.setImageDrawable(info.displayIcon); } } - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - if (mClearDefaultHint == null) return; - - if(isChecked) { - mClearDefaultHint.setVisibility(View.VISIBLE); - } else { - mClearDefaultHint.setVisibility(View.GONE); - } - } - class ItemLongClickListener implements AdapterView.OnItemLongClickListener { @Override public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) { ResolveInfo ri = mAdapter.resolveInfoForPosition(position); - Intent in = new Intent().setAction("android.settings.APPLICATION_DETAILS_SETTINGS") - .setData(Uri.fromParts("package", ri.activityInfo.packageName, null)); - startActivity(in); + showAppDetails(ri); return true; } diff --git a/core/java/com/android/internal/net/NetworkStatsFactory.java b/core/java/com/android/internal/net/NetworkStatsFactory.java index d59585f..8b222f0 100644 --- a/core/java/com/android/internal/net/NetworkStatsFactory.java +++ b/core/java/com/android/internal/net/NetworkStatsFactory.java @@ -24,20 +24,12 @@ import static com.android.server.NetworkManagementSocketTagger.kernelToTag; import android.net.NetworkStats; import android.os.StrictMode; import android.os.SystemClock; -import android.util.Slog; import com.android.internal.util.ProcFileReader; -import com.google.android.collect.Lists; -import com.google.android.collect.Sets; -import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; -import java.io.FileReader; import java.io.IOException; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.StringTokenizer; import libcore.io.IoUtils; @@ -50,14 +42,10 @@ public class NetworkStatsFactory { // TODO: consider moving parsing to native code - /** Path to {@code /proc/net/dev}. */ - @Deprecated - private final File mStatsIface; - /** Path to {@code /proc/net/xt_qtaguid/iface_stat}. */ - @Deprecated - private final File mStatsXtIface; /** Path to {@code /proc/net/xt_qtaguid/iface_stat_all}. */ private final File mStatsXtIfaceAll; + /** Path to {@code /proc/net/xt_qtaguid/iface_stat_fmt}. */ + private final File mStatsXtIfaceFmt; /** Path to {@code /proc/net/xt_qtaguid/stats}. */ private final File mStatsXtUid; @@ -67,28 +55,20 @@ public class NetworkStatsFactory { // @VisibleForTesting public NetworkStatsFactory(File procRoot) { - mStatsIface = new File(procRoot, "net/dev"); - mStatsXtUid = new File(procRoot, "net/xt_qtaguid/stats"); - mStatsXtIface = new File(procRoot, "net/xt_qtaguid/iface_stat"); mStatsXtIfaceAll = new File(procRoot, "net/xt_qtaguid/iface_stat_all"); + mStatsXtIfaceFmt = new File(procRoot, "net/xt_qtaguid/iface_stat_fmt"); + mStatsXtUid = new File(procRoot, "net/xt_qtaguid/stats"); } /** - * Parse and return interface-level summary {@link NetworkStats}. Values - * monotonically increase since device boot, and may include details about - * inactive interfaces. + * Parse and return interface-level summary {@link NetworkStats} measured + * using {@code /proc/net/dev} style hooks, which may include non IP layer + * traffic. Values monotonically increase since device boot, and may include + * details about inactive interfaces. * * @throws IllegalStateException when problem parsing stats. */ - public NetworkStats readNetworkStatsSummary() throws IllegalStateException { - if (mStatsXtIfaceAll.exists()) { - return readNetworkStatsSummarySingleFile(); - } else { - return readNetworkStatsSummaryMultipleFiles(); - } - } - - private NetworkStats readNetworkStatsSummarySingleFile() { + public NetworkStats readNetworkStatsSummaryDev() throws IllegalStateException { final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads(); final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 6); @@ -137,79 +117,40 @@ public class NetworkStatsFactory { } /** - * @deprecated remove once {@code iface_stat_all} is merged to all kernels. + * Parse and return interface-level summary {@link NetworkStats}. Designed + * to return only IP layer traffic. Values monotonically increase since + * device boot, and may include details about inactive interfaces. + * + * @throws IllegalStateException when problem parsing stats. */ - @Deprecated - private NetworkStats readNetworkStatsSummaryMultipleFiles() { + public NetworkStats readNetworkStatsSummaryXt() throws IllegalStateException { final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads(); + // return null when kernel doesn't support + if (!mStatsXtIfaceFmt.exists()) return null; + final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 6); final NetworkStats.Entry entry = new NetworkStats.Entry(); - final HashSet<String> knownIfaces = Sets.newHashSet(); - final HashSet<String> activeIfaces = Sets.newHashSet(); - - // collect any historical stats and active state - for (String iface : fileListWithoutNull(mStatsXtIface)) { - final File ifacePath = new File(mStatsXtIface, iface); - - final long active = readSingleLongFromFile(new File(ifacePath, "active")); - if (active == 1) { - knownIfaces.add(iface); - activeIfaces.add(iface); - } else if (active == 0) { - knownIfaces.add(iface); - } else { - continue; - } - - entry.iface = iface; - entry.uid = UID_ALL; - entry.set = SET_ALL; - entry.tag = TAG_NONE; - entry.rxBytes = readSingleLongFromFile(new File(ifacePath, "rx_bytes")); - entry.rxPackets = readSingleLongFromFile(new File(ifacePath, "rx_packets")); - entry.txBytes = readSingleLongFromFile(new File(ifacePath, "tx_bytes")); - entry.txPackets = readSingleLongFromFile(new File(ifacePath, "tx_packets")); + ProcFileReader reader = null; + try { + // open and consume header line + reader = new ProcFileReader(new FileInputStream(mStatsXtIfaceFmt)); + reader.finishLine(); - stats.addValues(entry); - } + while (reader.hasMoreData()) { + entry.iface = reader.nextString(); + entry.uid = UID_ALL; + entry.set = SET_ALL; + entry.tag = TAG_NONE; - final ArrayList<String> values = Lists.newArrayList(); + entry.rxBytes = reader.nextLong(); + entry.rxPackets = reader.nextLong(); + entry.txBytes = reader.nextLong(); + entry.txPackets = reader.nextLong(); - BufferedReader reader = null; - try { - reader = new BufferedReader(new FileReader(mStatsIface)); - - // skip first two header lines - reader.readLine(); - reader.readLine(); - - // parse remaining lines - String line; - while ((line = reader.readLine()) != null) { - splitLine(line, values); - - try { - entry.iface = values.get(0); - entry.uid = UID_ALL; - entry.set = SET_ALL; - entry.tag = TAG_NONE; - entry.rxBytes = Long.parseLong(values.get(1)); - entry.rxPackets = Long.parseLong(values.get(2)); - entry.txBytes = Long.parseLong(values.get(9)); - entry.txPackets = Long.parseLong(values.get(10)); - - if (activeIfaces.contains(entry.iface)) { - // combine stats when iface is active - stats.combineValues(entry); - } else if (!knownIfaces.contains(entry.iface)) { - // add stats when iface is unknown - stats.addValues(entry); - } - } catch (NumberFormatException e) { - Slog.w(TAG, "problem parsing stats row '" + line + "': " + e); - } + stats.addValues(entry); + reader.finishLine(); } } catch (NullPointerException e) { throw new IllegalStateException("problem parsing stats: " + e); @@ -221,7 +162,6 @@ public class NetworkStatsFactory { IoUtils.closeQuietly(reader); StrictMode.setThreadPolicy(savedPolicy); } - return stats; } @@ -286,41 +226,4 @@ public class NetworkStatsFactory { return stats; } - - /** - * Split given line into {@link ArrayList}. - */ - @Deprecated - private static void splitLine(String line, ArrayList<String> outSplit) { - outSplit.clear(); - - final StringTokenizer t = new StringTokenizer(line, " \t\n\r\f:"); - while (t.hasMoreTokens()) { - outSplit.add(t.nextToken()); - } - } - - /** - * Utility method to read a single plain-text {@link Long} from the given - * {@link File}, usually from a {@code /proc/} filesystem. - */ - private static long readSingleLongFromFile(File file) { - try { - final byte[] buffer = IoUtils.readFileAsByteArray(file.toString()); - return Long.parseLong(new String(buffer).trim()); - } catch (NumberFormatException e) { - return -1; - } catch (IOException e) { - return -1; - } - } - - /** - * Wrapper for {@link File#list()} that returns empty array instead of - * {@code null}. - */ - private static String[] fileListWithoutNull(File file) { - final String[] list = file.list(); - return list != null ? list : new String[0]; - } } diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 86118b1..5157385 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -5719,7 +5719,7 @@ public final class BatteryStatsImpl extends BatteryStats { if (SystemProperties.getBoolean(PROP_QTAGUID_ENABLED, false)) { try { - mNetworkSummaryCache = mNetworkStatsFactory.readNetworkStatsSummary(); + mNetworkSummaryCache = mNetworkStatsFactory.readNetworkStatsSummaryDev(); } catch (IllegalStateException e) { Log.wtf(TAG, "problem reading network stats", e); } diff --git a/core/java/com/android/internal/view/menu/ActionMenuItemView.java b/core/java/com/android/internal/view/menu/ActionMenuItemView.java index d5c2018..449194b 100644 --- a/core/java/com/android/internal/view/menu/ActionMenuItemView.java +++ b/core/java/com/android/internal/view/menu/ActionMenuItemView.java @@ -70,8 +70,13 @@ public class ActionMenuItemView extends TextView setOnClickListener(this); setOnLongClickListener(this); - // Save the inflated padding for later, we'll need it. - mSavedPaddingLeft = getPaddingLeft(); + mSavedPaddingLeft = -1; + } + + @Override + public void setPadding(int l, int t, int r, int b) { + mSavedPaddingLeft = l; + super.setPadding(l, t, r, b); } public MenuItemImpl getItemData() { @@ -217,8 +222,9 @@ public class ActionMenuItemView extends TextView @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { final boolean textVisible = hasText(); - if (textVisible) { - setPadding(mSavedPaddingLeft, getPaddingTop(), getPaddingRight(), getPaddingBottom()); + if (textVisible && mSavedPaddingLeft >= 0) { + super.setPadding(mSavedPaddingLeft, getPaddingTop(), + getPaddingRight(), getPaddingBottom()); } super.onMeasure(widthMeasureSpec, heightMeasureSpec); @@ -240,7 +246,7 @@ public class ActionMenuItemView extends TextView // a little coercion. Pad in to center the icon after we've measured. final int w = getMeasuredWidth(); final int dw = mIcon.getIntrinsicWidth(); - setPadding((w - dw) / 2, getPaddingTop(), getPaddingRight(), getPaddingBottom()); + super.setPadding((w - dw) / 2, getPaddingTop(), getPaddingRight(), getPaddingBottom()); } } } diff --git a/core/java/com/android/internal/view/menu/ActionMenuView.java b/core/java/com/android/internal/view/menu/ActionMenuView.java index af67d55..f54575b 100644 --- a/core/java/com/android/internal/view/menu/ActionMenuView.java +++ b/core/java/com/android/internal/view/menu/ActionMenuView.java @@ -17,6 +17,7 @@ package com.android.internal.view.menu; import android.content.Context; import android.content.res.Configuration; +import android.content.res.TypedArray; import android.util.AttributeSet; import android.view.Gravity; import android.view.View; @@ -25,6 +26,8 @@ import android.view.ViewGroup; import android.view.accessibility.AccessibilityEvent; import android.widget.LinearLayout; +import com.android.internal.R; + /** * @hide */ @@ -43,6 +46,7 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo private int mMinCellSize; private int mGeneratedItemPadding; private int mMeasuredExtraWidth; + private int mMaxItemHeight; public ActionMenuView(Context context) { this(context, null); @@ -54,6 +58,11 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo final float density = context.getResources().getDisplayMetrics().density; mMinCellSize = (int) (MIN_CELL_SIZE * density); mGeneratedItemPadding = (int) (GENERATED_ITEM_PADDING * density); + + TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ActionBar, + R.attr.actionBarStyle, 0); + mMaxItemHeight = a.getDimensionPixelSize(R.styleable.ActionBar_height, 0); + a.recycle(); } public void setPresenter(ActionMenuPresenter presenter) { @@ -116,6 +125,11 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo final int widthPadding = getPaddingLeft() + getPaddingRight(); final int heightPadding = getPaddingTop() + getPaddingBottom(); + final int itemHeightSpec = heightMode == MeasureSpec.EXACTLY + ? MeasureSpec.makeMeasureSpec(heightSize - heightPadding, MeasureSpec.EXACTLY) + : MeasureSpec.makeMeasureSpec( + Math.min(mMaxItemHeight, heightSize - heightPadding), MeasureSpec.AT_MOST); + widthSize -= widthPadding; // Divide the view into cells. @@ -167,7 +181,7 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo final int cellsAvailable = lp.isOverflowButton ? 1 : cellsRemaining; final int cellsUsed = measureChildForCells(child, cellSize, cellsAvailable, - heightMeasureSpec, heightPadding); + itemHeightSpec, heightPadding); maxCellsUsed = Math.max(maxCellsUsed, cellsUsed); if (lp.expandable) expandableItemCount++; @@ -298,7 +312,6 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo // Remeasure any items that have had extra space allocated to them. if (needsExpansion) { - int heightSpec = MeasureSpec.makeMeasureSpec(heightSize - heightPadding, heightMode); for (int i = 0; i < childCount; i++) { final View child = getChildAt(i); final LayoutParams lp = (LayoutParams) child.getLayoutParams(); @@ -306,7 +319,8 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo if (!lp.expanded) continue; final int width = lp.cellsUsed * cellSize + lp.extraPixels; - child.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), heightSpec); + child.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), + itemHeightSpec); } } diff --git a/core/java/com/android/internal/widget/ActionBarOverlayLayout.java b/core/java/com/android/internal/widget/ActionBarOverlayLayout.java index d1652df..a129496 100644 --- a/core/java/com/android/internal/widget/ActionBarOverlayLayout.java +++ b/core/java/com/android/internal/widget/ActionBarOverlayLayout.java @@ -22,6 +22,7 @@ import android.content.Context; import android.content.res.TypedArray; import android.graphics.Rect; import android.util.AttributeSet; +import android.util.Log; import android.view.View; import android.widget.FrameLayout; @@ -76,6 +77,26 @@ public class ActionBarOverlayLayout extends FrameLayout { } } + public void setShowingForActionMode(boolean showing) { + if (showing) { + // Here's a fun hack: if the status bar is currently being hidden, + // and the application has asked for stable content insets, then + // we will end up with the action mode action bar being shown + // without the status bar, but moved below where the status bar + // would be. Not nice. Trying to have this be positioned + // correctly is not easy (basically we need yet *another* content + // inset from the window manager to know where to put it), so + // instead we will just temporarily force the status bar to be shown. + if ((getWindowSystemUiVisibility() & (SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + | SYSTEM_UI_FLAG_LAYOUT_STABLE)) + == (SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | SYSTEM_UI_FLAG_LAYOUT_STABLE)) { + setDisabledSystemUiVisibility(SYSTEM_UI_FLAG_FULLSCREEN); + } + } else { + setDisabledSystemUiVisibility(0); + } + } + @Override public void onWindowSystemUiVisibilityChanged(int visible) { super.onWindowSystemUiVisibilityChanged(visible); @@ -83,11 +104,13 @@ public class ActionBarOverlayLayout extends FrameLayout { final int diff = mLastSystemUiVisibility ^ visible; mLastSystemUiVisibility = visible; final boolean barVisible = (visible&SYSTEM_UI_FLAG_FULLSCREEN) == 0; - final boolean wasVisible = mActionBar != null ? mActionBar.isShowing() : true; - if (barVisible != wasVisible || (diff&SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0) { + final boolean wasVisible = mActionBar != null ? mActionBar.isSystemShowing() : true; + if (mActionBar != null) { + if (barVisible) mActionBar.showForSystem(); + else mActionBar.hideForSystem(); + } + if ((diff&SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0) { if (mActionBar != null) { - if (barVisible) mActionBar.show(true, true); - else mActionBar.hide(true); requestFitSystemWindows(); } } |
