Merge "Remove the suggestion underline when the TextView loses focus."
diff --git a/api/current.txt b/api/current.txt
index 52b28f5..f380cf9 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -16775,7 +16775,7 @@
     field public static final android.net.Uri CONTENT_URI;
   }
 
-  public final class LiveFolders implements android.provider.BaseColumns {
+  public final deprecated class LiveFolders implements android.provider.BaseColumns {
     field public static final java.lang.String ACTION_CREATE_LIVE_FOLDER = "android.intent.action.CREATE_LIVE_FOLDER";
     field public static final java.lang.String DESCRIPTION = "description";
     field public static final int DISPLAY_MODE_GRID = 1; // 0x1
diff --git a/core/java/android/nfc/NfcActivityManager.java b/core/java/android/nfc/NfcActivityManager.java
index 3cc6820..da878d4 100644
--- a/core/java/android/nfc/NfcActivityManager.java
+++ b/core/java/android/nfc/NfcActivityManager.java
@@ -20,7 +20,7 @@
 import android.os.RemoteException;
 import android.util.Log;
 
-import java.util.HashMap;
+import java.util.WeakHashMap;
 
 /**
  * Manages NFC API's that are coupled to the life-cycle of an Activity.
@@ -38,7 +38,7 @@
     static final Boolean DBG = false;
 
     final NfcAdapter mAdapter;
-    final HashMap<Activity, NfcActivityState> mNfcState;  // contents protected by this
+    final WeakHashMap<Activity, NfcActivityState> mNfcState;  // contents protected by this
     final NfcEvent mDefaultEvent;  // can re-use one NfcEvent because it just contains adapter
 
     /**
@@ -60,7 +60,7 @@
 
     public NfcActivityManager(NfcAdapter adapter) {
         mAdapter = adapter;
-        mNfcState = new HashMap<Activity, NfcActivityState>();
+        mNfcState = new WeakHashMap<Activity, NfcActivityState>();
         mDefaultEvent = new NfcEvent(mAdapter);
     }
 
@@ -88,6 +88,13 @@
         }
     }
 
+    /**
+     * onDestroy hook from fragment attached to activity
+     */
+    public void onDestroy(Activity activity) {
+        mNfcState.remove(activity);
+    }
+
     public synchronized void setNdefPushMessage(Activity activity, NdefMessage message) {
         NfcActivityState state = getOrCreateState(activity, message != null);
         if (state == null || state.ndefMessage == message) {
@@ -214,4 +221,5 @@
             callback.onNdefPushComplete(mDefaultEvent);
         }
     }
+
 }
diff --git a/core/java/android/nfc/NfcFragment.java b/core/java/android/nfc/NfcFragment.java
index c48859b..17278dc 100644
--- a/core/java/android/nfc/NfcFragment.java
+++ b/core/java/android/nfc/NfcFragment.java
@@ -80,4 +80,14 @@
             sNfcActivityManager.onPause(getActivity());
         }
     }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        if (sNfcActivityManager != null) {
+            sNfcActivityManager.onDestroy(getActivity());
+        }
+    }
+
+
 }
diff --git a/core/java/android/provider/LiveFolders.java b/core/java/android/provider/LiveFolders.java
index 7856bab..cf8ad46 100644
--- a/core/java/android/provider/LiveFolders.java
+++ b/core/java/android/provider/LiveFolders.java
@@ -162,7 +162,17 @@
  *     </tr>
  *     </tbody>
  * </table>
