Merge "Fix occasional correction errors in suggesting obvious corrections."
diff --git a/java/res/values-sv/donottranslate.xml b/java/res/values-sv/donottranslate.xml
deleted file mode 100644
index 7f6f357..0000000
--- a/java/res/values-sv/donottranslate.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2009, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="alternates_for_d">ðď</string>
-    <string name="alternates_for_l">ł</string>
-    <string name="alternates_for_r">ř</string>
-    <string name="alternates_for_t">ťþ</string>
-    <string name="alternates_for_v">w</string>
-    <string name="alternates_for_z">źžż</string>
-    <string name="alternates_for_ae">æ</string>
-    <string name="alternates_for_oe">øœ</string>
-</resources>
diff --git a/java/res/xml-sv/kbd_qwerty.xml b/java/res/xml-sv/kbd_qwerty.xml
index b98f613..54d7d68 100644
--- a/java/res/xml-sv/kbd_qwerty.xml
+++ b/java/res/xml-sv/kbd_qwerty.xml
@@ -19,9 +19,10 @@
 -->
 
 <!--
-    Swedish Keyboard Layout.
-    Key placement according to Svensk standard SS 66 22 41.
-    Foreign letters according to Svenska skrivregler (2:a uppl.) §302.
+    Swedish Keyboard Layout
+
+    Key positioning: Svensk standard SS 66 22 41
+    Foreign letters: Svenska skrivregler (2:a uppl.) §302
     Local additions: ۧ
 -->
 
@@ -38,40 +39,41 @@
         <Key android:codes="119" android:keyLabel="w"/>
         <Key android:codes="101" android:keyLabel="e"
                 android:popupKeyboard="@xml/kbd_popup_template"
-                android:popupCharacters="@string/alternates_for_e"/>
+                android:popupCharacters="éèêëę€"/>
         <Key android:codes="114" android:keyLabel="r"
                 android:popupKeyboard="@xml/kbd_popup_template"
-                android:popupCharacters="@string/alternates_for_r"/>
+                android:popupCharacters="ř"/>
         <Key android:codes="116" android:keyLabel="t"
                 android:popupKeyboard="@xml/kbd_popup_template"
-                android:popupCharacters="@string/alternates_for_t"/>
+                android:popupCharacters="ťþ"/>
         <Key android:codes="121" android:keyLabel="y"
                 android:popupKeyboard="@xml/kbd_popup_template"
-                android:popupCharacters="@string/alternates_for_y"/>
+                android:popupCharacters="ýÿü"/>
         <Key android:codes="117" android:keyLabel="u"
                 android:popupKeyboard="@xml/kbd_popup_template"
-                android:popupCharacters="@string/alternates_for_u"/>
+                android:popupCharacters="úùûū"/>
         <Key android:codes="105" android:keyLabel="i"
                 android:popupKeyboard="@xml/kbd_popup_template"
-                android:popupCharacters="@string/alternates_for_i"/>
+                android:popupCharacters="íìîï"/>
         <Key android:codes="111" android:keyLabel="o"
                 android:popupKeyboard="@xml/kbd_popup_template"
-                android:popupCharacters="@string/alternates_for_o"/>
+                android:popupCharacters="óòôõ"/>
         <Key android:codes="112" android:keyLabel="p"/>
-        <Key android:keyLabel="å" android:keyWidth="8.75%p" android:keyEdgeFlags="right"/>
+        <Key android:keyLabel="å"
+                android:keyWidth="8.75%p" android:keyEdgeFlags="right"/>
     </Row>
 
     <Row>
-        <Key android:codes="97" android:keyLabel="a" android:keyWidth="8.75%p"
+        <Key android:codes="97" android:keyLabel="a"
                 android:popupKeyboard="@xml/kbd_popup_template"
-                android:popupCharacters="@string/alternates_for_a"
-                android:keyEdgeFlags="left"/>
+                android:popupCharacters="áàâąã"
+                android:keyWidth="8.75%p" android:keyEdgeFlags="left"/>
         <Key android:codes="115" android:keyLabel="s"
                 android:popupKeyboard="@xml/kbd_popup_template"
