diff options
Diffstat (limited to 'core/java')
28 files changed, 649 insertions, 428 deletions
diff --git a/core/java/android/app/ActionBar.java b/core/java/android/app/ActionBar.java index be00aa5..51c6f3a 100644 --- a/core/java/android/app/ActionBar.java +++ b/core/java/android/app/ActionBar.java @@ -29,16 +29,29 @@ import android.view.Window; import android.widget.SpinnerAdapter; /** - * Acts as a replacement for the title bar in Activities. - * The action bar provides facilities for creating toolbar actions as well as - * methods of navigating the application. - * <p>By default, the action bar appears at the top of every activity, with the application icon on - * the left, followed by the activity title. Items from the activity's options menu are also - * accessible from the action bar.</p> + * A window feature at the top of the activity that may display the activity title, navigation + * modes, and other interactive items. + * <p>Beginning with Android 3.0 (API level 11), the action bar appears at the top of an + * activity's window when the activity uses the system's {@link + * android.R.style#Theme_Holo Holo} theme (or one of its descendant themes), which is the default. + * You may otherwise add the action bar by calling {@link + * android.view.Window#requestFeature requestFeature(FEATURE_ACTION_BAR)} or by declaring it in a + * custom theme with the {@link android.R.styleable#Theme_windowActionBar windowActionBar} property. + * <p>By default, the action bar shows the application icon on + * the left, followed by the activity title. If your activity has an options menu, you can make + * select items accessible directly from the action bar as "action items". You can also + * modify various characteristics of the action bar or remove it completely.</p> * <p>From your activity, you can retrieve an instance of {@link ActionBar} by calling {@link * android.app.Activity#getActionBar getActionBar()}.</p> - * <p>For more information, read the <a href="{@docRoot}guide/topics/ui/actionbar.html">Action + * <p>For information about how to use the action bar, including how to add action items, navigation + * modes and more, read the <a href="{@docRoot}guide/topics/ui/actionbar.html">Action * Bar</a> developer guide.</p> + * <p>In some cases, the action bar may be overlayed by another bar that enables contextual actions, + * using an {@link android.view.ActionMode}. For example, when the user selects one or more items in + * your activity, you can enable an action mode that offers actions specific to the selected + * items, with a UI that temporarily replaces the action bar. Although the UI may occupy the + * same space, the {@link android.view.ActionMode} APIs are distinct and independent from those for + * {@link ActionBar}. */ public abstract class ActionBar { /** @@ -423,6 +436,7 @@ public abstract class ActionBar { * Set the ActionBar's split background. This will appear in * the split action bar containing menu-provided action buttons * on some devices and configurations. + * <p>You can enable split action bar with {@link android.R.attr#uiOptions} * * @param d Background drawable for the split bar */ @@ -460,13 +474,6 @@ public abstract class ActionBar { * </ul> * * @return The current navigation mode. - * - * @see #setStandardNavigationMode() - * @see #setStandardNavigationMode(CharSequence) - * @see #setStandardNavigationMode(CharSequence, CharSequence) - * @see #setDropdownNavigationMode(SpinnerAdapter) - * @see #setTabNavigationMode() - * @see #setCustomNavigationMode(View) */ public abstract int getNavigationMode(); @@ -498,7 +505,6 @@ public abstract class ActionBar { * @return A new Tab * * @see #addTab(Tab) - * @see #insertTab(Tab, int) */ public abstract Tab newTab(); @@ -606,7 +612,7 @@ public abstract class ActionBar { public abstract void show(); /** - * Hide the ActionBar if it is not currently showing. + * Hide the ActionBar if it is currently showing. * If the window hosting the ActionBar does not have the feature * {@link Window#FEATURE_ACTION_BAR_OVERLAY} it will resize application * content to fit the new space available. diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index 7799779..b4471f0 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -1550,6 +1550,13 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM return true; } + case DISMISS_KEYGUARD_ON_NEXT_ACTIVITY_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + dismissKeyguardOnNextActivity(); + reply.writeNoException(); + return true; + } + } return super.onTransact(code, data, reply, flags); @@ -3504,5 +3511,15 @@ class ActivityManagerProxy implements IActivityManager reply.recycle(); } + public void dismissKeyguardOnNextActivity() throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + mRemote.transact(DISMISS_KEYGUARD_ON_NEXT_ACTIVITY_TRANSACTION, data, reply, 0); + reply.readException(); + data.recycle(); + reply.recycle(); + } + private IBinder mRemote; } diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index 27dd691..26813bf 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -372,6 +372,8 @@ public interface IActivityManager extends IInterface { public void showBootMessage(CharSequence msg, boolean always) throws RemoteException; + public void dismissKeyguardOnNextActivity() throws RemoteException; + /* * Private non-Binder interfaces */ @@ -602,4 +604,5 @@ public interface IActivityManager extends IInterface { int UPDATE_PERSISTENT_CONFIGURATION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+135; int GET_PROCESS_PSS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+136; int SHOW_BOOT_MESSAGE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+137; + int DISMISS_KEYGUARD_ON_NEXT_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+138; } diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 72cf26a..f44d038 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -5577,24 +5577,35 @@ public class Intent implements Parcelable, Cloneable { @Override public String toString() { - StringBuilder b = new StringBuilder(128); + StringBuilder b = new StringBuilder(128); b.append("Intent { "); - toShortString(b, true, true); + toShortString(b, true, true, true); b.append(" }"); return b.toString(); } /** @hide */ - public String toShortString(boolean comp, boolean extras) { - StringBuilder b = new StringBuilder(128); - toShortString(b, comp, extras); + public String toInsecureString() { + StringBuilder b = new StringBuilder(128); + + b.append("Intent { "); + toShortString(b, false, true, true); + b.append(" }"); + return b.toString(); } /** @hide */ - public void toShortString(StringBuilder b, boolean comp, boolean extras) { + public String toShortString(boolean secure, boolean comp, boolean extras) { + StringBuilder b = new StringBuilder(128); + toShortString(b, secure, comp, extras); + return b.toString(); + } + + /** @hide */ + public void toShortString(StringBuilder b, boolean secure, boolean comp, boolean extras) { boolean first = true; if (mAction != null) { b.append("act=").append(mAction); @@ -5621,19 +5632,8 @@ public class Intent implements Parcelable, Cloneable { } first = false; b.append("dat="); - String scheme = mData.getScheme(); - if (scheme != null) { - if (scheme.equalsIgnoreCase("tel")) { - b.append("tel:xxx-xxx-xxxx"); - } else if (scheme.equalsIgnoreCase("sip")) { - b.append("sip:xxxxxxxxxx"); - } else if (scheme.equalsIgnoreCase("sms")) { - b.append("sms:xxx-xxx-xxxx"); - } else if (scheme.equalsIgnoreCase("smsto")) { - b.append("smsto:xxx-xxx-xxxx"); - } else { - b.append(mData); - } + if (secure) { + b.append(mData.toSafeString()); } else { b.append(mData); } diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java index bba329d..0e6694d 100644 --- a/core/java/android/content/pm/ActivityInfo.java +++ b/core/java/android/content/pm/ActivityInfo.java @@ -442,7 +442,11 @@ public class ActivityInfo extends ComponentInfo public int uiOptions = 0; /** - * Flag for use with uiOptions. + * Flag for use with {@link #uiOptions}. + * Indicates that the action bar should put all action items in a separate bar when + * the screen is narrow. + * <p>This value corresponds to "splitActionBarWhenNarrow" for the {@link #uiOptions} XML + * attribute. */ public static final int UIOPTION_SPLIT_ACTION_BAR_WHEN_NARROW = 1; diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java index 58f7869..e40de26 100644 --- a/core/java/android/hardware/Camera.java +++ b/core/java/android/hardware/Camera.java @@ -480,6 +480,12 @@ public class Camera { public final void stopPreview() { _stopPreview(); mFaceDetectionRunning = false; + + mShutterCallback = null; + mRawImageCallback = null; + mPostviewCallback = null; + mJpegCallback = null; + mAutoFocusCallback = null; } private native final void _stopPreview(); @@ -766,15 +772,8 @@ public class Camera { * onAutoFocus will be called immediately with a fake value of * <code>success</code> set to <code>true</code>. * - * The auto-focus routine may lock auto-exposure and auto-white balance - * after it completes. To check for the state of these locks, use the - * {@link android.hardware.Camera.Parameters#getAutoExposureLock()} and - * {@link android.hardware.Camera.Parameters#getAutoWhiteBalanceLock()} - * methods. If such locking is undesirable, use - * {@link android.hardware.Camera.Parameters#setAutoExposureLock(boolean)} - * and - * {@link android.hardware.Camera.Parameters#setAutoWhiteBalanceLock(boolean)} - * to release the locks. + * The auto-focus routine does not lock auto-exposure and auto-white + * balance after it completes. * * @param success true if focus was successful, false if otherwise * @param camera the Camera service object @@ -805,16 +804,10 @@ public class Camera { * {@link android.hardware.Camera.Parameters#FLASH_MODE_OFF}, flash may be * fired during auto-focus, depending on the driver and camera hardware.<p> * - * The auto-focus routine may lock auto-exposure and auto-white balance - * after it completes. To check for the state of these locks, use the - * {@link android.hardware.Camera.Parameters#getAutoExposureLock()} and - * {@link android.hardware.Camera.Parameters#getAutoWhiteBalanceLock()} - * methods after the {@link AutoFocusCallback#onAutoFocus(boolean, Camera)} - * callback is invoked. If such locking is undesirable, use - * {@link android.hardware.Camera.Parameters#setAutoExposureLock(boolean)} - * and - * {@link android.hardware.Camera.Parameters#setAutoWhiteBalanceLock(boolean)} - * to release the locks. + * Auto-exposure lock {@link android.hardware.Camera.Parameters#getAutoExposureLock()} + * and auto-white balance locks {@link android.hardware.Camera.Parameters#getAutoWhiteBalanceLock()} + * do not change during and after autofocus. But auto-focus routine may stop + * auto-exposure and auto-white balance transiently during focusing. * * @param cb the callback to run * @see #cancelAutoFocus() @@ -834,13 +827,7 @@ public class Camera { * this function will return the focus position to the default. * If the camera does not support auto-focus, this is a no-op. * - * Canceling auto-focus will return the auto-exposure lock and auto-white - * balance lock to their state before {@link #autoFocus(AutoFocusCallback)} - * was called. - * * @see #autoFocus(Camera.AutoFocusCallback) - * @see android.hardware.Camera.Parameters#setAutoExposureLock(boolean) - * @see android.hardware.Camera.Parameters#setAutoWhiteBalanceLock(boolean) */ public final void cancelAutoFocus() { @@ -2778,13 +2765,12 @@ public class Camera { * * <p>Stopping preview with {@link #stopPreview()}, or triggering still * image capture with {@link #takePicture(Camera.ShutterCallback, - * Camera.PictureCallback, Camera.PictureCallback)}, will automatically - * set the lock to false. However, the lock can be re-enabled before - * preview is re-started to keep the same AE parameters.</p> + * Camera.PictureCallback, Camera.PictureCallback)}, will not change the + * lock.</p> * - * <p>Exposure compensation, in conjunction with re-enabling the AE and - * AWB locks after each still capture, can be used to capture an - * exposure-bracketed burst of images, for example.</p> + * <p>Exposure compensation, auto-exposure lock, and auto-white balance + * lock can be used to capture an exposure-bracketed burst of images, + * for example.</p> * * <p>Auto-exposure state, including the lock state, will not be * maintained after camera {@link #release()} is called. Locking @@ -2793,14 +2779,6 @@ public class Camera { * run at all, and may result in severely over- or under-exposed * images.</p> * - * <p>The driver may also independently lock auto-exposure after - * auto-focus completes. If this is undesirable, be sure to always set - * the auto-exposure lock to false after the - * {@link AutoFocusCallback#onAutoFocus(boolean, Camera)} callback is - * received. The {@link #getAutoExposureLock()} method can be used after - * the callback to determine if the camera has locked auto-exposure - * independently.</p> - * * @param toggle new state of the auto-exposure lock. True means that * auto-exposure is locked, false means that the auto-exposure * routine is free to run normally. @@ -2817,11 +2795,7 @@ public class Camera { * {@link #setAutoExposureLock} for details about the lock. * * @return State of the auto-exposure lock. Returns true if - * auto-exposure is currently locked, and false otherwise. The - * auto-exposure lock may be independently enabled by the camera - * subsystem when auto-focus has completed. This method can be - * used after the {@link AutoFocusCallback#onAutoFocus(boolean, - * Camera)} callback to determine if the camera has locked AE. + * auto-exposure is currently locked, and false otherwise. * * @see #setAutoExposureLock(boolean) * @@ -2859,29 +2833,20 @@ public class Camera { * * <p>Stopping preview with {@link #stopPreview()}, or triggering still * image capture with {@link #takePicture(Camera.ShutterCallback, - * Camera.PictureCallback, Camera.PictureCallback)}, will automatically - * set the lock to false. However, the lock can be re-enabled before - * preview is re-started to keep the same white balance parameters.</p> + * Camera.PictureCallback, Camera.PictureCallback)}, will not change the + * the lock.</p> * * <p> Changing the white balance mode with {@link #setWhiteBalance} * will release the auto-white balance lock if it is set.</p> * - * <p>Exposure compensation, in conjunction with re-enabling the AE and - * AWB locks after each still capture, can be used to capture an - * exposure-bracketed burst of images, for example. Auto-white balance - * state, including the lock state, will not be maintained after camera - * {@link #release()} is called. Locking auto-white balance after - * {@link #open()} but before the first call to {@link #startPreview()} - * will not allow the auto-white balance routine to run at all, and may - * result in severely incorrect color in captured images.</p> - * - * <p>The driver may also independently lock auto-white balance after - * auto-focus completes. If this is undesirable, be sure to always set - * the auto-white balance lock to false after the - * {@link AutoFocusCallback#onAutoFocus(boolean, Camera)} callback is - * received. The {@link #getAutoWhiteBalanceLock()} method can be used - * after the callback to determine if the camera has locked auto-white - * balance independently.</p> + * <p>Exposure compensation, AE lock, and AWB lock can be used to + * capture an exposure-bracketed burst of images, for example. + * Auto-white balance state, including the lock state, will not be + * maintained after camera {@link #release()} is called. Locking + * auto-white balance after {@link #open()} but before the first call to + * {@link #startPreview()} will not allow the auto-white balance routine + * to run at all, and may result in severely incorrect color in captured + * images.</p> * * @param toggle new state of the auto-white balance lock. True means * that auto-white balance is locked, false means that the @@ -2902,11 +2867,7 @@ public class Camera { * * @return State of the auto-white balance lock. Returns true if * auto-white balance is currently locked, and false - * otherwise. The auto-white balance lock may be independently - * enabled by the camera subsystem when auto-focus has - * completed. This method can be used after the - * {@link AutoFocusCallback#onAutoFocus(boolean, Camera)} - * callback to determine if the camera has locked AWB. + * otherwise. * * @see #setAutoWhiteBalanceLock(boolean) * diff --git a/core/java/android/net/DnsPinger.java b/core/java/android/net/DnsPinger.java index 3e27b0d..3291e6b 100644 --- a/core/java/android/net/DnsPinger.java +++ b/core/java/android/net/DnsPinger.java @@ -53,7 +53,7 @@ import java.util.concurrent.atomic.AtomicInteger; public final class DnsPinger extends Handler { private static final boolean V = true; - private static final int RECEIVE_POLL_INTERVAL_MS = 30; + private static final int RECEIVE_POLL_INTERVAL_MS = 200; private static final int DNS_PORT = 53; /** Short socket timeout so we don't block one any 'receive' call */ @@ -70,6 +70,9 @@ public final class DnsPinger extends Handler { private final ArrayList<InetAddress> mDefaultDns; private String TAG; + //Invalidates old dns requests upon a cancel + private AtomicInteger mCurrentToken = new AtomicInteger(); + private static final int BASE = Protocol.BASE_DNS_PINGER; /** @@ -102,6 +105,17 @@ public final class DnsPinger extends Handler { long start = SystemClock.elapsedRealtime(); } + /* Message argument for ACTION_PING_DNS */ + private class DnsArg { + InetAddress dns; + int seq; + + DnsArg(InetAddress d, int s) { + dns = d; + seq = s; + } + } + public DnsPinger(Context context, String TAG, Looper looper, Handler target, int connectionType) { super(looper); @@ -122,9 +136,13 @@ public final class DnsPinger extends Handler { public void handleMessage(Message msg) { switch (msg.what) { case ACTION_PING_DNS: + DnsArg dnsArg = (DnsArg) msg.obj; + if (dnsArg.seq != mCurrentToken.get()) { + break; + } try { ActivePing newActivePing = new ActivePing(); - InetAddress dnsAddress = (InetAddress) msg.obj; + InetAddress dnsAddress = dnsArg.dns; newActivePing.internalId = msg.arg1; newActivePing.timeout = msg.arg2; newActivePing.socket = new DatagramSocket(); @@ -248,11 +266,13 @@ public final class DnsPinger extends Handler { */ public int pingDnsAsync(InetAddress dns, int timeout, int delay) { int id = sCounter.incrementAndGet(); - sendMessageDelayed(obtainMessage(ACTION_PING_DNS, id, timeout, dns), delay); + sendMessageDelayed(obtainMessage(ACTION_PING_DNS, id, timeout, + new DnsArg(dns, mCurrentToken.get())), delay); return id; } public void cancelPings() { + mCurrentToken.incrementAndGet(); obtainMessage(ACTION_CANCEL_ALL_PINGS).sendToTarget(); } diff --git a/core/java/android/net/NetworkStatsHistory.java b/core/java/android/net/NetworkStatsHistory.java index a5cdf70..a6635be 100644 --- a/core/java/android/net/NetworkStatsHistory.java +++ b/core/java/android/net/NetworkStatsHistory.java @@ -270,6 +270,11 @@ public class NetworkStatsHistory implements Parcelable { || entry.operations < 0) { throw new IllegalArgumentException("tried recording negative data"); } + if (entry.rxBytes == 0 && entry.rxPackets == 0 && entry.txBytes == 0 && entry.txPackets == 0 + && entry.operations == 0) { + // nothing to record; skip + return; + } // create any buckets needed by this range ensureBuckets(start, end); diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java index 2c875c8..9d28eff 100644 --- a/core/java/android/net/Uri.java +++ b/core/java/android/net/Uri.java @@ -353,6 +353,48 @@ public abstract class Uri implements Parcelable, Comparable<Uri> { public abstract String toString(); /** + * Return a string representation of the URI that is safe to print + * to logs and other places where PII should be avoided. + * @hide + */ + public String toSafeString() { + String scheme = getScheme(); + String ssp = getSchemeSpecificPart(); + if (scheme != null) { + if (scheme.equalsIgnoreCase("tel") || scheme.equalsIgnoreCase("sip") + || scheme.equalsIgnoreCase("sms") || scheme.equalsIgnoreCase("smsto") + || scheme.equalsIgnoreCase("mailto")) { + StringBuilder builder = new StringBuilder(64); + builder.append(scheme); + builder.append(':'); + if (ssp != null) { + for (int i=0; i<ssp.length(); i++) { + char c = ssp.charAt(i); + if (c == '-' || c == '@' || c == '.') { + builder.append(c); + } else { + builder.append('x'); + } + } + } + return builder.toString(); + } + } + // Not a sensitive scheme, but let's still be conservative about + // the data we include -- only the ssp, not the query params or + // fragment, because those can often have sensitive info. + StringBuilder builder = new StringBuilder(64); + if (scheme != null) { + builder.append(scheme); + builder.append(':'); + } + if (ssp != null) { + builder.append(ssp); + } + return builder.toString(); + } + + /** * Constructs a new builder, copying the attributes from this Uri. */ public abstract Builder buildUpon(); diff --git a/core/java/android/preference/PreferenceScreen.java b/core/java/android/preference/PreferenceScreen.java index 45e3a4c..dd9dd25 100644 --- a/core/java/android/preference/PreferenceScreen.java +++ b/core/java/android/preference/PreferenceScreen.java @@ -25,6 +25,7 @@ import android.os.Parcelable; import android.text.TextUtils; import android.util.AttributeSet; import android.view.View; +import android.view.Window; import android.widget.Adapter; import android.widget.AdapterView; import android.widget.ListAdapter; @@ -156,13 +157,13 @@ public final class PreferenceScreen extends PreferenceGroup implements AdapterVi // Set the title bar if title is available, else no title bar final CharSequence title = getTitle(); - Dialog dialog = mDialog = new Dialog(context, TextUtils.isEmpty(title) - ? com.android.internal.R.style.Theme_NoTitleBar - : com.android.internal.R.style.Theme); - dialog.setContentView(mListView); - if (!TextUtils.isEmpty(title)) { + Dialog dialog = mDialog = new Dialog(context, context.getThemeResId()); + if (TextUtils.isEmpty(title)) { + dialog.getWindow().requestFeature(Window.FEATURE_NO_TITLE); + } else { dialog.setTitle(title); } + dialog.setContentView(mListView); dialog.setOnDismissListener(this); if (state != null) { dialog.onRestoreInstanceState(state); diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java index a52d48e..1e17632 100644 --- a/core/java/android/text/TextLine.java +++ b/core/java/android/text/TextLine.java @@ -679,6 +679,12 @@ class TextLine { wp.getFontMetricsInt(fmi); + updateMetrics(fmi, previousTop, previousAscent, previousDescent, previousBottom, + previousLeading); + } + + static void updateMetrics(FontMetricsInt fmi, int previousTop, int previousAscent, + int previousDescent, int previousBottom, int previousLeading) { fmi.top = Math.min(fmi.top, previousTop); fmi.ascent = Math.min(fmi.ascent, previousAscent); fmi.descent = Math.max(fmi.descent, previousDescent); @@ -809,7 +815,28 @@ class TextLine { int textLimit = mStart + limit; if (needWidth || (c != null && runIsRtl)) { + int previousTop = 0; + int previousAscent = 0; + int previousDescent = 0; + int previousBottom = 0; + int previousLeading = 0; + + boolean needUpdateMetrics = (fmi != null); + + if (needUpdateMetrics) { + previousTop = fmi.top; + previousAscent = fmi.ascent; + previousDescent = fmi.descent; + previousBottom = fmi.bottom; + previousLeading = fmi.leading; + } + ret = replacement.getSize(wp, mText, textStart, textLimit, fmi); + + if (needUpdateMetrics) { + updateMetrics(fmi, previousTop, previousAscent, previousDescent, previousBottom, + previousLeading); + } } if (c != null) { diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index 55c821d..45f9da2 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -116,6 +116,7 @@ interface IWindowManager boolean isKeyguardLocked(); boolean isKeyguardSecure(); boolean inKeyguardRestrictedInputMode(); + void dismissKeyguard(); void closeSystemDialogs(String reason); diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index f993160..3c67521 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -81,7 +81,6 @@ import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.Locale; -import java.util.WeakHashMap; import java.util.concurrent.CopyOnWriteArrayList; /** @@ -1497,12 +1496,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal /** * Map used to store views' tags. */ - private static WeakHashMap<View, SparseArray<Object>> sTags; - - /** - * Lock used to access sTags. - */ - private static final Object sTagsLock = new Object(); + private SparseArray<Object> mKeyedTags; /** * The next available accessiiblity id. @@ -3900,6 +3894,12 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * Note: Called from the default {@link AccessibilityDelegate}. */ boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event) { + // Do not populate text to scroll events. They describe position change + // and usually come from container with a lot of text which is not very + // informative for accessibility purposes. Also they are fired frequently. + if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_SCROLLED) { + return true; + } onPopulateAccessibilityEvent(event); return false; } @@ -12236,14 +12236,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * @see #getTag() */ public Object getTag(int key) { - SparseArray<Object> tags = null; - synchronized (sTagsLock) { - if (sTags != null) { - tags = sTags.get(this); - } - } - - if (tags != null) return tags.get(key); + if (mKeyedTags != null) return mKeyedTags.get(key); return null; } @@ -12276,7 +12269,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal + "resource id."); } - setTagInternal(this, key, tag); + setKeyedTag(key, tag); } /** @@ -12291,27 +12284,15 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal + "resource id."); } - setTagInternal(this, key, tag); + setKeyedTag(key, tag); } - private static void setTagInternal(View view, int key, Object tag) { - SparseArray<Object> tags = null; - synchronized (sTagsLock) { - if (sTags == null) { - sTags = new WeakHashMap<View, SparseArray<Object>>(); - } else { - tags = sTags.get(view); - } - } - - if (tags == null) { - tags = new SparseArray<Object>(2); - synchronized (sTagsLock) { - sTags.put(view, tags); - } + private void setKeyedTag(int key, Object tag) { + if (mKeyedTags == null) { + mKeyedTags = new SparseArray<Object>(); } - tags.put(key, tag); + mKeyedTags.put(key, tag); } /** diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index b678c7d..a29cf13 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -2176,13 +2176,15 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager @Override boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event) { - // We first get a chance to populate the event. - super.dispatchPopulateAccessibilityEventInternal(event); + boolean handled = super.dispatchPopulateAccessibilityEventInternal(event); + if (handled) { + return handled; + } // Let our children have a shot in populating the event. for (int i = 0, count = getChildCount(); i < count; i++) { View child = getChildAt(i); if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) { - boolean handled = getChildAt(i).dispatchPopulateAccessibilityEvent(event); + handled = getChildAt(i).dispatchPopulateAccessibilityEvent(event); if (handled) { return handled; } diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java index 1dbb083..4f67675 100644 --- a/core/java/android/view/WindowManagerPolicy.java +++ b/core/java/android/view/WindowManagerPolicy.java @@ -850,6 +850,11 @@ public interface WindowManagerPolicy { public boolean inKeyguardRestrictedKeyInputMode(); /** + * Ask the policy to dismiss the keyguard, if it is currently shown. + */ + public void dismissKeyguardLw(); + + /** * Given an orientation constant, returns the appropriate surface rotation, * taking into account sensors, docking mode, rotation lock, and other factors. * @@ -876,6 +881,13 @@ public interface WindowManagerPolicy { public boolean rotationHasCompatibleMetricsLw(int orientation, int rotation); /** + * Called by the window manager when the rotation changes. + * + * @param rotation The new rotation. + */ + public void setRotationLw(int rotation); + + /** * Called when the system is mostly done booting to determine whether * the system should go into safe mode. */ diff --git a/core/java/android/view/WindowOrientationListener.java b/core/java/android/view/WindowOrientationListener.java index 726bf4a..c3c74a7 100755 --- a/core/java/android/view/WindowOrientationListener.java +++ b/core/java/android/view/WindowOrientationListener.java @@ -26,7 +26,7 @@ import android.util.Slog; /** * A special helper class used by the WindowManager - * for receiving notifications from the SensorManager when + * for receiving notifications from the SensorManager when * the orientation of the device has changed. * * NOTE: If changing anything here, please run the API demo @@ -54,6 +54,7 @@ public abstract class WindowOrientationListener { private Sensor mSensor; private SensorEventListenerImpl mSensorEventListener; boolean mLogEnabled; + int mCurrentRotation = -1; /** * Creates a new WindowOrientationListener. @@ -117,12 +118,25 @@ public abstract class WindowOrientationListener { } /** - * Gets the current orientation. - * @return The current rotation, or -1 if unknown. + * Sets the current rotation. + * + * @param rotation The current rotation. + */ + public void setCurrentRotation(int rotation) { + mCurrentRotation = rotation; + } + + /** + * Gets the proposed rotation. + * + * This method only returns a rotation if the orientation listener is certain + * of its proposal. If the rotation is indeterminate, returns -1. + * + * @return The proposed rotation, or -1 if unknown. */ - public int getCurrentRotation() { + public int getProposedRotation() { if (mEnabled) { - return mSensorEventListener.getCurrentRotation(); + return mSensorEventListener.getProposedRotation(); } return -1; } @@ -137,10 +151,14 @@ public abstract class WindowOrientationListener { /** * Called when the rotation view of the device has changed. * + * This method is called whenever the orientation becomes certain of an orientation. + * It is called each time the orientation determination transitions from being + * uncertain to being certain again, even if it is the same orientation as before. + * * @param rotation The new orientation of the device, one of the Surface.ROTATION_* constants. * @see Surface */ - public abstract void onOrientationChanged(int rotation); + public abstract void onProposedRotationChanged(int rotation); /** * Enables or disables the window orientation listener logging for use with @@ -182,23 +200,8 @@ public abstract class WindowOrientationListener { * to the corresponding orientation. These thresholds have some hysteresis built-in * to avoid oscillations between adjacent orientations. * - * - Use the magnitude to judge the confidence of the orientation. - * Under ideal conditions, the magnitude should equal to that of gravity. When it - * differs significantly, we know the device is under external acceleration and - * we can't trust the data. - * - * - Use the tilt angle to judge the confidence of the orientation. - * When the tilt angle is high in absolute value then the device is nearly flat - * so small physical movements produce large changes in orientation angle. - * This can be the case when the device is being picked up from a table. - * - * - Use the orientation angle to judge the confidence of the orientation. - * The close the orientation angle is to the canonical orientation angle, the better. - * - * - Based on the aggregate confidence, we determine how long we want to wait for - * the new orientation to settle. This is accomplished by integrating the confidence - * for each orientation over time. When a threshold integration sum is reached - * then we actually change orientations. + * - Wait for the device to settle for a little bit. Once that happens, issue the + * new orientation proposal. * * Details are explained inline. */ @@ -211,22 +214,8 @@ public abstract class WindowOrientationListener { private static final int ACCELEROMETER_DATA_Y = 1; private static final int ACCELEROMETER_DATA_Z = 2; - // Rotation constants. - // These are the same as Surface rotation constants with the addition of a 5th - // unknown state when we are not confident about the proporsed orientation. - // One important property of these constants is that they are equal to the - // orientation angle itself divided by 90. We use this fact to map - // back and forth between orientation angles and rotation values. - private static final int ROTATION_UNKNOWN = -1; - //private static final int ROTATION_0 = Surface.ROTATION_0; // 0 - //private static final int ROTATION_90 = Surface.ROTATION_90; // 1 - //private static final int ROTATION_180 = Surface.ROTATION_180; // 2 - //private static final int ROTATION_270 = Surface.ROTATION_270; // 3 - private final WindowOrientationListener mOrientationListener; - private int mRotation = ROTATION_UNKNOWN; - /* State for first order low-pass filtering of accelerometer data. * See http://en.wikipedia.org/wiki/Low-pass_filter#Discrete-time_realization for * signal processing background. @@ -235,6 +224,24 @@ public abstract class WindowOrientationListener { private long mLastTimestamp = Long.MAX_VALUE; // in nanoseconds private float mLastFilteredX, mLastFilteredY, mLastFilteredZ; + // The current proposal. We wait for the proposal to be stable for a + // certain amount of time before accepting it. + // + // The basic idea is to ignore intermediate poses of the device while the + // user is picking up, putting down or turning the device. + private int mProposalRotation; + private long mProposalAgeMS; + + // A historical trace of tilt and orientation angles. Used to determine whether + // the device posture has settled down. + private static final int HISTORY_SIZE = 20; + private int mHistoryIndex; // index of most recent sample + private int mHistoryLength; // length of historical trace + private final long[] mHistoryTimestampMS = new long[HISTORY_SIZE]; + private final float[] mHistoryMagnitudes = new float[HISTORY_SIZE]; + private final int[] mHistoryTiltAngles = new int[HISTORY_SIZE]; + private final int[] mHistoryOrientationAngles = new int[HISTORY_SIZE]; + // The maximum sample inter-arrival time in milliseconds. // If the acceleration samples are further apart than this amount in time, we reset the // state of the low-pass filter and orientation properties. This helps to handle @@ -242,24 +249,26 @@ public abstract class WindowOrientationListener { // a significant gap in samples. private static final float MAX_FILTER_DELTA_TIME_MS = 1000; - // The acceleration filter cutoff frequency. - // This is the frequency at which signals are attenuated by 3dB (half the passband power). + // The acceleration filter time constant. + // + // This time constant is used to tune the acceleration filter such that + // impulses and vibrational noise (think car dock) is suppressed before we + // try to calculate the tilt and orientation angles. + // + // The filter time constant is related to the filter cutoff frequency, which is the + // frequency at which signals are attenuated by 3dB (half the passband power). // Each successive octave beyond this frequency is attenuated by an additional 6dB. // - // We choose the cutoff frequency such that impulses and vibrational noise - // (think car dock) is suppressed. However, this filtering does not eliminate - // all possible sources of orientation ambiguity so we also rely on a dynamic - // settle time for establishing a new orientation. Filtering adds latency - // inversely proportional to the cutoff frequency so we don't want to make - // it too small or we can lose hundreds of milliseconds of responsiveness. - private static final float FILTER_CUTOFF_FREQUENCY_HZ = 1f; - private static final float FILTER_TIME_CONSTANT_MS = (float)(500.0f - / (Math.PI * FILTER_CUTOFF_FREQUENCY_HZ)); // t = 1 / (2pi * Fc) * 1000ms - - // The filter gain. - // We choose a value slightly less than unity to avoid numerical instabilities due - // to floating-point error accumulation. - private static final float FILTER_GAIN = 0.999f; + // Given a time constant t in seconds, the filter cutoff frequency Fc in Hertz + // is given by Fc = 1 / (2pi * t). + // + // The higher the time constant, the lower the cutoff frequency, so more noise + // will be suppressed. + // + // Filtering adds latency proportional the time constant (inversely proportional + // to the cutoff frequency) so we don't want to make the time constant too + // large or we can lose responsiveness. + private static final float FILTER_TIME_CONSTANT_MS = 100.0f; /* State for orientation detection. */ @@ -297,10 +306,10 @@ public abstract class WindowOrientationListener { // The ideal tilt angle is 0 (when the device is vertical) so the limits establish // how close to vertical the device must be in order to change orientation. private static final int[][] TILT_TOLERANCE = new int[][] { - /* ROTATION_0 */ { -20, 75 }, - /* ROTATION_90 */ { -20, 70 }, - /* ROTATION_180 */ { -20, 65 }, - /* ROTATION_270 */ { -20, 70 } + /* ROTATION_0 */ { -20, 70 }, + /* ROTATION_90 */ { -20, 60 }, + /* ROTATION_180 */ { -20, 50 }, + /* ROTATION_270 */ { -20, 60 } }; // The gap angle in degrees between adjacent orientation angles for hysteresis. @@ -308,41 +317,31 @@ public abstract class WindowOrientationListener { // adjacent orientation. No orientation proposal is made when the orientation // angle is within the gap between the current orientation and the adjacent // orientation. - private static final int ADJACENT_ORIENTATION_ANGLE_GAP = 30; - - // The confidence scale factors for angle, tilt and magnitude. - // When the distance between the actual value and the ideal value is the - // specified delta, orientation transitions will take twice as long as they would - // in the ideal case. Increasing or decreasing the delta has an exponential effect - // on each factor's influence over the transition time. + private static final int ADJACENT_ORIENTATION_ANGLE_GAP = 45; - // Transition takes 2x longer when angle is 30 degrees from ideal orientation angle. - private static final float ORIENTATION_ANGLE_CONFIDENCE_SCALE = - confidenceScaleFromDelta(30); + // The number of milliseconds for which the device posture must be stable + // before we perform an orientation change. If the device appears to be rotating + // (being picked up, put down) then we keep waiting until it settles. + private static final int SETTLE_TIME_MS = 200; - // Transition takes 2x longer when tilt is 60 degrees from vertical. - private static final float TILT_ANGLE_CONFIDENCE_SCALE = confidenceScaleFromDelta(60); + // The maximum change in magnitude that can occur during the settle time. + // Tuning this constant particularly helps to filter out situations where the + // device is being picked up or put down by the user. + private static final float SETTLE_MAGNITUDE_MAX_DELTA = + SensorManager.STANDARD_GRAVITY * 0.2f; - // Transition takes 2x longer when acceleration is 0.5 Gs. - private static final float MAGNITUDE_CONFIDENCE_SCALE = confidenceScaleFromDelta( - SensorManager.STANDARD_GRAVITY * 0.5f); + // The maximum change in tilt angle that can occur during the settle time. + private static final int SETTLE_TILT_ANGLE_MAX_DELTA = 5; - // The number of milliseconds for which a new orientation must be stable before - // we perform an orientation change under ideal conditions. It will take - // proportionally longer than this to effect an orientation change when - // the proposed orientation confidence is low. - private static final float ORIENTATION_SETTLE_TIME_MS = 250; - - // The confidence that we have abount effecting each orientation change. - // When one of these values exceeds 1.0, we have determined our new orientation! - private float mConfidence[] = new float[4]; + // The maximum change in orientation angle that can occur during the settle time. + private static final int SETTLE_ORIENTATION_ANGLE_MAX_DELTA = 5; public SensorEventListenerImpl(WindowOrientationListener orientationListener) { mOrientationListener = orientationListener; } - public int getCurrentRotation() { - return mRotation; // may be -1, if unknown + public int getProposedRotation() { + return mProposalAgeMS >= SETTLE_TIME_MS ? mProposalRotation : -1; } @Override @@ -368,20 +367,18 @@ public abstract class WindowOrientationListener { // Reset the orientation listener state if the samples are too far apart in time // or when we see values of (0, 0, 0) which indicates that we polled the // accelerometer too soon after turning it on and we don't have any data yet. - final float timeDeltaMS = (event.timestamp - mLastTimestamp) * 0.000001f; + final long now = event.timestamp; + final float timeDeltaMS = (now - mLastTimestamp) * 0.000001f; boolean skipSample; if (timeDeltaMS <= 0 || timeDeltaMS > MAX_FILTER_DELTA_TIME_MS || (x == 0 && y == 0 && z == 0)) { if (log) { Slog.v(TAG, "Resetting orientation listener."); } - for (int i = 0; i < 4; i++) { - mConfidence[i] = 0; - } + clearProposal(); skipSample = true; } else { - final float alpha = timeDeltaMS - / (FILTER_TIME_CONSTANT_MS + timeDeltaMS) * FILTER_GAIN; + final float alpha = timeDeltaMS / (FILTER_TIME_CONSTANT_MS + timeDeltaMS); x = alpha * (x - mLastFilteredX) + mLastFilteredX; y = alpha * (y - mLastFilteredY) + mLastFilteredY; z = alpha * (z - mLastFilteredZ) + mLastFilteredZ; @@ -391,17 +388,13 @@ public abstract class WindowOrientationListener { } skipSample = false; } - mLastTimestamp = event.timestamp; + mLastTimestamp = now; mLastFilteredX = x; mLastFilteredY = y; mLastFilteredZ = z; - boolean orientationChanged = false; + final int oldProposedRotation = getProposedRotation(); if (!skipSample) { - // Determine a proposed orientation based on the currently available data. - int proposedOrientation = ROTATION_UNKNOWN; - float combinedConfidence = 1.0f; - // Calculate the magnitude of the acceleration vector. final float magnitude = (float) Math.sqrt(x * x + y * y + z * z); if (magnitude < MIN_ACCELERATION_MAGNITUDE @@ -410,6 +403,7 @@ public abstract class WindowOrientationListener { Slog.v(TAG, "Ignoring sensor data, magnitude out of range: " + "magnitude=" + magnitude); } + clearProposal(); } else { // Calculate the tilt angle. // This is the angle between the up vector and the x-y plane (the plane of @@ -417,123 +411,82 @@ public abstract class WindowOrientationListener { // -90 degrees: screen horizontal and facing the ground (overhead) // 0 degrees: screen vertical // 90 degrees: screen horizontal and facing the sky (on table) - final int tiltAngle = (int) Math.round( - Math.asin(z / magnitude) * RADIANS_TO_DEGREES); - - // If the tilt angle is too close to horizontal then we cannot determine - // the orientation angle of the screen. - if (Math.abs(tiltAngle) > MAX_TILT) { - if (log) { - Slog.v(TAG, "Ignoring sensor data, tilt angle too high: " - + "magnitude=" + magnitude + ", tiltAngle=" + tiltAngle); - } - } else { - // Calculate the orientation angle. - // This is the angle between the x-y projection of the up vector onto - // the +y-axis, increasing clockwise in a range of [0, 360] degrees. - int orientationAngle = (int) Math.round( - -Math.atan2(-x, y) * RADIANS_TO_DEGREES); - if (orientationAngle < 0) { - // atan2 returns [-180, 180]; normalize to [0, 360] - orientationAngle += 360; - } - - // Find the nearest orientation. - // An orientation of 0 can have a nearest angle of 0 or 360 depending - // on which is closer to the measured orientation angle. We leave the - // nearest angle at 360 in that case since it makes the delta calculation - // for orientation angle confidence easier below. - int nearestOrientation = (orientationAngle + 45) / 90; - int nearestOrientationAngle = nearestOrientation * 90; - if (nearestOrientation == 4) { - nearestOrientation = 0; - } - - // Determine the proposed orientation. - // The confidence of the proposal is 1.0 when it is ideal and it - // decays exponentially as the proposal moves further from the ideal - // angle, tilt and magnitude of the proposed orientation. - if (isTiltAngleAcceptable(nearestOrientation, tiltAngle) - && isOrientationAngleAcceptable(nearestOrientation, - orientationAngle)) { - proposedOrientation = nearestOrientation; - - final float idealOrientationAngle = nearestOrientationAngle; - final float orientationConfidence = confidence(orientationAngle, - idealOrientationAngle, ORIENTATION_ANGLE_CONFIDENCE_SCALE); - - final float idealTiltAngle = 0; - final float tiltConfidence = confidence(tiltAngle, - idealTiltAngle, TILT_ANGLE_CONFIDENCE_SCALE); - - final float idealMagnitude = SensorManager.STANDARD_GRAVITY; - final float magnitudeConfidence = confidence(magnitude, - idealMagnitude, MAGNITUDE_CONFIDENCE_SCALE); - - combinedConfidence = orientationConfidence - * tiltConfidence * magnitudeConfidence; - - if (log) { - Slog.v(TAG, "Proposal: " - + "magnitude=" + magnitude - + ", tiltAngle=" + tiltAngle - + ", orientationAngle=" + orientationAngle - + ", proposedOrientation=" + proposedOrientation - + ", combinedConfidence=" + combinedConfidence - + ", orientationConfidence=" + orientationConfidence - + ", tiltConfidence=" + tiltConfidence - + ", magnitudeConfidence=" + magnitudeConfidence); - } - } else { - if (log) { - Slog.v(TAG, "Ignoring sensor data, no proposal: " - + "magnitude=" + magnitude + ", tiltAngle=" + tiltAngle - + ", orientationAngle=" + orientationAngle); - } - } - } - } - - // Sum up the orientation confidence weights. - // Detect an orientation change when the sum reaches 1.0. - final float confidenceAmount = combinedConfidence * timeDeltaMS - / ORIENTATION_SETTLE_TIME_MS; - for (int i = 0; i < 4; i++) { - if (i == proposedOrientation) { - mConfidence[i] += confidenceAmount; - if (mConfidence[i] >= 1.0f) { - mConfidence[i] = 1.0f; - - if (i != mRotation) { - if (log) { - Slog.v(TAG, "Orientation changed! rotation=" + i); - } - mRotation = i; - orientationChanged = true; - } + final int tiltAngle = (int) Math.round( + Math.asin(z / magnitude) * RADIANS_TO_DEGREES); + + // If the tilt angle is too close to horizontal then we cannot determine + // the orientation angle of the screen. + if (Math.abs(tiltAngle) > MAX_TILT) { + if (log) { + Slog.v(TAG, "Ignoring sensor data, tilt angle too high: " + + "magnitude=" + magnitude + ", tiltAngle=" + tiltAngle); } + clearProposal(); } else { - mConfidence[i] -= confidenceAmount; - if (mConfidence[i] < 0.0f) { - mConfidence[i] = 0.0f; + // Calculate the orientation angle. + // This is the angle between the x-y projection of the up vector onto + // the +y-axis, increasing clockwise in a range of [0, 360] degrees. + int orientationAngle = (int) Math.round( + -Math.atan2(-x, y) * RADIANS_TO_DEGREES); + if (orientationAngle < 0) { + // atan2 returns [-180, 180]; normalize to [0, 360] + orientationAngle += 360; + } + + // Find the nearest rotation. + int nearestRotation = (orientationAngle + 45) / 90; + if (nearestRotation == 4) { + nearestRotation = 0; + } + + // Determine the proposed orientation. + // The confidence of the proposal is 1.0 when it is ideal and it + // decays exponentially as the proposal moves further from the ideal + // angle, tilt and magnitude of the proposed orientation. + if (!isTiltAngleAcceptable(nearestRotation, tiltAngle) + || !isOrientationAngleAcceptable(nearestRotation, + orientationAngle)) { + if (log) { + Slog.v(TAG, "Ignoring sensor data, no proposal: " + + "magnitude=" + magnitude + ", tiltAngle=" + tiltAngle + + ", orientationAngle=" + orientationAngle); + } + clearProposal(); + } else { + if (log) { + Slog.v(TAG, "Proposal: " + + "magnitude=" + magnitude + + ", tiltAngle=" + tiltAngle + + ", orientationAngle=" + orientationAngle + + ", proposalRotation=" + mProposalRotation); + } + updateProposal(nearestRotation, now / 1000000L, + magnitude, tiltAngle, orientationAngle); } } } } // Write final statistics about where we are in the orientation detection process. + final int proposedRotation = getProposedRotation(); if (log) { - Slog.v(TAG, "Result: rotation=" + mRotation - + ", confidence=[" - + mConfidence[0] + ", " - + mConfidence[1] + ", " - + mConfidence[2] + ", " - + mConfidence[3] + "], timeDeltaMS=" + timeDeltaMS); + final float proposalConfidence = Math.min( + mProposalAgeMS * 1.0f / SETTLE_TIME_MS, 1.0f); + Slog.v(TAG, "Result: currentRotation=" + mOrientationListener.mCurrentRotation + + ", proposedRotation=" + proposedRotation + + ", timeDeltaMS=" + timeDeltaMS + + ", proposalRotation=" + mProposalRotation + + ", proposalAgeMS=" + mProposalAgeMS + + ", proposalConfidence=" + proposalConfidence); } // Tell the listener. - if (orientationChanged) { - mOrientationListener.onOrientationChanged(mRotation); + if (proposedRotation != oldProposedRotation && proposedRotation >= 0) { + if (log) { + Slog.v(TAG, "Proposed rotation changed! proposedRotation=" + proposedRotation + + ", oldProposedRotation=" + oldProposedRotation); + } + mOrientationListener.onProposedRotationChanged(proposedRotation); } } @@ -541,33 +494,34 @@ public abstract class WindowOrientationListener { * Returns true if the tilt angle is acceptable for a proposed * orientation transition. */ - private boolean isTiltAngleAcceptable(int proposedOrientation, + private boolean isTiltAngleAcceptable(int proposedRotation, int tiltAngle) { - return tiltAngle >= TILT_TOLERANCE[proposedOrientation][0] - && tiltAngle <= TILT_TOLERANCE[proposedOrientation][1]; + return tiltAngle >= TILT_TOLERANCE[proposedRotation][0] + && tiltAngle <= TILT_TOLERANCE[proposedRotation][1]; } /** * Returns true if the orientation angle is acceptable for a proposed * orientation transition. + * * This function takes into account the gap between adjacent orientations * for hysteresis. */ - private boolean isOrientationAngleAcceptable(int proposedOrientation, - int orientationAngle) { - final int currentOrientation = mRotation; - + private boolean isOrientationAngleAcceptable(int proposedRotation, int orientationAngle) { // If there is no current rotation, then there is no gap. - if (currentOrientation != ROTATION_UNKNOWN) { - // If the proposed orientation is the same or is counter-clockwise adjacent, + // The gap is used only to introduce hysteresis among advertised orientation + // changes to avoid flapping. + final int currentRotation = mOrientationListener.mCurrentRotation; + if (currentRotation >= 0) { + // If the proposed rotation is the same or is counter-clockwise adjacent, // then we set a lower bound on the orientation angle. - // For example, if currentOrientation is ROTATION_0 and proposed is ROTATION_90, + // For example, if currentRotation is ROTATION_0 and proposed is ROTATION_90, // then we want to check orientationAngle > 45 + GAP / 2. - if (proposedOrientation == currentOrientation - || proposedOrientation == (currentOrientation + 1) % 4) { - int lowerBound = proposedOrientation * 90 - 45 + if (proposedRotation == currentRotation + || proposedRotation == (currentRotation + 1) % 4) { + int lowerBound = proposedRotation * 90 - 45 + ADJACENT_ORIENTATION_ANGLE_GAP / 2; - if (proposedOrientation == 0) { + if (proposedRotation == 0) { if (orientationAngle >= 315 && orientationAngle < lowerBound + 360) { return false; } @@ -578,15 +532,15 @@ public abstract class WindowOrientationListener { } } - // If the proposed orientation is the same or is clockwise adjacent, + // If the proposed rotation is the same or is clockwise adjacent, // then we set an upper bound on the orientation angle. - // For example, if currentOrientation is ROTATION_0 and proposed is ROTATION_270, + // For example, if currentRotation is ROTATION_0 and proposed is ROTATION_270, // then we want to check orientationAngle < 315 - GAP / 2. - if (proposedOrientation == currentOrientation - || proposedOrientation == (currentOrientation + 3) % 4) { - int upperBound = proposedOrientation * 90 + 45 + if (proposedRotation == currentRotation + || proposedRotation == (currentRotation + 3) % 4) { + int upperBound = proposedRotation * 90 + 45 - ADJACENT_ORIENTATION_ANGLE_GAP / 2; - if (proposedOrientation == 0) { + if (proposedRotation == 0) { if (orientationAngle <= 45 && orientationAngle > upperBound) { return false; } @@ -600,21 +554,58 @@ public abstract class WindowOrientationListener { return true; } - /** - * Calculate an exponentially weighted confidence value in the range [0.0, 1.0]. - * The further the value is from the target, the more the confidence trends to 0. - */ - private static float confidence(float value, float target, float scale) { - return (float) Math.exp(-Math.abs(value - target) * scale); + private void clearProposal() { + mProposalRotation = -1; + mProposalAgeMS = 0; } - /** - * Calculate a scale factor for the confidence weight exponent. - * The scale value is chosen such that confidence(value, target, scale) == 0.5 - * whenever abs(value - target) == cutoffDelta. - */ - private static float confidenceScaleFromDelta(float cutoffDelta) { - return (float) -Math.log(0.5) / cutoffDelta; + private void updateProposal(int rotation, long timestampMS, + float magnitude, int tiltAngle, int orientationAngle) { + if (mProposalRotation != rotation) { + mProposalRotation = rotation; + mHistoryIndex = 0; + mHistoryLength = 0; + } + + final int index = mHistoryIndex; + mHistoryTimestampMS[index] = timestampMS; + mHistoryMagnitudes[index] = magnitude; + mHistoryTiltAngles[index] = tiltAngle; + mHistoryOrientationAngles[index] = orientationAngle; + mHistoryIndex = (index + 1) % HISTORY_SIZE; + if (mHistoryLength < HISTORY_SIZE) { + mHistoryLength += 1; + } + + long age = 0; + for (int i = 1; i < mHistoryLength; i++) { + final int olderIndex = (index + HISTORY_SIZE - i) % HISTORY_SIZE; + if (Math.abs(mHistoryMagnitudes[olderIndex] - magnitude) + > SETTLE_MAGNITUDE_MAX_DELTA) { + break; + } + if (angleAbsoluteDelta(mHistoryTiltAngles[olderIndex], + tiltAngle) > SETTLE_TILT_ANGLE_MAX_DELTA) { + break; + } + if (angleAbsoluteDelta(mHistoryOrientationAngles[olderIndex], + orientationAngle) > SETTLE_ORIENTATION_ANGLE_MAX_DELTA) { + break; + } + age = timestampMS - mHistoryTimestampMS[olderIndex]; + if (age >= SETTLE_TIME_MS) { + break; + } + } + mProposalAgeMS = age; + } + + private static int angleAbsoluteDelta(int a, int b) { + int delta = Math.abs(a - b); + if (delta > 180) { + delta = 360 - delta; + } + return delta; } } } diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index cb6922d..139f9f3 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -1808,6 +1808,8 @@ public class WebView extends AbsoluteLayout /** * Restore the display data that was save in {@link #savePicture}. Used in * conjunction with {@link #restoreState}. + * + * Note that this will not work if the WebView is hardware accelerated. * @param b A Bundle containing the saved display data. * @param src The file where the picture data was stored. * @return True if the picture was successfully restored. @@ -4300,6 +4302,7 @@ public class WebView extends AbsoluteLayout selectionDone(); } mOrientation = newConfig.orientation; + contentInvalidateAll(); } /** diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index ba89ef3..7b8c7f2 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -1304,16 +1304,6 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } } - @Override - public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { - // Do not append text content to scroll events they are fired frequently - // and the client has already received another event type with the text. - if (event.getEventType() != AccessibilityEvent.TYPE_VIEW_SCROLLED) { - super.dispatchPopulateAccessibilityEvent(event); - } - return false; - } - /** * Indicates whether the children's drawing cache is used during a scroll. * By default, the drawing cache is enabled but this will consume more memory. diff --git a/core/java/android/widget/AdapterView.java b/core/java/android/widget/AdapterView.java index 5392c2e..a4b4e78 100644 --- a/core/java/android/widget/AdapterView.java +++ b/core/java/android/widget/AdapterView.java @@ -881,31 +881,30 @@ public abstract class AdapterView<T extends Adapter> extends ViewGroup { @Override public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { - // This is an exceptional case which occurs when a window gets the - // focus and sends a focus event via its focused child to announce - // current focus/selection. AdapterView fires selection but not focus - // events so we change the event type here. - if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_FOCUSED) { - event.setEventType(AccessibilityEvent.TYPE_VIEW_SELECTED); + final int eventType = event.getEventType(); + switch (eventType) { + case AccessibilityEvent.TYPE_VIEW_SCROLLED: + // Do not populate the text of scroll events. + return true; + case AccessibilityEvent.TYPE_VIEW_FOCUSED: + // This is an exceptional case which occurs when a window gets the + // focus and sends a focus event via its focused child to announce + // current focus/selection. AdapterView fires selection but not focus + // events so we change the event type here. + if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_FOCUSED) { + event.setEventType(AccessibilityEvent.TYPE_VIEW_SELECTED); + } + break; } View selectedView = getSelectedView(); if (selectedView != null && selectedView.getVisibility() == VISIBLE) { - // We first get a chance to populate the event. - onPopulateAccessibilityEvent(event); + getSelectedView().dispatchPopulateAccessibilityEvent(event); } return false; } @Override - public void onPopulateAccessibilityEvent(AccessibilityEvent event) { - super.onPopulateAccessibilityEvent(event); - // We send selection events only from AdapterView to avoid - // generation of such event for each child. - getSelectedView().dispatchPopulateAccessibilityEvent(event); - } - - @Override public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) { if (super.onRequestSendAccessibilityEvent(child, event)) { // Add a record for ourselves as well. diff --git a/core/java/android/widget/SearchView.java b/core/java/android/widget/SearchView.java index f230031..adf2b7b 100644 --- a/core/java/android/widget/SearchView.java +++ b/core/java/android/widget/SearchView.java @@ -120,6 +120,7 @@ public class SearchView extends LinearLayout implements CollapsibleActionView { private CharSequence mOldQueryText; private CharSequence mUserQuery; private boolean mExpandedInActionView; + private int mCollapsedImeOptions; private SearchableInfo mSearchable; private Bundle mAppSearchData; @@ -1166,6 +1167,7 @@ public class SearchView extends LinearLayout implements CollapsibleActionView { clearFocus(); updateViewsVisibility(true); mQueryTextView.setText(""); + mQueryTextView.setImeOptions(mCollapsedImeOptions); mExpandedInActionView = false; } @@ -1175,6 +1177,8 @@ public class SearchView extends LinearLayout implements CollapsibleActionView { @Override public void onActionViewExpanded() { mExpandedInActionView = true; + mCollapsedImeOptions = mQueryTextView.getImeOptions(); + mQueryTextView.setImeOptions(mCollapsedImeOptions | EditorInfo.IME_FLAG_NO_FULLSCREEN); setIconified(false); } diff --git a/core/java/com/android/internal/view/menu/ActionMenuPresenter.java b/core/java/com/android/internal/view/menu/ActionMenuPresenter.java index 0db1ccc..aabea2c 100644 --- a/core/java/com/android/internal/view/menu/ActionMenuPresenter.java +++ b/core/java/com/android/internal/view/menu/ActionMenuPresenter.java @@ -200,8 +200,19 @@ public class ActionMenuPresenter extends BaseMenuPresenter } } - final boolean hasOverflow = mReserveOverflow && mMenu != null && - mMenu.getNonActionItems().size() > 0; + final ArrayList<MenuItemImpl> nonActionItems = mMenu != null ? + mMenu.getNonActionItems() : null; + + boolean hasOverflow = false; + if (mReserveOverflow && nonActionItems != null) { + final int count = nonActionItems.size(); + if (count == 1) { + hasOverflow = !nonActionItems.get(0).isActionViewExpanded(); + } else { + hasOverflow = count > 0; + } + } + if (hasOverflow) { if (mOverflowButton == null) { mOverflowButton = new OverflowMenuButton(mContext); diff --git a/core/java/com/android/internal/view/menu/ListMenuPresenter.java b/core/java/com/android/internal/view/menu/ListMenuPresenter.java index 0141427..a331bec 100644 --- a/core/java/com/android/internal/view/menu/ListMenuPresenter.java +++ b/core/java/com/android/internal/view/menu/ListMenuPresenter.java @@ -17,6 +17,7 @@ package com.android.internal.view.menu; import android.content.Context; +import android.database.DataSetObserver; import android.os.Bundle; import android.os.Parcelable; import android.util.SparseArray; @@ -47,7 +48,7 @@ public class ListMenuPresenter implements MenuPresenter, AdapterView.OnItemClick int mItemLayoutRes; private Callback mCallback; - private MenuAdapter mAdapter; + MenuAdapter mAdapter; private int mId; @@ -216,14 +217,29 @@ public class ListMenuPresenter implements MenuPresenter, AdapterView.OnItemClick } private class MenuAdapter extends BaseAdapter { + private int mExpandedIndex = -1; + + public MenuAdapter() { + registerDataSetObserver(new ExpandedIndexObserver()); + findExpandedIndex(); + } + public int getCount() { ArrayList<MenuItemImpl> items = mMenu.getNonActionItems(); - return items.size() - mItemIndexOffset; + int count = items.size() - mItemIndexOffset; + if (mExpandedIndex < 0) { + return count; + } + return count - 1; } public MenuItemImpl getItem(int position) { ArrayList<MenuItemImpl> items = mMenu.getNonActionItems(); - return items.get(position + mItemIndexOffset); + position += mItemIndexOffset; + if (mExpandedIndex >= 0 && position >= mExpandedIndex) { + position++; + } + return items.get(position); } public long getItemId(int position) { @@ -241,5 +257,28 @@ public class ListMenuPresenter implements MenuPresenter, AdapterView.OnItemClick itemView.initialize(getItem(position), 0); return convertView; } + + void findExpandedIndex() { + final MenuItemImpl expandedItem = mMenu.getExpandedItem(); + if (expandedItem != null) { + final ArrayList<MenuItemImpl> items = mMenu.getNonActionItems(); + final int count = items.size(); + for (int i = 0; i < count; i++) { + final MenuItemImpl item = items.get(i); + if (item == expandedItem) { + mExpandedIndex = i; + return; + } + } + } + mExpandedIndex = -1; + } + } + + private class ExpandedIndexObserver extends DataSetObserver { + @Override + public void onChanged() { + mAdapter.findExpandedIndex(); + } } } diff --git a/core/java/com/android/internal/view/menu/MenuBuilder.java b/core/java/com/android/internal/view/menu/MenuBuilder.java index c30e83b..9fbca82 100644 --- a/core/java/com/android/internal/view/menu/MenuBuilder.java +++ b/core/java/com/android/internal/view/menu/MenuBuilder.java @@ -1258,4 +1258,8 @@ public class MenuBuilder implements Menu { } return collapsed; } + + public MenuItemImpl getExpandedItem() { + return mExpandedItem; + } } diff --git a/core/java/com/android/internal/view/menu/MenuPopupHelper.java b/core/java/com/android/internal/view/menu/MenuPopupHelper.java index 6265618..329b457 100644 --- a/core/java/com/android/internal/view/menu/MenuPopupHelper.java +++ b/core/java/com/android/internal/view/menu/MenuPopupHelper.java @@ -18,6 +18,7 @@ package com.android.internal.view.menu; import android.content.Context; import android.content.res.Resources; +import android.database.DataSetObserver; import android.os.Parcelable; import android.view.KeyEvent; import android.view.LayoutInflater; @@ -286,22 +287,45 @@ public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.On return false; } + @Override + public int getId() { + return 0; + } + + @Override + public Parcelable onSaveInstanceState() { + return null; + } + + @Override + public void onRestoreInstanceState(Parcelable state) { + } + private class MenuAdapter extends BaseAdapter { private MenuBuilder mAdapterMenu; + private int mExpandedIndex = -1; public MenuAdapter(MenuBuilder menu) { mAdapterMenu = menu; + registerDataSetObserver(new ExpandedIndexObserver()); + findExpandedIndex(); } public int getCount() { ArrayList<MenuItemImpl> items = mOverflowOnly ? mAdapterMenu.getNonActionItems() : mAdapterMenu.getVisibleItems(); - return items.size(); + if (mExpandedIndex < 0) { + return items.size(); + } + return items.size() - 1; } public MenuItemImpl getItem(int position) { ArrayList<MenuItemImpl> items = mOverflowOnly ? mAdapterMenu.getNonActionItems() : mAdapterMenu.getVisibleItems(); + if (mExpandedIndex >= 0 && position >= mExpandedIndex) { + position++; + } return items.get(position); } @@ -323,19 +347,28 @@ public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.On itemView.initialize(getItem(position), 0); return convertView; } - } - - @Override - public int getId() { - return 0; - } - @Override - public Parcelable onSaveInstanceState() { - return null; + void findExpandedIndex() { + final MenuItemImpl expandedItem = mMenu.getExpandedItem(); + if (expandedItem != null) { + final ArrayList<MenuItemImpl> items = mMenu.getNonActionItems(); + final int count = items.size(); + for (int i = 0; i < count; i++) { + final MenuItemImpl item = items.get(i); + if (item == expandedItem) { + mExpandedIndex = i; + return; + } + } + } + mExpandedIndex = -1; + } } - @Override - public void onRestoreInstanceState(Parcelable state) { + private class ExpandedIndexObserver extends DataSetObserver { + @Override + public void onChanged() { + mAdapter.findExpandedIndex(); + } } } diff --git a/core/java/com/android/internal/widget/AbsActionBarView.java b/core/java/com/android/internal/widget/AbsActionBarView.java index 6c11288..06f5158 100644 --- a/core/java/com/android/internal/widget/AbsActionBarView.java +++ b/core/java/com/android/internal/widget/AbsActionBarView.java @@ -35,6 +35,8 @@ public abstract class AbsActionBarView extends ViewGroup { protected ActionMenuView mMenuView; protected ActionMenuPresenter mActionMenuPresenter; protected ActionBarContainer mSplitView; + protected boolean mSplitActionBar; + protected boolean mSplitWhenNarrow; protected int mContentHeight; protected Animator mVisibilityAnim; @@ -66,11 +68,31 @@ public abstract class AbsActionBarView extends ViewGroup { com.android.internal.R.attr.actionBarStyle, 0); setContentHeight(a.getLayoutDimension(R.styleable.ActionBar_height, 0)); a.recycle(); + if (mSplitWhenNarrow) { + setSplitActionBar(getContext().getResources().getBoolean( + com.android.internal.R.bool.split_action_bar_is_narrow)); + } if (mActionMenuPresenter != null) { mActionMenuPresenter.onConfigurationChanged(newConfig); } } + /** + * Sets whether the bar should be split right now, no questions asked. + * @param split true if the bar should split + */ + public void setSplitActionBar(boolean split) { + mSplitActionBar = split; + } + + /** + * Sets whether the bar should split if we enter a narrow screen configuration. + * @param splitWhenNarrow true if the bar should check to split after a config change + */ + public void setSplitWhenNarrow(boolean splitWhenNarrow) { + mSplitWhenNarrow = splitWhenNarrow; + } + public void setContentHeight(int height) { mContentHeight = height; requestLayout(); diff --git a/core/java/com/android/internal/widget/ActionBarContextView.java b/core/java/com/android/internal/widget/ActionBarContextView.java index 7bc33c7..acffa5c 100644 --- a/core/java/com/android/internal/widget/ActionBarContextView.java +++ b/core/java/com/android/internal/widget/ActionBarContextView.java @@ -93,6 +93,39 @@ public class ActionBarContextView extends AbsActionBarView implements AnimatorLi a.recycle(); } + @Override + public void setSplitActionBar(boolean split) { + if (mSplitActionBar != split) { + if (mActionMenuPresenter != null) { + // Mode is already active; move everything over and adjust the menu itself. + final LayoutParams layoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT, + LayoutParams.MATCH_PARENT); + if (!split) { + mMenuView = (ActionMenuView) mActionMenuPresenter.getMenuView(this); + mMenuView.setBackgroundDrawable(null); + final ViewGroup oldParent = (ViewGroup) mMenuView.getParent(); + if (oldParent != null) oldParent.removeView(mMenuView); + addView(mMenuView, layoutParams); + } else { + // Allow full screen width in split mode. + mActionMenuPresenter.setWidthLimit( + getContext().getResources().getDisplayMetrics().widthPixels, true); + // No limit to the item count; use whatever will fit. + mActionMenuPresenter.setItemLimit(Integer.MAX_VALUE); + // Span the whole width + layoutParams.width = LayoutParams.MATCH_PARENT; + layoutParams.height = mContentHeight; + mMenuView = (ActionMenuView) mActionMenuPresenter.getMenuView(this); + mMenuView.setBackgroundDrawable(mSplitBackground); + final ViewGroup oldParent = (ViewGroup) mMenuView.getParent(); + if (oldParent != null) oldParent.removeView(mMenuView); + mSplitView.addView(mMenuView, layoutParams); + } + } + super.setSplitActionBar(split); + } + } + public void setContentHeight(int height) { mContentHeight = height; } @@ -179,7 +212,7 @@ public class ActionBarContextView extends AbsActionBarView implements AnimatorLi final LayoutParams layoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT); - if (mSplitView == null) { + if (!mSplitActionBar) { menu.addMenuPresenter(mActionMenuPresenter); mMenuView = (ActionMenuView) mActionMenuPresenter.getMenuView(this); mMenuView.setBackgroundDrawable(null); diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java index bbecb6c..dc8feea 100644 --- a/core/java/com/android/internal/widget/ActionBarView.java +++ b/core/java/com/android/internal/widget/ActionBarView.java @@ -113,7 +113,6 @@ public class ActionBarView extends AbsActionBarView { private int mProgressStyle; private int mIndeterminateProgressStyle; - private boolean mSplitActionBar; private boolean mUserTitle; private boolean mIncludeTabs; private boolean mIsCollapsable; @@ -301,6 +300,7 @@ public class ActionBarView extends AbsActionBarView { addView(mIndeterminateProgressView); } + @Override public void setSplitActionBar(boolean splitActionBar) { if (mSplitActionBar != splitActionBar) { if (mMenuView != null) { @@ -316,7 +316,10 @@ public class ActionBarView extends AbsActionBarView { addView(mMenuView); } } - mSplitActionBar = splitActionBar; + if (mSplitView != null) { + mSplitView.setVisibility(splitActionBar ? VISIBLE : GONE); + } + super.setSplitActionBar(splitActionBar); } } diff --git a/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java b/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java index c03f91c..ebd355a 100644 --- a/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java +++ b/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java @@ -114,6 +114,13 @@ public class MultiWaveView extends View { } }; + private AnimatorListener mResetListenerWithPing = new AnimatorListenerAdapter() { + public void onAnimationEnd(Animator animator) { + ping(); + switchToState(STATE_IDLE, mWaveCenterX, mWaveCenterY); + } + }; + private AnimatorUpdateListener mUpdateListener = new AnimatorUpdateListener() { public void onAnimationUpdate(ValueAnimator animation) { invalidateGlobalRegion(mHandleDrawable); @@ -421,7 +428,7 @@ public class MultiWaveView extends View { "x", mWaveCenterX, "y", mWaveCenterY, "onUpdate", mUpdateListener, - "onComplete", mResetListener); + "onComplete", mDragging ? mResetListenerWithPing : mResetListener); } setGrabbedState(OnTriggerListener.NO_HANDLE); |