+ * 
+ * @deprecated Live folders are no longer supported by Android.  These have been
+ * replaced by the new
+ * <a href="{@docRoot}guide/topics/appwidgets/index.html#collections">AppWidget Collection</a>
+ * APIs introduced in {@link android.os.Build.VERSION_CODES#HONEYCOMB}.  These provide
+ * all of the features of live folders plus many more.  The use of live folders is greatly
+ * discouraged because of security issues they introduce -- publishing a live folder requires
+ * making all data show for the live folder available to all applications with no
+ * permissions protecting it.
  */
+@Deprecated
 public final class LiveFolders implements BaseColumns {
     /**
      * <p>Content provider column.</p>
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index efdb79e..bc5994e 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -42,6 +42,7 @@
 import android.text.TextUtils;
 import android.util.AndroidException;
 import android.util.Log;
+import android.view.WindowOrientationListener;
 
 import java.net.URISyntaxException;
 import java.util.HashMap;
@@ -59,7 +60,7 @@
      * <p>
      * Input: Nothing.
      * <p>
-     * Output: nothing.
+     * Output: Nothing.
      */
     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
     public static final String ACTION_SETTINGS = "android.settings.SETTINGS";
@@ -69,7 +70,7 @@
      * <p>
      * Input: Nothing.
      * <p>
-     * Output: nothing.
+     * Output: Nothing.
      */
     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
     public static final String ACTION_APN_SETTINGS = "android.settings.APN_SETTINGS";
@@ -328,6 +329,23 @@
             "android.settings.USER_DICTIONARY_SETTINGS";
 
     /**
+     * Activity Action: Adds a word to the user dictionary.
+     * <p>
+     * In some cases, a matching Activity may not exist, so ensure you
+     * safeguard against this.
+     * <p>
+     * Input: An extra with key <code>word</code> that contains the word
+     * that should be added to the dictionary.
+     * <p>
+     * Output: Nothing.
+     *
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_USER_DICTIONARY_INSERT =
+            "com.android.settings.USER_DICTIONARY_INSERT";
+
+    /**
      * Activity Action: Show settings to allow configuration of application-related settings.
      * <p>
      * In some cases, a matching Activity may not exist, so ensure you
@@ -1462,6 +1480,7 @@
          * @hide
          * @deprecated
          */
+        @Deprecated
         public static final String NOTIFICATIONS_USE_RING_VOLUME =
             "notifications_use_ring_volume";
 
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index 355d467..894afbd 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -750,7 +750,7 @@
                 case SUGGESTION_RANGE_SPAN:
                     readSpan(p, sp, new SuggestionRangeSpan(p));
                     break;
-                    
+
                 case EASY_EDIT_SPAN:
                     readSpan(p, sp, new EasyEditSpan());
                     break;
diff --git a/core/java/android/text/method/ArrowKeyMovementMethod.java b/core/java/android/text/method/ArrowKeyMovementMethod.java
index fe96565..b8728ee 100644
--- a/core/java/android/text/method/ArrowKeyMovementMethod.java
+++ b/core/java/android/text/method/ArrowKeyMovementMethod.java
@@ -232,8 +232,7 @@
 
         if (widget.isFocused() && !widget.didTouchFocusSelect()) {
             if (action == MotionEvent.ACTION_DOWN) {
-              boolean cap = isSelecting(buffer);
-              if (cap) {
+              if (isSelecting(buffer)) {
                   int offset = widget.getOffsetForPosition(event.getX(), event.getY());
 
                   buffer.setSpan(LAST_TAP_DOWN, offset, offset, Spannable.SPAN_POINT_POINT);
@@ -245,9 +244,7 @@
                   widget.getParent().requestDisallowInterceptTouchEvent(true);
               }
             } else if (action == MotionEvent.ACTION_MOVE) {
-                boolean cap = isSelecting(buffer);
-
-                if (cap && handled) {
+                if (isSelecting(buffer) && handled) {
                     // Before selecting, make sure we've moved out of the "slop".
                     // handled will be true, if we're in select mode AND we're
                     // OUT of the slop
@@ -279,7 +276,7 @@
                 if (isSelecting(buffer)) {
                     buffer.removeSpan(LAST_TAP_DOWN);
                     Selection.extendSelection(buffer, offset);
-                } else {
+                } else if (!widget.shouldIgnoreActionUpEvent()) {
                     Selection.setSelection(buffer, offset);
                 }
 
diff --git a/core/java/android/text/method/Touch.java b/core/java/android/text/method/Touch.java
index a528044..3f9b945 100644
--- a/core/java/android/text/method/Touch.java
+++ b/core/java/android/text/method/Touch.java
@@ -147,12 +147,10 @@
                     int nx = widget.getScrollX() + (int) dx;
                     int ny = widget.getScrollY() + (int) dy;
 
-                    int padding = widget.getTotalPaddingTop() +
-                                  widget.getTotalPaddingBottom();
+                    int padding = widget.getTotalPaddingTop() + widget.getTotalPaddingBottom();
                     Layout layout = widget.getLayout();
 
-                    ny = Math.min(ny, layout.getHeight() - (widget.getHeight() -
-                                                            padding));
+                    ny = Math.min(ny, layout.getHeight() - (widget.getHeight() - padding));
                     ny = Math.max(ny, 0);
         
                     int oldX = widget.getScrollX();
@@ -161,8 +159,7 @@
                     scrollTo(widget, layout, nx, ny);
 
                     // If we actually scrolled, then cancel the up action.
-                    if (oldX != widget.getScrollX()
-                            || oldY != widget.getScrollY()) {
+                    if (oldX != widget.getScrollX() || oldY != widget.getScrollY()) {
                         widget.cancelLongPress();
                     }
 
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 2e7f923..64fbae6f 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -2346,6 +2346,14 @@
     }
 
     /**
+     * Return the reading level scale of the WebView
+     * @return The reading level scale.
+     */
+    /*package*/ float getReadingLevelScale() {
+        return mZoomManager.getReadingLevelScale();
+    }
+
+    /**
      * Set the initial scale for the WebView. 0 means default. If
      * {@link WebSettings#getUseWideViewPort()} is true, it zooms out all the
      * way. Otherwise it starts with 100%. If initial scale is greater than 0,
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index c895b84..c61bd48 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -2460,7 +2460,7 @@
                     if (mSettings.isNarrowColumnLayout()) {
                         // In case of automatic text reflow in fixed view port mode.
                         mInitialViewState.mTextWrapScale =
-                                ZoomManager.computeReadingLevelScale(data.mScale);
+                                mWebView.getReadingLevelScale();
                     }
                 } else {
                     // Scale is given such as when page is restored, use it.
diff --git a/core/java/android/webkit/ZoomManager.java b/core/java/android/webkit/ZoomManager.java
index 2bcb020..0bfb668 100644
--- a/core/java/android/webkit/ZoomManager.java
+++ b/core/java/android/webkit/ZoomManager.java
@@ -58,13 +58,6 @@
     private ZoomControlExternal mExternalZoomControl;
 
     /*
-     * For large screen devices, the defaultScale usually set to 1.0 and
-     * equal to the overview scale, to differentiate the zoom level for double tapping,
-     * a default reading level scale is used.
-     */
-    private static final float DEFAULT_READING_LEVEL_SCALE = 1.5f;
-
-    /*
      * The scale factors that determine the upper and lower bounds for the
      * default zoom scale.
      */
@@ -151,6 +144,19 @@
     private float mDefaultScale;
     private float mInvDefaultScale;
 
+    /*
+     * The scale factor that is used to determine the zoom level for reading text.
+     * The value is initially set to equal the display density.
+     * TODO: Support changing this in WebSettings
+     */
+    private float mReadingLevelScale;
+
+    /*
+     * The scale factor that is used as the minimum increment when going from
+     * overview to reading level on a double tap.
+     */
+    private static float MIN_DOUBLE_TAP_SCALE_INCREMENT = 0.5f;
+
     // the current computed zoom scale and its inverse.
     private float mActualScale;
     private float mInvActualScale;
@@ -230,6 +236,7 @@
         setDefaultZoomScale(density);
         mActualScale = density;
         mInvActualScale = 1 / density;
+        mReadingLevelScale = density;
         mTextWrapScale = density;
     }
 
@@ -304,13 +311,7 @@
     }
 
     public final float getReadingLevelScale() {
-        return computeScaleWithLimits(computeReadingLevelScale(getZoomOverviewScale()));
-    }
-
-    /* package */ final static float computeReadingLevelScale(float scale) {
-        // The reading scale is at least 0.5f apart from the input scale.
-        final float MIN_SCALE_DIFF = 0.5f;
-        return Math.max(scale + MIN_SCALE_DIFF, DEFAULT_READING_LEVEL_SCALE);
+        return mReadingLevelScale;
     }
 
     public final float getInvDefaultScale() {
@@ -652,7 +653,7 @@
         } else if (!mInZoomOverview && willScaleTriggerZoom(getZoomOverviewScale())) {
             zoomToOverview();
         } else {
-            zoomToReadingLevel();
+            zoomToReadingLevelOrMore();
         }
     }
 
@@ -683,8 +684,10 @@
             !mWebView.getSettings().getUseFixedViewport());
     }
 
-    private void zoomToReadingLevel() {
-        final float readingScale = getReadingLevelScale();
+    private void zoomToReadingLevelOrMore() {
+        final float zoomScale = Math.max(getReadingLevelScale(),
+                mActualScale + MIN_DOUBLE_TAP_SCALE_INCREMENT);
+
         int left = mWebView.nativeGetBlockLeftEdge(mAnchorX, mAnchorY, mActualScale);
         if (left != WebView.NO_LEFTEDGE) {
             // add a 5pt padding to the left edge.
@@ -693,13 +696,13 @@
             // Re-calculate the zoom center so that the new scroll x will be
             // on the left edge.
             if (viewLeft > 0) {
-                mZoomCenterX = viewLeft * readingScale / (readingScale - mActualScale);
+                mZoomCenterX = viewLeft * zoomScale / (zoomScale - mActualScale);
             } else {
                 mWebView.scrollBy(viewLeft, 0);
                 mZoomCenterX = 0;
             }
         }
-        startZoomAnimation(readingScale,
+        startZoomAnimation(zoomScale,
             !mWebView.getSettings().getUseFixedViewport());
     }
 
diff --git a/core/java/android/widget/Switch.java b/core/java/android/widget/Switch.java
index 4fcb358..02c9d03 100644
--- a/core/java/android/widget/Switch.java
+++ b/core/java/android/widget/Switch.java
@@ -161,6 +161,7 @@
         mMinFlingVelocity = config.getScaledMinimumFlingVelocity();
 
         // Refresh display with current params
+        refreshDrawableState();
         setChecked(isChecked());
     }
 
@@ -632,8 +633,9 @@
         int[] myDrawableState = getDrawableState();
 
         // Set the state of the Drawable
-        mThumbDrawable.setState(myDrawableState);
-        mTrackDrawable.setState(myDrawableState);
+        // Drawable may be null when checked state is set from XML, from super constructor
+        if (mThumbDrawable != null) mThumbDrawable.setState(myDrawableState);
+        if (mTrackDrawable != null) mTrackDrawable.setState(myDrawableState);
 
         invalidate();
     }
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 719740f..abf1281 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -21,6 +21,7 @@
 import android.content.ClipData.Item;
 import android.content.ClipboardManager;
 import android.content.Context;
+import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
@@ -42,6 +43,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.SystemClock;
+import android.provider.Settings;
 import android.text.BoringLayout;
 import android.text.DynamicLayout;
 import android.text.Editable;
@@ -8244,10 +8246,10 @@
             return superResult;
         }
 
-        final boolean touchIsFinished = action == MotionEvent.ACTION_UP && !mIgnoreActionUpEvent &&
-                isFocused();
+        final boolean touchIsFinished = (action == MotionEvent.ACTION_UP) &&
+                !shouldIgnoreActionUpEvent() && isFocused();
 
-        if ((mMovement != null || onCheckIsTextEditor()) && isEnabled()
+         if ((mMovement != null || onCheckIsTextEditor()) && isEnabled()
                 && mText instanceof Spannable && mLayout != null) {
             boolean handled = false;
 
@@ -8255,9 +8257,10 @@
                 handled |= mMovement.onTouchEvent(this, (Spannable) mText, event);
             }
 
-            if (mLinksClickable && mAutoLinkMask != 0 && mTextIsSelectable && touchIsFinished) {
+            if (touchIsFinished && mLinksClickable && mAutoLinkMask != 0 && mTextIsSelectable) {
                 // The LinkMovementMethod which should handle taps on links has not been installed
-                // to support text selection. We reproduce its behavior here to open links.
+                // on non editable text that support text selection.
+                // We reproduce its behavior here to open links for these.
                 ClickableSpan[] links = ((Spannable) mText).getSpans(getSelectionStart(),
                         getSelectionEnd(), ClickableSpan.class);
 
@@ -8267,7 +8270,7 @@
                 }
             }
 
-            if ((isTextEditable() || mTextIsSelectable) && touchIsFinished) {
+            if (touchIsFinished && (isTextEditable() || mTextIsSelectable)) {
                 // Show the IME, except when selecting in read-only text.
                 final InputMethodManager imm = InputMethodManager.peekInstance();
                 if (imm != null) {
@@ -8278,16 +8281,12 @@
                 }
 
                 boolean selectAllGotFocus = mSelectAllOnFocus && didTouchFocusSelect();
-                if (!selectAllGotFocus && hasSelection()) {
-                    startSelectionActionMode();
-                } else {
-                    hideControllers();
-                    if (!selectAllGotFocus && mText.length() > 0) {
-                        if (isCursorInsideEasyCorrectionSpan()) {
-                            showSuggestions();
-                        } else if (hasInsertionController()) {
-                            getInsertionController().show();
-                        }
+                hideControllers();
+                if (!selectAllGotFocus && mText.length() > 0) {
+                    if (isCursorInsideEasyCorrectionSpan()) {
+                        showSuggestions();
+                    } else if (hasInsertionController()) {
+                        getInsertionController().show();
                     }
                 }
 
@@ -8419,7 +8418,18 @@
         super.cancelLongPress();
         mIgnoreActionUpEvent = true;
     }
-    
+
+    /**
+     * This method is only valid during a touch event.
+     *
+     * @return true when the ACTION_UP event should be ignored, false otherwise.
+     *
+     * @hide
+     */
+    public boolean shouldIgnoreActionUpEvent() {
+        return mIgnoreActionUpEvent;
+    }
+
     @Override
     public boolean onTrackballEvent(MotionEvent event) {
         if (mMovement != null && mText instanceof Spannable &&
@@ -9173,6 +9183,7 @@
                 startDrag(data, getTextThumbnailBuilder(selectedText), localState, 0);
                 stopSelectionActionMode();
             } else {
+                getSelectionController().hide();
                 selectCurrentWord();
                 getSelectionController().show();
             }
@@ -9220,7 +9231,8 @@
     }
 
     private interface TextViewPositionListener {
-        public void updatePosition(int parentPositionX, int parentPositionY, boolean modified);
+        public void updatePosition(int parentPositionX, int parentPositionY,
+                boolean parentPositionChanged, boolean parentScrolled);
     }
 
     private class PositionListener implements ViewTreeObserver.OnPreDrawListener {
@@ -9233,6 +9245,7 @@
         // Absolute position of the TextView with respect to its parent window
         private int mPositionX, mPositionY;
         private int mNumberOfListeners;
+        private boolean mScrollHasChanged;
 
         public void addSubscriber(TextViewPositionListener positionListener, boolean canMove) {
             if (mNumberOfListeners == 0) {
@@ -9284,15 +9297,16 @@
             updatePosition();
 
             for (int i = 0; i < MAXIMUM_NUMBER_OF_LISTENERS; i++) {
-                if (mPositionHasChanged || mCanMove[i]) {
+                if (mPositionHasChanged || mScrollHasChanged || mCanMove[i]) {
                     TextViewPositionListener positionListener = mPositionListeners[i];
                     if (positionListener != null) {
                         positionListener.updatePosition(mPositionX, mPositionY,
-                                mPositionHasChanged);
+                                mPositionHasChanged, mScrollHasChanged);
                     }
                 }
             }
 
+            mScrollHasChanged = false;
             return true;
         }
 
@@ -9334,6 +9348,18 @@
             final int primaryHorizontal = (int) mLayout.getPrimaryHorizontal(offset);
             return isVisible(primaryHorizontal, lineBottom);
         }
+
+        public void onScrollChanged() {
+            mScrollHasChanged = true;
+        }
+    }
+
+    @Override
+    protected void onScrollChanged(int horiz, int vert, int oldHoriz, int oldVert) {
+        super.onScrollChanged(horiz, vert, oldHoriz, oldVert);
+        if (mPositionListener != null) {
+            mPositionListener.onScrollChanged();
+        }
     }
 
     private abstract class PinnedPopupWindow implements TextViewPositionListener {
@@ -9364,7 +9390,7 @@
         }
 
         public void show() {
-            TextView.this.getPositionListener().addSubscriber(this, false);
+            TextView.this.getPositionListener().addSubscriber(this, false /* offset is fixed */);
 
             computeLocalPosition();
 
@@ -9423,8 +9449,11 @@
         }
 
         @Override
-        public void updatePosition(int parentPositionX, int parentPositionY, boolean modified) {
+        public void updatePosition(int parentPositionX, int parentPositionY,
+                boolean parentPositionChanged, boolean parentScrolled) {
+            // Either parentPositionChanged or parentScrolled is true, check if still visible
             if (isShowing() && getPositionListener().isOffsetVisible(getTextOffset())) {
+                if (parentScrolled) computeLocalPosition();
                 updatePosition(parentPositionX, parentPositionY);
             } else {
                 hide();
@@ -9438,7 +9467,6 @@
 
     private class SuggestionsPopupWindow extends PinnedPopupWindow implements OnItemClickListener {
         private static final int MAX_NUMBER_SUGGESTIONS = SuggestionSpan.SUGGESTIONS_MAX_SIZE;
-        private static final int NO_SUGGESTIONS = -1;
         private static final float AVERAGE_HIGHLIGHTS_PER_SUGGESTION = 1.4f;
         private WordIterator mSuggestionWordIterator;
         private TextAppearanceSpan[] mHighlightSpans = new TextAppearanceSpan
@@ -9493,9 +9521,9 @@
             listView.setOnItemClickListener(this);
             mContentView = listView;
 
-            // Inflate the suggestion items once and for all.
-            mSuggestionInfos = new SuggestionInfo[MAX_NUMBER_SUGGESTIONS];
-            for (int i = 0; i < MAX_NUMBER_SUGGESTIONS; i++) {
+            // Inflate the suggestion items once and for all. +1 for add to dictionary
+            mSuggestionInfos = new SuggestionInfo[MAX_NUMBER_SUGGESTIONS + 1];
+            for (int i = 0; i < MAX_NUMBER_SUGGESTIONS + 1; i++) {
                 mSuggestionInfos[i] = new SuggestionInfo();
             }
         }
@@ -9506,6 +9534,15 @@
             SuggestionSpan suggestionSpan; // the SuggestionSpan that this TextView represents
             int suggestionIndex; // the index of the suggestion inside suggestionSpan
             SpannableStringBuilder text = new SpannableStringBuilder();
+
+            void removeMisspelledFlag() {
+                int suggestionSpanFlags = suggestionSpan.getFlags();
+                if ((suggestionSpanFlags & SuggestionSpan.FLAG_MISSPELLED) > 0) {
+                    suggestionSpanFlags &= ~(SuggestionSpan.FLAG_MISSPELLED);
+                    suggestionSpanFlags &= ~(SuggestionSpan.FLAG_EASY_CORRECT);
+                    suggestionSpan.setFlags(suggestionSpanFlags);
+                }
+            }
         }
 
         private class SuggestionAdapter extends BaseAdapter {
@@ -9644,6 +9681,8 @@
             int spanUnionStart = mText.length();
             int spanUnionEnd = 0;
 
+            SuggestionSpan misspelledSpan = null;
+
             for (int spanIndex = 0; spanIndex < nbSpans; spanIndex++) {
                 SuggestionSpan suggestionSpan = suggestionSpans[spanIndex];
                 final int spanStart = spannable.getSpanStart(suggestionSpan);
@@ -9651,6 +9690,10 @@
                 spanUnionStart = Math.min(spanStart, spanUnionStart);
                 spanUnionEnd = Math.max(spanEnd, spanUnionEnd);
 
+                if ((suggestionSpan.getFlags() & SuggestionSpan.FLAG_MISSPELLED) != 0) {
+                    misspelledSpan = suggestionSpan;
+                }
+
                 String[] suggestions = suggestionSpan.getSuggestions();
                 int nbSuggestions = suggestions.length;
                 for (int suggestionIndex = 0; suggestionIndex < nbSuggestions; suggestionIndex++) {
@@ -9659,7 +9702,8 @@
                     suggestionInfo.spanEnd = spanEnd;
                     suggestionInfo.suggestionSpan = suggestionSpan;
                     suggestionInfo.suggestionIndex = suggestionIndex;
-                    suggestionInfo.text = new SpannableStringBuilder(suggestions[suggestionIndex]);
+                    suggestionInfo.text.replace(0, suggestionInfo.text.length(),
+                            suggestions[suggestionIndex]);
 
                     mNumberOfSuggestions++;
                     if (mNumberOfSuggestions == MAX_NUMBER_SUGGESTIONS) {
@@ -9670,41 +9714,38 @@
                 }
             }
 
+            for (int i = 0; i < mNumberOfSuggestions; i++) {
+                highlightTextDifferences(mSuggestionInfos[i], spanUnionStart, spanUnionEnd);
+            }
+
+            if (misspelledSpan != null) {
+                final int misspelledStart = spannable.getSpanStart(misspelledSpan);
+                final int misspelledEnd = spannable.getSpanEnd(misspelledSpan);
+                if (misspelledStart >= 0 && misspelledEnd > misspelledStart) {
+                    SuggestionInfo suggestionInfo = mSuggestionInfos[mNumberOfSuggestions];
+                    suggestionInfo.spanStart = misspelledStart;
+                    suggestionInfo.spanEnd = misspelledEnd;
+                    suggestionInfo.suggestionSpan = misspelledSpan;
+                    suggestionInfo.suggestionIndex = -1;
+                    suggestionInfo.text.replace(0, suggestionInfo.text.length(),
+                            getContext().getString(com.android.internal.R.string.addToDictionary));
+
+                    mNumberOfSuggestions++;
+                }
+            }
+
             if (mNumberOfSuggestions == 0) return false;
 
             if (mSuggestionRangeSpan == null) mSuggestionRangeSpan =
                     new SuggestionRangeSpan(mHighlightColor);
-
             ((Editable) mText).setSpan(mSuggestionRangeSpan, spanUnionStart, spanUnionEnd,
                     Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
 
-            for (int i = 0; i < mNumberOfSuggestions; i++) {
-                highlightTextDifferences(mSuggestionInfos[i], spanUnionStart, spanUnionEnd);
-            }
             mSuggestionsAdapter.notifyDataSetChanged();
 
             return true;
         }
 
-        private void onDictionarySuggestionsReceived(String[] suggestions) {
-            if (suggestions.length == 0) {
-                // TODO Actual implementation of this feature
-                suggestions = new String[] {"Add to dictionary"};
-            }
-
-            WordIterator wordIterator = getWordIterator();
-            wordIterator.setCharSequence(mText);
-
-            final int pos = getSelectionStart();
-            int wordStart = wordIterator.getBeginning(pos);
-            int wordEnd = wordIterator.getEnd(pos);
-
-            SuggestionSpan suggestionSpan = new SuggestionSpan(getContext(), suggestions, 0);
-            ((Editable) mText).setSpan(suggestionSpan, wordStart, wordEnd,
-                    Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
-            show();
-        }
-
         private long[] getWordLimits(CharSequence text) {
             // TODO locale for mSuggestionWordIterator
             if (mSuggestionWordIterator == null) mSuggestionWordIterator = new WordIterator();
@@ -9857,10 +9898,19 @@
         public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
             if (view instanceof TextView) {
                 TextView textView = (TextView) view;
+
                 SuggestionInfo suggestionInfo = mSuggestionInfos[position];
                 final int spanStart = suggestionInfo.spanStart;
                 final int spanEnd = suggestionInfo.spanEnd;
-                if (spanStart != NO_SUGGESTIONS) {
+                final String originalText = mText.subSequence(spanStart, spanEnd).toString();
+
+                if (suggestionInfo.suggestionIndex < 0) {
+                    Intent intent = new Intent(Settings.ACTION_USER_DICTIONARY_INSERT);
+                    intent.putExtra("word", originalText);
+                    intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
+                    getContext().startActivity(intent);
+                    suggestionInfo.removeMisspelledFlag();
+                } else {
                     // SuggestionSpans are removed by replace: save them before
                     Editable editable = (Editable) mText;
                     SuggestionSpan[] suggestionSpans = editable.getSpans(spanStart, spanEnd,
@@ -9880,17 +9930,9 @@
                     final int suggestionEnd = suggestionInfo.suggestionEnd;
                     final String suggestion = textView.getText().subSequence(
                             suggestionStart, suggestionEnd).toString();
-                    final String originalText = mText.subSequence(spanStart, spanEnd).toString();
                     editable.replace(spanStart, spanEnd, suggestion);
 
-                    // A replacement on a misspelled text removes the misspelled flag.
-                    // TODO restore the flag if the misspelled word is selected back?
-                    int suggestionSpanFlags = suggestionInfo.suggestionSpan.getFlags();
-                    if ((suggestionSpanFlags & SuggestionSpan.FLAG_MISSPELLED) > 0) {
-                        suggestionSpanFlags &= ~(SuggestionSpan.FLAG_MISSPELLED);
-                        suggestionSpanFlags &= ~(SuggestionSpan.FLAG_EASY_CORRECT);
-                        suggestionInfo.suggestionSpan.setFlags(suggestionSpanFlags);
-                    }
+                    suggestionInfo.removeMisspelledFlag();
 
                     // Notify source IME of the suggestion pick. Do this before swaping texts.
                     if (!TextUtils.isEmpty(
@@ -9937,12 +9979,6 @@
         return mSuggestionsPopupWindow != null && mSuggestionsPopupWindow.isShowing();
     }
 
-    void onDictionarySuggestionsReceived(String[] suggestions) {
-        if (mSuggestionsPopupWindow != null) {
-            mSuggestionsPopupWindow.onDictionarySuggestionsReceived(suggestions);
-        }
-    }
-
     /**
      * Return whether or not suggestions are enabled on this TextView. The suggestions are generated
      * by the IME or by the spell checker as the user types. This is done by adding
@@ -10370,7 +10406,7 @@
 
             if (i > 0 && i < iMax &&
                     (now - mPreviousOffsetsTimes[index]) > TOUCH_UP_FILTER_DELAY_BEFORE) {
-                positionAtCursorOffset(mPreviousOffsets[index]);
+                positionAtCursorOffset(mPreviousOffsets[index], false);
             }
         }
 
@@ -10386,11 +10422,11 @@
         public void show() {
             if (isShowing()) return;
 
-            getPositionListener().addSubscriber(this, true);
+            getPositionListener().addSubscriber(this, true /* local position may change */);
 
             // Make sure the offset is always considered new, even when focusing at same position
             mPreviousOffset = -1;
-            positionAtCursorOffset(getCurrentCursorOffset());
+            positionAtCursorOffset(getCurrentCursorOffset(), false);
 
             hideActionPopupWindow();
         }
@@ -10455,7 +10491,7 @@
 
         public abstract void updatePosition(float x, float y);
 
-        protected void positionAtCursorOffset(int offset) {
+        protected void positionAtCursorOffset(int offset, boolean parentScrolled) {
             // A HandleView relies on the layout, which may be nulled by external methods
             if (mLayout == null) {
                 // Will update controllers' state, hiding them and stopping selection mode if needed
@@ -10463,7 +10499,7 @@
                 return;
             }
 
-            if (offset != mPreviousOffset) {
+            if (offset != mPreviousOffset || parentScrolled) {
                 updateSelection(offset);
                 addPositionToTouchUpFilter(offset);
                 final int line = mLayout.getLineForOffset(offset);
@@ -10480,9 +10516,10 @@
             }
         }
 
-        public void updatePosition(int parentPositionX, int parentPositionY, boolean modified) {
-            positionAtCursorOffset(getCurrentCursorOffset());
-            if (modified || mPositionHasChanged) {
+        public void updatePosition(int parentPositionX, int parentPositionY,
+                boolean parentPositionChanged, boolean parentScrolled) {
+            positionAtCursorOffset(getCurrentCursorOffset(), parentScrolled);
+            if (parentPositionChanged || mPositionHasChanged) {
                 if (mIsDragging) {
                     // Update touchToWindow offset in case of parent scrolling while dragging
                     if (parentPositionX != mLastParentX || parentPositionY != mLastParentY) {
@@ -10687,7 +10724,7 @@
 
         @Override
         public void updatePosition(float x, float y) {
-            positionAtCursorOffset(getOffsetForPosition(x, y));
+            positionAtCursorOffset(getOffsetForPosition(x, y), false);
         }
 
         @Override
@@ -10726,17 +10763,13 @@
 
         @Override
         public void updatePosition(float x, float y) {
-            final int selectionStart = getSelectionStart();
-            final int selectionEnd = getSelectionEnd();
-
             int offset = getOffsetForPosition(x, y);
 
-            // No need to redraw when the offset is unchanged
-            if (offset == selectionStart) return;
             // Handles can not cross and selection is at least one character
+            final int selectionEnd = getSelectionEnd();
             if (offset >= selectionEnd) offset = selectionEnd - 1;
 
-            positionAtCursorOffset(offset);
+            positionAtCursorOffset(offset, false);
         }
 
         public ActionPopupWindow getActionPopupWindow() {
@@ -10767,17 +10800,13 @@
 
         @Override
         public void updatePosition(float x, float y) {
-            final int selectionStart = getSelectionStart();
-            final int selectionEnd = getSelectionEnd();
-
             int offset = getOffsetForPosition(x, y);
 
-            // No need to redraw when the offset is unchanged
-            if (offset == selectionEnd) return;
             // Handles can not cross and selection is at least one character
+            final int selectionStart = getSelectionStart();
             if (offset <= selectionStart) offset = selectionStart + 1;
 
-            positionAtCursorOffset(offset);
+            positionAtCursorOffset(offset, false);
         }
 
         public void setActionPopupWindow(ActionPopupWindow actionPopupWindow) {
diff --git a/core/res/res/layout-sw600dp/status_bar_latest_event_ticker.xml b/core/res/res/layout-sw600dp/status_bar_latest_event_ticker.xml
index 7631781..269e086 100644
--- a/core/res/res/layout-sw600dp/status_bar_latest_event_ticker.xml
+++ b/core/res/res/layout-sw600dp/status_bar_latest_event_ticker.xml
@@ -38,8 +38,8 @@
         android:textAppearance="@style/TextAppearance.StatusBar.EventContent.Info"
         android:layout_width="wrap_content"
         android:layout_height="match_parent"
-        android:layout_marginTop="-10dp"
+        android:layout_marginBottom="6dip"
+        android:gravity="bottom"
         android:singleLine="true"
         />
 </LinearLayout>
-
diff --git a/core/res/res/layout-sw600dp/status_bar_latest_event_ticker_large_icon.xml b/core/res/res/layout-sw600dp/status_bar_latest_event_ticker_large_icon.xml
index ff0f7d4..69eac92 100644
--- a/core/res/res/layout-sw600dp/status_bar_latest_event_ticker_large_icon.xml
+++ b/core/res/res/layout-sw600dp/status_bar_latest_event_ticker_large_icon.xml
@@ -33,8 +33,9 @@
         android:textAppearance="@style/TextAppearance.StatusBar.EventContent.Info"
         android:layout_width="wrap_content"
         android:layout_height="match_parent"
+        android:layout_marginBottom="6dip"
+        android:gravity="bottom"
         android:singleLine="true"
-        android:layout_marginTop="-10dp"
         />
     <ImageView android:id="@+id/icon"
         android:layout_width="wrap_content"
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 685ebb7..5e010fe 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2484,6 +2484,9 @@
     <!-- Text selection contextual mode title, displayed in the CAB. [CHAR LIMIT=20] -->
     <string name="textSelectionCABTitle">Text selection</string>
 
+    <!-- Option to add the current misspelled word to the user dictionary. [CHAR LIMIT=25] -->
+    <string name="addToDictionary">+ add to dictionary</string>
+
     <!-- EditText context menu -->
     <string name="inputMethod">Input method</string>
 
diff --git a/data/fonts/fallback_fonts.xml b/data/fonts/fallback_fonts.xml
index 1bee47a..e468558 100644
--- a/data/fonts/fallback_fonts.xml
+++ b/data/fonts/fallback_fonts.xml
@@ -46,6 +46,16 @@
     </family>
     <family>
         <fileset>
+            <file>DroidSansArmenian.ttf</file>
+        </fileset>
+    </family>
+    <family>
+        <fileset>
+            <file>DroidSansGeorgian.ttf</file>
+        </fileset>
+    </family>
+    <family>
+        <fileset>
             <file>Lohit_Hindi.ttf</file>
         </fileset>
     </family>
diff --git a/data/fonts/fonts.mk b/data/fonts/fonts.mk
index 56bd2ce..5bac8f0 100644
--- a/data/fonts/fonts.mk
+++ b/data/fonts/fonts.mk
@@ -31,6 +31,8 @@
     frameworks/base/data/fonts/DroidSerif-BoldItalic.ttf:system/fonts/DroidSerif-BoldItalic.ttf \
     frameworks/base/data/fonts/DroidSansMono.ttf:system/fonts/DroidSansMono.ttf \
     frameworks/base/data/fonts/Lohit_Hindi.ttf:system/fonts/Lohit_Hindi.ttf \
+    frameworks/base/data/fonts/DroidSansArmenian.ttf:system/fonts/DroidSansArmenian.ttf \
+    frameworks/base/data/fonts/DroidSansGeorgian.ttf:system/fonts/DroidSansGeorgian.ttf \
     frameworks/base/data/fonts/Clockopia.ttf:system/fonts/Clockopia.ttf \
     frameworks/base/data/fonts/AndroidClock.ttf:system/fonts/AndroidClock.ttf \
     frameworks/base/data/fonts/AndroidClock_Highlight.ttf:system/fonts/AndroidClock_Highlight.ttf \
diff --git a/data/sounds/alarms/wav/Copernicium.wav b/data/sounds/alarms/wav/Copernicium.wav
new file mode 100644
index 0000000..935ea75
--- /dev/null
+++ b/data/sounds/alarms/wav/Copernicium.wav
Binary files differ
diff --git a/data/sounds/alarms/wav/Curium.wav b/data/sounds/alarms/wav/Curium.wav
new file mode 100644
index 0000000..a9942d3
--- /dev/null
+++ b/data/sounds/alarms/wav/Curium.wav
Binary files differ
diff --git a/data/sounds/alarms/wav/Fermium.wav b/data/sounds/alarms/wav/Fermium.wav
new file mode 100644
index 0000000..0174884
--- /dev/null
+++ b/data/sounds/alarms/wav/Fermium.wav
Binary files differ
diff --git a/data/sounds/alarms/wav/Hassium.wav b/data/sounds/alarms/wav/Hassium.wav
new file mode 100644
index 0000000..e3992cf
--- /dev/null
+++ b/data/sounds/alarms/wav/Hassium.wav
Binary files differ
diff --git a/data/sounds/alarms/wav/Neptunium.wav b/data/sounds/alarms/wav/Neptunium.wav
new file mode 100644
index 0000000..cf3684a
--- /dev/null
+++ b/data/sounds/alarms/wav/Neptunium.wav
Binary files differ
diff --git a/data/sounds/alarms/wav/Nobelium.wav b/data/sounds/alarms/wav/Nobelium.wav
new file mode 100644
index 0000000..0e9101a
--- /dev/null
+++ b/data/sounds/alarms/wav/Nobelium.wav
Binary files differ
diff --git a/data/sounds/effects/wav/CameraClick.wav b/data/sounds/effects/wav/CameraClick.wav
new file mode 100644
index 0000000..9fe75f2
--- /dev/null
+++ b/data/sounds/effects/wav/CameraClick.wav
Binary files differ
diff --git a/data/sounds/effects/wav/Dock.wav b/data/sounds/effects/wav/Dock.wav
index 7fa6a7e..7ec64a8 100644
--- a/data/sounds/effects/wav/Dock.wav
+++ b/data/sounds/effects/wav/Dock.wav
Binary files differ
diff --git a/data/sounds/effects/wav/KeypressDelete_14.wav b/data/sounds/effects/wav/KeypressDelete_14.wav
new file mode 100644
index 0000000..1d3498e
--- /dev/null
+++ b/data/sounds/effects/wav/KeypressDelete_14.wav
Binary files differ
diff --git a/data/sounds/effects/wav/KeypressDelete_49.wav b/data/sounds/effects/wav/KeypressDelete_49.wav
new file mode 100644
index 0000000..2404759
--- /dev/null
+++ b/data/sounds/effects/wav/KeypressDelete_49.wav
Binary files differ
diff --git a/data/sounds/effects/wav/KeypressReturn_14.wav b/data/sounds/effects/wav/KeypressReturn_14.wav
new file mode 100644
index 0000000..01573ff
--- /dev/null
+++ b/data/sounds/effects/wav/KeypressReturn_14.wav
Binary files differ
diff --git a/data/sounds/effects/wav/KeypressReturn_49.wav b/data/sounds/effects/wav/KeypressReturn_49.wav
new file mode 100644
index 0000000..6bf216f
--- /dev/null
+++ b/data/sounds/effects/wav/KeypressReturn_49.wav
Binary files differ
diff --git a/data/sounds/effects/wav/KeypressSpacebar_14.wav b/data/sounds/effects/wav/KeypressSpacebar_14.wav
new file mode 100644
index 0000000..77269a6
--- /dev/null
+++ b/data/sounds/effects/wav/KeypressSpacebar_14.wav
Binary files differ
diff --git a/data/sounds/effects/wav/KeypressSpacebar_49.wav b/data/sounds/effects/wav/KeypressSpacebar_49.wav
new file mode 100644
index 0000000..8504a25
--- /dev/null
+++ b/data/sounds/effects/wav/KeypressSpacebar_49.wav
Binary files differ
diff --git a/data/sounds/effects/wav/KeypressStandard_14.wav b/data/sounds/effects/wav/KeypressStandard_14.wav
new file mode 100644
index 0000000..93389ac
--- /dev/null
+++ b/data/sounds/effects/wav/KeypressStandard_14.wav
Binary files differ
diff --git a/data/sounds/effects/wav/KeypressStandard_49.wav b/data/sounds/effects/wav/KeypressStandard_49.wav
new file mode 100644
index 0000000..9a660a1
--- /dev/null
+++ b/data/sounds/effects/wav/KeypressStandard_49.wav
Binary files differ
diff --git a/data/sounds/effects/wav/Lock.wav b/data/sounds/effects/wav/Lock.wav
index 88be052..fead37f 100644
--- a/data/sounds/effects/wav/Lock.wav
+++ b/data/sounds/effects/wav/Lock.wav
Binary files differ
diff --git a/data/sounds/effects/wav/LowBattery.wav b/data/sounds/effects/wav/LowBattery.wav
index 8905534..5d8b48d 100644
--- a/data/sounds/effects/wav/LowBattery.wav
+++ b/data/sounds/effects/wav/LowBattery.wav
Binary files differ
diff --git a/data/sounds/effects/wav/Undock.wav b/data/sounds/effects/wav/Undock.wav
index 358eb18..79abb4e 100644
--- a/data/sounds/effects/wav/Undock.wav
+++ b/data/sounds/effects/wav/Undock.wav
Binary files differ
diff --git a/data/sounds/effects/wav/Unlock.wav b/data/sounds/effects/wav/Unlock.wav
index 4b39c5c..33b80ff 100644
--- a/data/sounds/effects/wav/Unlock.wav
+++ b/data/sounds/effects/wav/Unlock.wav
Binary files differ
diff --git a/data/sounds/effects/wav/VideoRecord.wav b/data/sounds/effects/wav/VideoRecord.wav
index 6880b29..f431023 100644
--- a/data/sounds/effects/wav/VideoRecord.wav
+++ b/data/sounds/effects/wav/VideoRecord.wav
Binary files differ
diff --git a/data/sounds/effects/wav/camera_click.wav b/data/sounds/effects/wav/camera_click.wav
deleted file mode 100644
index 420da7c..0000000
--- a/data/sounds/effects/wav/camera_click.wav
+++ /dev/null
Binary files differ
diff --git a/data/sounds/notifications/wav/Altair.wav b/data/sounds/notifications/wav/Altair.wav
new file mode 100644
index 0000000..0fb9788
--- /dev/null
+++ b/data/sounds/notifications/wav/Altair.wav
Binary files differ
diff --git a/data/sounds/notifications/wav/Antares.wav b/data/sounds/notifications/wav/Antares.wav
new file mode 100644
index 0000000..7c2dd23
--- /dev/null
+++ b/data/sounds/notifications/wav/Antares.wav
Binary files differ
diff --git a/data/sounds/notifications/wav/Betelgeuse.wav b/data/sounds/notifications/wav/Betelgeuse.wav
new file mode 100644
index 0000000..9ad799f
--- /dev/null
+++ b/data/sounds/notifications/wav/Betelgeuse.wav
Binary files differ
diff --git a/data/sounds/notifications/wav/Deneb.wav b/data/sounds/notifications/wav/Deneb.wav
new file mode 100644
index 0000000..ffe7c31
--- /dev/null
+++ b/data/sounds/notifications/wav/Deneb.wav
Binary files differ
diff --git a/data/sounds/notifications/wav/Hojus.wav b/data/sounds/notifications/wav/Hojus.wav
new file mode 100644
index 0000000..076a0c7
--- /dev/null
+++ b/data/sounds/notifications/wav/Hojus.wav
Binary files differ
diff --git a/data/sounds/notifications/wav/Lalande.wav b/data/sounds/notifications/wav/Lalande.wav
new file mode 100644
index 0000000..14c9fec
--- /dev/null
+++ b/data/sounds/notifications/wav/Lalande.wav
Binary files differ
diff --git a/data/sounds/notifications/wav/Mira.wav b/data/sounds/notifications/wav/Mira.wav
new file mode 100644
index 0000000..71be516
--- /dev/null
+++ b/data/sounds/notifications/wav/Mira.wav
Binary files differ
diff --git a/data/sounds/notifications/wav/Proxima.wav b/data/sounds/notifications/wav/Proxima.wav
new file mode 100644
index 0000000..109bfdd
--- /dev/null
+++ b/data/sounds/notifications/wav/Proxima.wav
Binary files differ
diff --git a/data/sounds/notifications/wav/Upsilon.wav b/data/sounds/notifications/wav/Upsilon.wav
new file mode 100644
index 0000000..ffc959b
--- /dev/null
+++ b/data/sounds/notifications/wav/Upsilon.wav
Binary files differ
diff --git a/data/sounds/ringtones/wav/Cassiopeia.wav b/data/sounds/ringtones/wav/Cassiopeia.wav
new file mode 100644
index 0000000..5c5c6e0
--- /dev/null
+++ b/data/sounds/ringtones/wav/Cassiopeia.wav
Binary files differ
diff --git a/data/sounds/ringtones/wav/Lyra.wav b/data/sounds/ringtones/wav/Lyra.wav
new file mode 100644
index 0000000..2943cf5
--- /dev/null
+++ b/data/sounds/ringtones/wav/Lyra.wav
Binary files differ
diff --git a/data/sounds/ringtones/wav/Sceptrum.wav b/data/sounds/ringtones/wav/Sceptrum.wav
new file mode 100644
index 0000000..3694373
--- /dev/null
+++ b/data/sounds/ringtones/wav/Sceptrum.wav
Binary files differ
diff --git a/data/sounds/ringtones/wav/Solarium.wav b/data/sounds/ringtones/wav/Solarium.wav
new file mode 100644
index 0000000..93f1e01
--- /dev/null
+++ b/data/sounds/ringtones/wav/Solarium.wav
Binary files differ
diff --git a/data/sounds/ringtones/wav/UrsaMinor.wav b/data/sounds/ringtones/wav/UrsaMinor.wav
new file mode 100644
index 0000000..5e16c94
--- /dev/null
+++ b/data/sounds/ringtones/wav/UrsaMinor.wav
Binary files differ
diff --git a/data/sounds/ringtones/wav/Vespa.wav b/data/sounds/ringtones/wav/Vespa.wav
new file mode 100644
index 0000000..7d696f8
--- /dev/null
+++ b/data/sounds/ringtones/wav/Vespa.wav
Binary files differ
diff --git a/graphics/java/android/graphics/drawable/ColorDrawable.java b/graphics/java/android/graphics/drawable/ColorDrawable.java
index 4418e02a..88c9155 100644
--- a/graphics/java/android/graphics/drawable/ColorDrawable.java
+++ b/graphics/java/android/graphics/drawable/ColorDrawable.java
@@ -111,8 +111,11 @@
         alpha += alpha >> 7;   // make it 0..256
         int baseAlpha = mState.mBaseColor >>> 24;
         int useAlpha = baseAlpha * alpha >> 8;
+        int oldUseColor = mState.mUseColor;
         mState.mUseColor = (mState.mBaseColor << 8 >>> 8) | (useAlpha << 24);
-        invalidateSelf();
+        if (oldUseColor != mState.mUseColor) {
+            invalidateSelf();
+        }
     }
 
     /**
diff --git a/media/libstagefright/FileSource.cpp b/media/libstagefright/FileSource.cpp
index f2f3500..0794f57 100644
--- a/media/libstagefright/FileSource.cpp
+++ b/media/libstagefright/FileSource.cpp
@@ -110,6 +110,8 @@
 }
 
 status_t FileSource::getSize(off64_t *size) {
+    Mutex::Autolock autoLock(mLock);
+
     if (mFd < 0) {
         return NO_INIT;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationPanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationPanel.java
index d9cb4e8..4a1cafd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationPanel.java
@@ -28,6 +28,10 @@
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+import android.view.animation.AccelerateInterpolator;
+import android.view.animation.DecelerateInterpolator;
+import android.view.animation.Interpolator;
 import android.widget.RelativeLayout;
 
 import com.android.systemui.R;
@@ -52,6 +56,8 @@
     ViewGroup mContentParent;
     TabletStatusBar mBar;
     View mClearButton;
+    static Interpolator sAccelerateInterpolator = new AccelerateInterpolator();
+    static Interpolator sDecelerateInterpolator = new DecelerateInterpolator();
 
     // amount to slide mContentParent down by when mContentFrame is missing
     float mContentFrameMissingTranslation;
@@ -117,8 +123,13 @@
                 mShowing = show;
                 if (show) {
                     setVisibility(View.VISIBLE);
+                    // Don't start the animation until we've created the layer, which is done
+                    // right before we are drawn
+                    mContentParent.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+                    getViewTreeObserver().addOnPreDrawListener(mPreDrawListener);
+                } else {
+                    mChoreo.startAnimation(show);
                 }
-                mChoreo.startAnimation(show);
             }
         } else {
             mShowing = show;
@@ -127,6 +138,20 @@
     }
 
     /**
+     * This is used only when we've created a hardware layer and are waiting until it's
+     * been created in order to start the appearing animation.
+     */
+    private ViewTreeObserver.OnPreDrawListener mPreDrawListener =
+            new ViewTreeObserver.OnPreDrawListener() {
+        @Override
+        public boolean onPreDraw() {
+            getViewTreeObserver().removeOnPreDrawListener(this);
+            mChoreo.startAnimation(true);
+            return true;
+        }
+    };
+
+    /**
      * Whether the panel is showing, or, if it's animating, whether it will be
      * when the animation is done.
      */
@@ -330,8 +355,8 @@
         AnimatorSet mContentAnim;
 
         // should group this into a multi-property animation
-        final static int OPEN_DURATION = 300;
-        final static int CLOSE_DURATION = 300;
+        final static int OPEN_DURATION = 250;
+        final static int CLOSE_DURATION = 250;
 
         // the panel will start to appear this many px from the end
         final int HYPERSPACE_OFFRAMP = 200;
@@ -362,19 +387,15 @@
 
             Animator posAnim = ObjectAnimator.ofFloat(mContentParent, "translationY",
                     start, end);
-            posAnim.setInterpolator(appearing
-                    ? new android.view.animation.DecelerateInterpolator(1.0f)
-                    : new android.view.animation.AccelerateInterpolator(1.0f));
+            posAnim.setInterpolator(appearing ? sDecelerateInterpolator : sAccelerateInterpolator);
 
             if (mContentAnim != null && mContentAnim.isRunning()) {
                 mContentAnim.cancel();
             }
 
             Animator fadeAnim = ObjectAnimator.ofFloat(mContentParent, "alpha",
-                                mContentParent.getAlpha(), appearing ? 1.0f : 0.0f);
-            fadeAnim.setInterpolator(appearing
-                    ? new android.view.animation.AccelerateInterpolator(2.0f)
-                    : new android.view.animation.DecelerateInterpolator(2.0f));
+                    appearing ? 1.0f : 0.0f);
+            fadeAnim.setInterpolator(appearing ? sAccelerateInterpolator : sDecelerateInterpolator);
 
             mContentAnim = new AnimatorSet();
             mContentAnim
@@ -389,8 +410,6 @@
             if (DEBUG) Slog.d(TAG, "startAnimation(appearing=" + appearing + ")");
 
             createAnimation(appearing);
-
-            mContentParent.setLayerType(View.LAYER_TYPE_HARDWARE, null);
             mContentAnim.start();
 
             mVisible = appearing;
diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java
index cbd986f..bbc26d6 100644
--- a/services/java/com/android/server/PowerManagerService.java
+++ b/services/java/com/android/server/PowerManagerService.java
@@ -853,6 +853,14 @@
                 if ((wl.flags & PowerManager.ACQUIRE_CAUSES_WAKEUP) != 0) {
                     int oldWakeLockState = mWakeLockState;
                     mWakeLockState = mLocks.reactivateScreenLocksLocked();
+
+                    // Disable proximity sensor if if user presses power key while we are in the
+                    // "waiting for proximity sensor to go negative" state.
+                    if ((mWakeLockState & SCREEN_ON_BIT) != 0
+                            && mProximitySensorActive && mProximityWakeLockCount == 0) {
+                        mProximitySensorActive = false;
+                    }
+
                     if (mSpew) {
                         Slog.d(TAG, "wakeup here mUserState=0x" + Integer.toHexString(mUserState)
                                 + " mWakeLockState=0x"