-                android:popupCharacters="@string/alternates_for_s"/>
+                android:popupCharacters="śšşß"/>
         <Key android:codes="100" android:keyLabel="d"
                 android:popupKeyboard="@xml/kbd_popup_template"
-                android:popupCharacters="@string/alternates_for_d"/>
+                android:popupCharacters="ðď"/>
         <Key android:codes="102" android:keyLabel="f"/>
         <Key android:codes="103" android:keyLabel="g"/>
         <Key android:codes="104" android:keyLabel="h"/>
@@ -79,13 +81,14 @@
         <Key android:codes="107" android:keyLabel="k"/>
         <Key android:codes="108" android:keyLabel="l"
                 android:popupKeyboard="@xml/kbd_popup_template"
-                android:popupCharacters="@string/alternates_for_l"/>
+                android:popupCharacters="ł"/>
         <Key android:keyLabel="ö"
                 android:popupKeyboard="@xml/kbd_popup_template"
-                android:popupCharacters="@string/alternates_for_oe"/>
-        <Key android:keyLabel="ä" android:keyEdgeFlags="right" android:keyWidth="8.75%p"
+                android:popupCharacters="øœ"/>
+        <Key android:keyLabel="ä"
                 android:popupKeyboard="@xml/kbd_popup_template"
-                android:popupCharacters="@string/alternates_for_ae"/>
+                android:popupCharacters="æ"
+                android:keyWidth="8.75%p" android:keyEdgeFlags="right"/>
     </Row>
 
     <Row android:keyWidth="10%p">
@@ -95,18 +98,18 @@
                 android:isSticky="true" android:keyEdgeFlags="left"/>
         <Key android:codes="122" android:keyLabel="z"
                 android:popupKeyboard="@xml/kbd_popup_template"
-                android:popupCharacters="@string/alternates_for_z"/>
+                android:popupCharacters="źžż"/>
         <Key android:codes="120" android:keyLabel="x"/>
         <Key android:codes="99" android:keyLabel="c"
                 android:popupKeyboard="@xml/kbd_popup_template"
-                android:popupCharacters="@string/alternates_for_c"/>
+                android:popupCharacters="çćč"/>
         <Key android:codes="118" android:keyLabel="v"
                 android:popupKeyboard="@xml/kbd_popup_template"
-                android:popupCharacters="@string/alternates_for_v"/>
+                android:popupCharacters="w"/>
         <Key android:codes="98" android:keyLabel="b"/>
         <Key android:codes="110" android:keyLabel="n"
                 android:popupKeyboard="@xml/kbd_popup_template"
-                android:popupCharacters="@string/alternates_for_n"/>
+                android:popupCharacters="ńñň"/>
         <Key android:codes="109" android:keyLabel="m"/>
         <Key android:codes="-5" android:keyIcon="@drawable/sym_keyboard_delete"
                 android:keyWidth="15%p" android:keyEdgeFlags="right"
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index ffa7b0a..70d49f5 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -234,6 +234,7 @@
         List<String> candidates;
         Map<String, List<CharSequence>> alternatives;
     }
+
     private boolean mRefreshKeyboardRequired;
 
     Handler mHandler = new Handler() {
@@ -552,6 +553,7 @@
 
         if (VOICE_INSTALLED && !mConfigurationChanging) {
             if (mAfterVoiceInput) {
+                mVoiceInput.flushAllTextModificationCounters();
                 mVoiceInput.logInputEnded();
             }
             mVoiceInput.flushLogs();
@@ -567,8 +569,6 @@
         super.onUpdateExtractedText(token, text);
         InputConnection ic = getCurrentInputConnection();
         if (!mImmediatelyAfterVoiceInput && mAfterVoiceInput && ic != null) {
-            mVoiceInput.logTextModified();
-
             if (mHints.showPunctuationHintIfNecessary(ic)) {
                 mVoiceInput.logPunctuationHintDisplayed();
             }
@@ -592,6 +592,11 @@
                     + ", ce=" + candidatesEnd);
         }
 
+        if (mAfterVoiceInput) {
+            mVoiceInput.setCursorPos(newSelEnd);
+            mVoiceInput.setSelectionSpan(newSelEnd - newSelStart);
+        }
+
         mSuggestionShouldReplaceCurrentWord = false;
         // If the current selection in the text view changes, we should
         // clear whatever candidate text we have.
@@ -997,12 +1002,27 @@
 
     private void handleBackspace() {
         if (VOICE_INSTALLED && mVoiceInputHighlighted) {
+            mVoiceInput.incrementTextModificationDeleteCount(
+                    mVoiceResults.candidates.get(0).toString().length());
             revertVoiceInput();
             return;
         }
         boolean deleteChar = false;
         InputConnection ic = getCurrentInputConnection();
         if (ic == null) return;
+
+        if (mAfterVoiceInput) {
+            // Don't log delete if the user is pressing delete at
+            // the beginning of the text box (hence not deleting anything)
+            if (mVoiceInput.getCursorPos() > 0) {
+                // If anything was selected before the delete was pressed, increment the
+                // delete count by the length of the selection
+                int deleteLen  =  mVoiceInput.getSelectionSpan() > 0 ?
+                        mVoiceInput.getSelectionSpan() : 1;
+                mVoiceInput.incrementTextModificationDeleteCount(deleteLen);
+            }
+        }
+
         if (mPredicting) {
             final int length = mComposing.length();
             if (length > 0) {
@@ -1048,6 +1068,12 @@
         if (VOICE_INSTALLED && mVoiceInputHighlighted) {
             commitVoiceInput();
         }
+
+        if (mAfterVoiceInput) {
+            // Assume input length is 1. This assumption fails for smiley face insertions.
+            mVoiceInput.incrementTextModificationInsertCount(1);
+        }
+
         if (isAlphabet(primaryCode) && isPredictionOn() && !isCursorTouchingWord()) {
             if (!mPredicting) {
                 mPredicting = true;
@@ -1091,6 +1117,12 @@
         if (VOICE_INSTALLED && mVoiceInputHighlighted) {
             commitVoiceInput();
         }
+
+        if (mAfterVoiceInput){
+            // Assume input length is 1. This assumption fails for smiley face insertions.
+            mVoiceInput.incrementTextModificationInsertPunctuationCount(1);
+        }
+
         boolean pickedDefault = false;
         // Handle separator
         InputConnection ic = getCurrentInputConnection();
@@ -1344,7 +1376,7 @@
 
         String bestResult = nBest.get(0).toString();
 
-        mVoiceInput.logVoiceInputDelivered();
+        mVoiceInput.logVoiceInputDelivered(bestResult.length());
 
         mHints.registerVoiceResult(bestResult);
 
@@ -1448,6 +1480,12 @@
     public void pickSuggestionManually(int index, CharSequence suggestion) {
         if (mAfterVoiceInput && mShowingVoiceSuggestions) mVoiceInput.logNBestChoose(index);
 
+        if (mAfterVoiceInput && !mShowingVoiceSuggestions) {
+            mVoiceInput.flushAllTextModificationCounters();
+            // send this intent AFTER logging any prior aggregated edits.
+            mVoiceInput.logTextModifiedByChooseSuggestion(suggestion.length());
+        }
+
         InputConnection ic = getCurrentInputConnection();
         if (ic != null) {
             ic.beginBatchEdit();
diff --git a/java/src/com/android/inputmethod/voice/VoiceInput.java b/java/src/com/android/inputmethod/voice/VoiceInput.java
index e881856..72cb2f9 100644
--- a/java/src/com/android/inputmethod/voice/VoiceInput.java
+++ b/java/src/com/android/inputmethod/voice/VoiceInput.java
@@ -94,6 +94,12 @@
     public static final int WORKING = 2;
     public static final int ERROR = 3;
 
+    private int mAfterVoiceInputDeleteCount = 0;
+    private int mAfterVoiceInputInsertCount = 0;
+    private int mAfterVoiceInputInsertPunctuationCount = 0;
+    private int mAfterVoiceInputCursorPos = 0;
+    private int mAfterVoiceInputSelectionSpan = 0;
+
     private int mState = DEFAULT;
     
     private final static int MSG_CLOSE_ERROR_DIALOG = 1;
@@ -161,6 +167,87 @@
         mBlacklist.addApp("com.android.setupwizard");
     }
 
+    public void setCursorPos(int pos) {
+        mAfterVoiceInputCursorPos = pos;
+    }
+
+    public int getCursorPos() {
+        return mAfterVoiceInputCursorPos;
+    }
+
+    public void setSelectionSpan(int span) {
+        mAfterVoiceInputSelectionSpan = span;
+    }
+
+    public int getSelectionSpan() {
+        return mAfterVoiceInputSelectionSpan;
+    }
+
+    public void incrementTextModificationDeleteCount(int count){
+        mAfterVoiceInputDeleteCount += count;
+        // Send up intents for other text modification types
+        if (mAfterVoiceInputInsertCount > 0) {
+            logTextModifiedByTypingInsertion(mAfterVoiceInputInsertCount);
+            mAfterVoiceInputInsertCount = 0;
+        }
+        if (mAfterVoiceInputInsertPunctuationCount > 0) {
+            logTextModifiedByTypingInsertionPunctuation(mAfterVoiceInputInsertPunctuationCount);
+            mAfterVoiceInputInsertPunctuationCount = 0;
+        }
+
+    }
+
+    public void incrementTextModificationInsertCount(int count){
+        mAfterVoiceInputInsertCount += count;
+        if (mAfterVoiceInputSelectionSpan > 0) {
+            // If text was highlighted before inserting the char, count this as
+            // a delete.
+            mAfterVoiceInputDeleteCount += mAfterVoiceInputSelectionSpan;
+        }
+        // Send up intents for other text modification types
+        if (mAfterVoiceInputDeleteCount > 0) {
+            logTextModifiedByTypingDeletion(mAfterVoiceInputDeleteCount);
+            mAfterVoiceInputDeleteCount = 0;
+        }
+        if (mAfterVoiceInputInsertPunctuationCount > 0) {
+            logTextModifiedByTypingInsertionPunctuation(mAfterVoiceInputInsertPunctuationCount);
+            mAfterVoiceInputInsertPunctuationCount = 0;
+        }
+    }
+
+    public void incrementTextModificationInsertPunctuationCount(int count){
+        mAfterVoiceInputInsertPunctuationCount += 1;
+        if (mAfterVoiceInputSelectionSpan > 0) {
+            // If text was highlighted before inserting the char, count this as
+            // a delete.
+            mAfterVoiceInputDeleteCount += mAfterVoiceInputSelectionSpan;
+        }
+        // Send up intents for aggregated non-punctuation insertions
+        if (mAfterVoiceInputDeleteCount > 0) {
+            logTextModifiedByTypingDeletion(mAfterVoiceInputDeleteCount);
+            mAfterVoiceInputDeleteCount = 0;
+        }
+        if (mAfterVoiceInputInsertCount > 0) {
+            logTextModifiedByTypingInsertion(mAfterVoiceInputInsertCount);
+            mAfterVoiceInputInsertCount = 0;
+        }
+    }
+
+    public void flushAllTextModificationCounters() {
+        if (mAfterVoiceInputInsertCount > 0) {
+            logTextModifiedByTypingInsertion(mAfterVoiceInputInsertCount);
+            mAfterVoiceInputInsertCount = 0;
+        }
+        if (mAfterVoiceInputDeleteCount > 0) {
+            logTextModifiedByTypingDeletion(mAfterVoiceInputDeleteCount);
+            mAfterVoiceInputDeleteCount = 0;
+        }
+        if (mAfterVoiceInputInsertPunctuationCount > 0) {
+            logTextModifiedByTypingInsertionPunctuation(mAfterVoiceInputInsertPunctuationCount);
+            mAfterVoiceInputInsertPunctuationCount = 0;
+        }
+    }
+
     /**
      * The configuration of the IME changed and may have caused the views to be layed out
      * again. Restore the state of the recognition view.
@@ -302,8 +389,20 @@
         }
     }
 
-    public void logTextModified() {
-        mLogger.textModified();
+    public void logTextModifiedByTypingInsertion(int length) {
+        mLogger.textModifiedByTypingInsertion(length);
+    }
+
+    public void logTextModifiedByTypingInsertionPunctuation(int length) {
+        mLogger.textModifiedByTypingInsertionPunctuation(length);
+    }
+
+    public void logTextModifiedByTypingDeletion(int length) {
+        mLogger.textModifiedByTypingDeletion(length);
+    }
+
+    public void logTextModifiedByChooseSuggestion(int length) {
+        mLogger.textModifiedByChooseSuggestion(length);
     }
 
     public void logKeyboardWarningDialogShown() {
@@ -330,8 +429,8 @@
         mLogger.punctuationHintDisplayed();
     }
 
-    public void logVoiceInputDelivered() {
-        mLogger.voiceInputDelivered();
+    public void logVoiceInputDelivered(int length) {
+        mLogger.voiceInputDelivered(length);
     }
 
     public void logNBestChoose(int index) {
diff --git a/java/src/com/android/inputmethod/voice/VoiceInputLogger.java b/java/src/com/android/inputmethod/voice/VoiceInputLogger.java
index 6590333..9d3a920 100644
--- a/java/src/com/android/inputmethod/voice/VoiceInputLogger.java
+++ b/java/src/com/android/inputmethod/voice/VoiceInputLogger.java
@@ -147,12 +147,43 @@
         mContext.sendBroadcast(i);
     }
     
-    public void voiceInputDelivered() {
-        mContext.sendBroadcast(newLoggingBroadcast(LoggingEvents.VoiceIme.VOICE_INPUT_DELIVERED));
+    public void voiceInputDelivered(int length) {
+        Intent i = newLoggingBroadcast(LoggingEvents.VoiceIme.VOICE_INPUT_DELIVERED);
+        i.putExtra(LoggingEvents.VoiceIme.EXTRA_TEXT_MODIFIED_LENGTH, length);
+        mContext.sendBroadcast(i);
     }
 
-    public void textModified() {
-        mContext.sendBroadcast(newLoggingBroadcast(LoggingEvents.VoiceIme.TEXT_MODIFIED));
+    public void textModifiedByTypingInsertion(int length) {
+        Intent i = newLoggingBroadcast(LoggingEvents.VoiceIme.TEXT_MODIFIED);
+        i.putExtra(LoggingEvents.VoiceIme.EXTRA_TEXT_MODIFIED_LENGTH, length);
+        i.putExtra(LoggingEvents.VoiceIme.EXTRA_TEXT_MODIFIED_TYPE,
+                LoggingEvents.VoiceIme.TEXT_MODIFIED_TYPE_TYPING_INSERTION);
+        mContext.sendBroadcast(i);
+    }
+
+    public void textModifiedByTypingInsertionPunctuation(int length) {
+        Intent i = newLoggingBroadcast(LoggingEvents.VoiceIme.TEXT_MODIFIED);
+        i.putExtra(LoggingEvents.VoiceIme.EXTRA_TEXT_MODIFIED_LENGTH, length);
+        i.putExtra(LoggingEvents.VoiceIme.EXTRA_TEXT_MODIFIED_TYPE,
+                LoggingEvents.VoiceIme.TEXT_MODIFIED_TYPE_TYPING_INSERTION_PUNCTUATION);
+        mContext.sendBroadcast(i);
+    }
+
+    public void textModifiedByTypingDeletion(int length) {
+        Intent i = newLoggingBroadcast(LoggingEvents.VoiceIme.TEXT_MODIFIED);
+        i.putExtra(LoggingEvents.VoiceIme.EXTRA_TEXT_MODIFIED_LENGTH, length);
+        i.putExtra(LoggingEvents.VoiceIme.EXTRA_TEXT_MODIFIED_TYPE,
+                LoggingEvents.VoiceIme.TEXT_MODIFIED_TYPE_TYPING_DELETION);
+
+        mContext.sendBroadcast(i);
+    }
+
+    public void textModifiedByChooseSuggestion(int length) {
+        Intent i = newLoggingBroadcast(LoggingEvents.VoiceIme.TEXT_MODIFIED);
+        i.putExtra(LoggingEvents.VoiceIme.EXTRA_TEXT_MODIFIED_LENGTH, length);
+        i.putExtra(LoggingEvents.VoiceIme.EXTRA_TEXT_MODIFIED_TYPE,
+                LoggingEvents.VoiceIme.TEXT_MODIFIED_TYPE_CHOOSE_SUGGESTION);
+        mContext.sendBroadcast(i);
     }
 
     public void nBestChoose(int index) {