Shift+Smiley key will register newline character of textMultiLine

This change also
 * Honors the custom action label in EditorInfo.actionLabel.
 * Invokes InputConnection.performeEditorAction if action is specifed
   at EditorInfo.imeOptions or actionLabel/actionId.
 * Stops using InputMethodService.sendKeyChar. Implements
   sendKeyCodePoint instead.

Bug: 2498607
Bug: 5961809
Bug: 5368408
Change-Id: If4cd5eb3dacfc6b6a7ea434b0617c2438e06e42d
diff --git a/java/res/values/attrs.xml b/java/res/values/attrs.xml
index 86e3fe3..2dea8fb 100644
--- a/java/res/values/attrs.xml
+++ b/java/res/values/attrs.xml
@@ -232,10 +232,7 @@
              been replaced, those remaining entries are appended at the end of moreKeys. -->
         <attr name="additionalMoreKeys" format="string" />
         <!-- Maximum column of more keys keyboard -->
-        <attr name="maxMoreKeysColumn" format="integer">
-            <!-- This should be aligned with  -->
-            <flag name="fixedWidthMoreKeysColumn" value="0x8000000" />
-        </attr>
+        <attr name="maxMoreKeysColumn" format="integer" />
         <attr name="backgroundType" format="enum">
             <!-- This should be aligned with Key.BACKGROUND_TYPE_* -->
             <enum name="normal" value="0" />
@@ -281,10 +278,13 @@
             <!-- If true, use keyShiftedLetterHintActivatedColor for the shifted letter hint and
                  keyTextInactivatedColor for the primary key top label. -->
             <flag name="shiftedLetterActivated" value="0x10000" />
+            <!-- If true, use EditorInfo.actionLabel for the key label. -->
+            <flag name="fromCustomActionLabel" value="0x20000" />
         </attr>
         <!-- The icon to display on the key instead of the label. -->
         <attr name="keyIcon" format="enum">
             <!-- This should be aligned with the KeyboardIconsSet.ICON_* -->
+            <enum name="iconUndefined" value="0" />
             <enum name="iconShiftKey" value="1" />
             <enum name="iconDeleteKey" value="2" />
             <enum name="iconSettingsKey" value="3" />
@@ -368,6 +368,7 @@
         <attr name="clobberSettingsKey" format="boolean" />
         <attr name="shortcutKeyEnabled" format="boolean" />
         <attr name="hasShortcutKey" format="boolean" />
+        <attr name="isMultiLine" format="boolean" />
         <attr name="imeAction" format="enum">
             <!-- This should be aligned with EditorInfo.IME_ACTION_* -->
             <enum name="actionUnspecified" value="0" />
@@ -378,6 +379,8 @@
             <enum name="actionNext" value="5" />
             <enum name="actionDone" value="6" />
             <enum name="actionPrevious" value="7" />
+            <!--  This should be aligned with KeyboardId.IME_ACTION_* -->
+            <enum name="actionCustomLabel" value="0x100" />
         </attr>
         <attr name="localeCode" format="string" />
         <attr name="languageCode" format="string" />
diff --git a/java/res/values/keycodes.xml b/java/res/values/keycodes.xml
index 2a91a3d..d552b93 100644
--- a/java/res/values/keycodes.xml
+++ b/java/res/values/keycodes.xml
@@ -25,9 +25,10 @@
     <integer name="key_space">32</integer>
     <integer name="key_shift">-1</integer>
     <integer name="key_switch_alpha_symbol">-2</integer>
-    <integer name="key_output_text">-4</integer>
-    <integer name="key_delete">-5</integer>
-    <integer name="key_settings">-6</integer>
-    <integer name="key_shortcut">-7</integer>
+    <integer name="key_output_text">-3</integer>
+    <integer name="key_delete">-4</integer>
+    <integer name="key_settings">-5</integer>
+    <integer name="key_shortcut">-6</integer>
+    <integer name="key_action_enter">-7</integer>
     <integer name="key_unspecified">-9</integer>
 </resources>
diff --git a/java/res/values/strings.xml b/java/res/values/strings.xml
index d64059f..089acf3 100644
--- a/java/res/values/strings.xml
+++ b/java/res/values/strings.xml
@@ -128,6 +128,8 @@
     <string name="label_go_key">Go</string>
     <!-- Label for soft enter key when it performs NEXT action.  Must be short to fit on key! [CHAR LIMIT=5] -->
     <string name="label_next_key">Next</string>
+    <!-- Label for soft enter key when it performs PREVIOUS action.  Must be short to fit on key! [CHAR LIMIT=5] -->
+    <string name="label_previous_key">Prev</string>
     <!-- Label for soft enter key when it performs DONE action.  Must be short to fit on key! [CHAR LIMIT=5] -->
     <string name="label_done_key">Done</string>
     <!-- Label for soft enter key when it performs SEND action.  Must be short to fit on key! [CHAR LIMIT=5] -->
diff --git a/java/res/xml-sw600dp/key_styles_common.xml b/java/res/xml-sw600dp/key_styles_common.xml
index b554147..81334c9 100644
--- a/java/res/xml-sw600dp/key_styles_common.xml
+++ b/java/res/xml-sw600dp/key_styles_common.xml
@@ -89,12 +89,8 @@
         latin:keyIcon="iconDeleteKey"
         latin:keyActionFlags="isRepeatable|noKeyPreview"
         latin:backgroundType="functional" />
-    <key-style
-        latin:styleName="returnKeyStyle"
-        latin:code="@integer/key_return"
-        latin:keyIcon="iconReturnKey"
-        latin:keyActionFlags="noKeyPreview"
-        latin:backgroundType="functional" />
+    <include
+        latin:keyboardLayout="@xml/key_styles_enter_tablet" />
     <key-style
         latin:styleName="spaceKeyStyle"
         latin:code="@integer/key_space"
diff --git a/java/res/xml-sw600dp/row_qwerty2.xml b/java/res/xml-sw600dp/row_qwerty2.xml
index 52a948f..cabb9cb 100644
--- a/java/res/xml-sw600dp/row_qwerty2.xml
+++ b/java/res/xml-sw600dp/row_qwerty2.xml
@@ -50,7 +50,7 @@
             latin:keyLabel="l"
             latin:moreKeys="@string/more_keys_for_l" />
         <Key
-            latin:keyStyle="returnKeyStyle"
+            latin:keyStyle="enterKeyStyle"
             latin:keyXPos="-14.6%p"
             latin:keyWidth="fillBoth" />
     </Row>
diff --git a/java/res/xml-sw600dp/rows_arabic.xml b/java/res/xml-sw600dp/rows_arabic.xml
index 8d4901b..1f03968 100644
--- a/java/res/xml-sw600dp/rows_arabic.xml
+++ b/java/res/xml-sw600dp/rows_arabic.xml
@@ -138,7 +138,7 @@
         <Key
             latin:keyLabel="ط" />
         <Key
-            latin:keyStyle="returnKeyStyle"
+            latin:keyStyle="enterKeyStyle"
             latin:keyXPos="-14.6%p"
             latin:keyWidth="fillBoth" />
     </Row>
diff --git a/java/res/xml-sw600dp/rows_azerty.xml b/java/res/xml-sw600dp/rows_azerty.xml
index aabe6b0..5c79962 100644
--- a/java/res/xml-sw600dp/rows_azerty.xml
+++ b/java/res/xml-sw600dp/rows_azerty.xml
@@ -90,7 +90,7 @@
         <Key
             latin:keyLabel="m" />
         <Key
-            latin:keyStyle="returnKeyStyle"
+            latin:keyStyle="enterKeyStyle"
             latin:keyXPos="-14.6%p"
             latin:keyWidth="fillBoth" />
     </Row>
diff --git a/java/res/xml-sw600dp/rows_bulgarian.xml b/java/res/xml-sw600dp/rows_bulgarian.xml
index b464158..7a23ce9 100644
--- a/java/res/xml-sw600dp/rows_bulgarian.xml
+++ b/java/res/xml-sw600dp/rows_bulgarian.xml
@@ -80,7 +80,7 @@
         <Key
             latin:keyLabel="ь" />
         <Key
-            latin:keyStyle="returnKeyStyle"
+            latin:keyStyle="enterKeyStyle"
             latin:keyWidth="fillBoth" />
     </Row>
     <Row
diff --git a/java/res/xml-sw600dp/rows_hebrew.xml b/java/res/xml-sw600dp/rows_hebrew.xml
index 71fb463..812e2d6 100644
--- a/java/res/xml-sw600dp/rows_hebrew.xml
+++ b/java/res/xml-sw600dp/rows_hebrew.xml
@@ -106,7 +106,7 @@
             latin:keyLabel="ץ"
             latin:moreKeys="ץ׳" />
         <Key
-            latin:keyStyle="returnKeyStyle"
+            latin:keyStyle="enterKeyStyle"
             latin:keyXPos="-10.400%p"
             latin:keyWidth="fillBoth" />
     </Row>
diff --git a/java/res/xml-sw600dp/rows_number_normal.xml b/java/res/xml-sw600dp/rows_number_normal.xml
index f7eb950..3141bbd 100644
--- a/java/res/xml-sw600dp/rows_number_normal.xml
+++ b/java/res/xml-sw600dp/rows_number_normal.xml
@@ -78,7 +78,7 @@
             latin:keyLabel="6"
             latin:keyStyle="numKeyStyle" />
         <Key
-            latin:keyStyle="returnKeyStyle"
+            latin:keyStyle="enterKeyStyle"
             latin:keyXPos="-11.00%p"
             latin:keyWidth="fillRight" />
     </Row>
diff --git a/java/res/xml-sw600dp/rows_number_password.xml b/java/res/xml-sw600dp/rows_number_password.xml
index c3f21e1..0a71f74 100644
--- a/java/res/xml-sw600dp/rows_number_password.xml
+++ b/java/res/xml-sw600dp/rows_number_password.xml
@@ -47,7 +47,7 @@
         <Key
             latin:keyStyle="num6KeyStyle" />
         <Key
-            latin:keyStyle="returnKeyStyle"
+            latin:keyStyle="enterKeyStyle"
             latin:keyXPos="-11.00%p"
             latin:keyWidth="fillRight" />
     </Row>
diff --git a/java/res/xml-sw600dp/rows_phone.xml b/java/res/xml-sw600dp/rows_phone.xml
index fe85119..d61b4b2 100644
--- a/java/res/xml-sw600dp/rows_phone.xml
+++ b/java/res/xml-sw600dp/rows_phone.xml
@@ -75,7 +75,7 @@
         <Key
             latin:keyStyle="num6KeyStyle" />
         <Key
-            latin:keyStyle="returnKeyStyle"
+            latin:keyStyle="enterKeyStyle"
             latin:keyXPos="-11.00%p"
             latin:keyWidth="fillRight" />
     </Row>
diff --git a/java/res/xml-sw600dp/rows_scandinavian.xml b/java/res/xml-sw600dp/rows_scandinavian.xml
index 5ecb7d2..912c406 100644
--- a/java/res/xml-sw600dp/rows_scandinavian.xml
+++ b/java/res/xml-sw600dp/rows_scandinavian.xml
@@ -95,7 +95,7 @@
             latin:keyLabel="@string/keylabel_for_scandinavia_row2_11"
             latin:moreKeys="@string/more_keys_for_scandinavia_row2_11" />
         <Key
-            latin:keyStyle="returnKeyStyle"
+            latin:keyStyle="enterKeyStyle"
             latin:keyXPos="-14.6%p"
             latin:keyWidth="fillBoth" />
     </Row>
diff --git a/java/res/xml-sw600dp/rows_serbian.xml b/java/res/xml-sw600dp/rows_serbian.xml
index c21fd4c..ea4bb14 100644
--- a/java/res/xml-sw600dp/rows_serbian.xml
+++ b/java/res/xml-sw600dp/rows_serbian.xml
@@ -80,7 +80,7 @@
         <Key
             latin:keyLabel="ћ" />
         <Key
-            latin:keyStyle="returnKeyStyle"
+            latin:keyStyle="enterKeyStyle"
             latin:keyXPos="-14.6%p"
             latin:keyWidth="fillBoth" />
     </Row>
diff --git a/java/res/xml-sw600dp/rows_slavic.xml b/java/res/xml-sw600dp/rows_slavic.xml
index 889a438..020ea16 100644
--- a/java/res/xml-sw600dp/rows_slavic.xml
+++ b/java/res/xml-sw600dp/rows_slavic.xml
@@ -87,7 +87,7 @@
         <Key
             latin:keyLabel="э" />
         <Key
-            latin:keyStyle="returnKeyStyle"
+            latin:keyStyle="enterKeyStyle"
             latin:keyXPos="-14.6%p"
             latin:keyWidth="fillBoth" />
     </Row>
diff --git a/java/res/xml-sw600dp/rows_spanish.xml b/java/res/xml-sw600dp/rows_spanish.xml
index b516beb..2ab94e8 100644
--- a/java/res/xml-sw600dp/rows_spanish.xml
+++ b/java/res/xml-sw600dp/rows_spanish.xml
@@ -56,7 +56,7 @@
         <Key
             latin:keyLabel="ñ" />
         <Key
-            latin:keyStyle="returnKeyStyle"
+            latin:keyStyle="enterKeyStyle"
             latin:keyXPos="-14.6%p"
             latin:keyWidth="fillBoth" />
     </Row>
diff --git a/java/res/xml-sw600dp/rows_symbols.xml b/java/res/xml-sw600dp/rows_symbols.xml
index c2dfe2f..ce6e539 100644
--- a/java/res/xml-sw600dp/rows_symbols.xml
+++ b/java/res/xml-sw600dp/rows_symbols.xml
@@ -98,7 +98,7 @@
         <include
             latin:keyboardLayout="@xml/keys_parentheses" />
         <Key
-            latin:keyStyle="returnKeyStyle"
+            latin:keyStyle="enterKeyStyle"
             latin:keyXPos="-14.6%p"
             latin:keyWidth="fillBoth" />
     </Row>
diff --git a/java/res/xml-sw600dp/rows_symbols_shift.xml b/java/res/xml-sw600dp/rows_symbols_shift.xml
index 6a640c0..a10d174 100644
--- a/java/res/xml-sw600dp/rows_symbols_shift.xml
+++ b/java/res/xml-sw600dp/rows_symbols_shift.xml
@@ -80,7 +80,7 @@
         <include
             latin:keyboardLayout="@xml/keys_curly_brackets" />
         <Key
-            latin:keyStyle="returnKeyStyle"
+            latin:keyStyle="enterKeyStyle"
             latin:keyXPos="-14.6%p"
             latin:keyWidth="fillBoth" />
     </Row>
diff --git a/java/res/xml-sw768dp/key_styles_common.xml b/java/res/xml-sw768dp/key_styles_common.xml
index 0d2ac5d..f01f349 100644
--- a/java/res/xml-sw768dp/key_styles_common.xml
+++ b/java/res/xml-sw768dp/key_styles_common.xml
@@ -71,12 +71,8 @@
         latin:keyIcon="iconDeleteKey"
         latin:keyActionFlags="isRepeatable|noKeyPreview"
         latin:backgroundType="functional" />
-    <key-style
-        latin:styleName="returnKeyStyle"
-        latin:code="@integer/key_return"
-        latin:keyIcon="iconReturnKey"
-        latin:keyActionFlags="noKeyPreview"
-        latin:backgroundType="functional" />
+    <include
+        latin:keyboardLayout="@xml/key_styles_enter_tablet" />
     <key-style
         latin:styleName="spaceKeyStyle"
         latin:code="@integer/key_space"
diff --git a/java/res/xml-sw768dp/row_qwerty2.xml b/java/res/xml-sw768dp/row_qwerty2.xml
index 1129ecd..d348041 100644
--- a/java/res/xml-sw768dp/row_qwerty2.xml
+++ b/java/res/xml-sw768dp/row_qwerty2.xml
@@ -53,7 +53,7 @@
             latin:keyLabel="l"
             latin:moreKeys="@string/more_keys_for_l" />
         <Key
-            latin:keyStyle="returnKeyStyle"
+            latin:keyStyle="enterKeyStyle"
             latin:keyXPos="-15.704%p"
             latin:keyWidth="fillBoth" />
     </Row>
diff --git a/java/res/xml-sw768dp/rows_arabic.xml b/java/res/xml-sw768dp/rows_arabic.xml
index 0e4aee4..baced66 100644
--- a/java/res/xml-sw768dp/rows_arabic.xml
+++ b/java/res/xml-sw768dp/rows_arabic.xml
@@ -144,7 +144,7 @@
         <Key
             latin:keyLabel="ط" />
         <Key
-            latin:keyStyle="returnKeyStyle"
+            latin:keyStyle="enterKeyStyle"
             latin:keyXPos="-9.375%p"
             latin:keyWidth="fillBoth" />
     </Row>
diff --git a/java/res/xml-sw768dp/rows_azerty.xml b/java/res/xml-sw768dp/rows_azerty.xml
index b9ef898..6023e98 100644
--- a/java/res/xml-sw768dp/rows_azerty.xml
+++ b/java/res/xml-sw768dp/rows_azerty.xml
@@ -97,7 +97,7 @@
         <Key
             latin:keyLabel="m" />
         <Key
-            latin:keyStyle="returnKeyStyle"
+            latin:keyStyle="enterKeyStyle"
             latin:keyXPos="-15.704%p"
             latin:keyWidth="fillBoth" />
     </Row>
diff --git a/java/res/xml-sw768dp/rows_bulgarian.xml b/java/res/xml-sw768dp/rows_bulgarian.xml
index 57f39b4..d67a0d1 100644
--- a/java/res/xml-sw768dp/rows_bulgarian.xml
+++ b/java/res/xml-sw768dp/rows_bulgarian.xml
@@ -86,7 +86,7 @@
         <Key
             latin:keyLabel="ь" />
         <Key
-            latin:keyStyle="returnKeyStyle"
+            latin:keyStyle="enterKeyStyle"
             latin:keyWidth="fillBoth" />
     </Row>
     <Row
diff --git a/java/res/xml-sw768dp/rows_hebrew.xml b/java/res/xml-sw768dp/rows_hebrew.xml
index 755afc8..61c5eae 100644
--- a/java/res/xml-sw768dp/rows_hebrew.xml
+++ b/java/res/xml-sw768dp/rows_hebrew.xml
@@ -111,7 +111,7 @@
             latin:keyLabel="ץ"
             latin:moreKeys="ץ׳" />
         <Key
-            latin:keyStyle="returnKeyStyle"
+            latin:keyStyle="enterKeyStyle"
             latin:keyXPos="-10.400%p"
             latin:keyWidth="fillBoth" />
     </Row>
diff --git a/java/res/xml-sw768dp/rows_number_normal.xml b/java/res/xml-sw768dp/rows_number_normal.xml
index 0e80e80..cf947ab 100644
--- a/java/res/xml-sw768dp/rows_number_normal.xml
+++ b/java/res/xml-sw768dp/rows_number_normal.xml
@@ -80,7 +80,7 @@
             latin:keyLabel="6"
             latin:keyStyle="numKeyStyle" />
         <Key
-            latin:keyStyle="returnKeyStyle"
+            latin:keyStyle="enterKeyStyle"
             latin:keyXPos="-11.172%p"
             latin:keyWidth="fillRight" />
     </Row>
diff --git a/java/res/xml-sw768dp/rows_number_password.xml b/java/res/xml-sw768dp/rows_number_password.xml
index 77fb9ca..8acfac6 100644
--- a/java/res/xml-sw768dp/rows_number_password.xml
+++ b/java/res/xml-sw768dp/rows_number_password.xml
@@ -49,7 +49,7 @@
         <Key
             latin:keyStyle="num6KeyStyle" />
         <Key
-            latin:keyStyle="returnKeyStyle"
+            latin:keyStyle="enterKeyStyle"
             latin:keyXPos="-11.172%p"
             latin:keyWidth="fillRight" />
     </Row>
diff --git a/java/res/xml-sw768dp/rows_phone.xml b/java/res/xml-sw768dp/rows_phone.xml
index 789c02c..0404bb1 100644
--- a/java/res/xml-sw768dp/rows_phone.xml
+++ b/java/res/xml-sw768dp/rows_phone.xml
@@ -78,7 +78,7 @@
         <Key
             latin:keyStyle="num6KeyStyle" />
         <Key
-            latin:keyStyle="returnKeyStyle"
+            latin:keyStyle="enterKeyStyle"
             latin:keyXPos="-11.172%p"
             latin:keyWidth="fillRight" />
     </Row>
diff --git a/java/res/xml-sw768dp/rows_scandinavian.xml b/java/res/xml-sw768dp/rows_scandinavian.xml
index 9e5ad14..4373166 100644
--- a/java/res/xml-sw768dp/rows_scandinavian.xml
+++ b/java/res/xml-sw768dp/rows_scandinavian.xml
@@ -102,7 +102,7 @@
             latin:keyLabel="@string/keylabel_for_scandinavia_row2_11"
             latin:moreKeys="@string/more_keys_for_scandinavia_row2_11" />
         <Key
-            latin:keyStyle="returnKeyStyle"
+            latin:keyStyle="enterKeyStyle"
             latin:keyXPos="-9.375%p"
             latin:keyWidth="fillBoth" />
     </Row>
diff --git a/java/res/xml-sw768dp/rows_serbian.xml b/java/res/xml-sw768dp/rows_serbian.xml
index 2e9e140..6659755 100644
--- a/java/res/xml-sw768dp/rows_serbian.xml
+++ b/java/res/xml-sw768dp/rows_serbian.xml
@@ -114,7 +114,7 @@
         <include
             latin:keyboardLayout="@xml/keys_comma_period" />
         <Key
-            latin:keyStyle="returnKeyStyle"
+            latin:keyStyle="enterKeyStyle"
             latin:keyXPos="-13.750%p"
             latin:keyWidth="fillRight" />
     </Row>
diff --git a/java/res/xml-sw768dp/rows_slavic.xml b/java/res/xml-sw768dp/rows_slavic.xml
index 86b4498..58d5a75 100644
--- a/java/res/xml-sw768dp/rows_slavic.xml
+++ b/java/res/xml-sw768dp/rows_slavic.xml
@@ -92,7 +92,7 @@
         <Key
             latin:keyLabel="э" />
         <Key
-            latin:keyStyle="returnKeyStyle"
+            latin:keyStyle="enterKeyStyle"
             latin:keyXPos="-9.375%p"
             latin:keyWidth="fillBoth" />
     </Row>
diff --git a/java/res/xml-sw768dp/rows_spanish.xml b/java/res/xml-sw768dp/rows_spanish.xml
index f626a45..864c435 100644
--- a/java/res/xml-sw768dp/rows_spanish.xml
+++ b/java/res/xml-sw768dp/rows_spanish.xml
@@ -59,7 +59,7 @@
         <Key
             latin:keyLabel="ñ" />
         <Key
-            latin:keyStyle="returnKeyStyle"
+            latin:keyStyle="enterKeyStyle"
             latin:keyXPos="-15.704%p"
             latin:keyWidth="fillBoth" />
     </Row>
diff --git a/java/res/xml-sw768dp/rows_symbols.xml b/java/res/xml-sw768dp/rows_symbols.xml
index 2ba9357..c199ae4 100644
--- a/java/res/xml-sw768dp/rows_symbols.xml
+++ b/java/res/xml-sw768dp/rows_symbols.xml
@@ -105,7 +105,7 @@
         <include
             latin:keyboardLayout="@xml/keys_parentheses" />
         <Key
-            latin:keyStyle="returnKeyStyle"
+            latin:keyStyle="enterKeyStyle"
             latin:keyXPos="-15.704%p"
             latin:keyWidth="fillBoth" />
     </Row>
diff --git a/java/res/xml-sw768dp/rows_symbols_shift.xml b/java/res/xml-sw768dp/rows_symbols_shift.xml
index aba9c23..e88f786 100644
--- a/java/res/xml-sw768dp/rows_symbols_shift.xml
+++ b/java/res/xml-sw768dp/rows_symbols_shift.xml
@@ -87,7 +87,7 @@
         <include
             latin:keyboardLayout="@xml/keys_curly_brackets" />
         <Key
-            latin:keyStyle="returnKeyStyle"
+            latin:keyStyle="enterKeyStyle"
             latin:keyXPos="-15.704%p"
             latin:keyWidth="fillBoth" />
     </Row>
diff --git a/java/res/xml/key_styles_common.xml b/java/res/xml/key_styles_common.xml
index 177a62c..97e06df 100644
--- a/java/res/xml/key_styles_common.xml
+++ b/java/res/xml/key_styles_common.xml
@@ -98,99 +98,8 @@
         latin:keyIcon="iconDeleteKey"
         latin:keyActionFlags="isRepeatable|noKeyPreview"
         latin:backgroundType="functional" />
-    <!-- Return key style -->
-    <key-style
-        latin:styleName="defaultReturnKeyStyle"
-        latin:code="@integer/key_return"
-        latin:keyIcon="iconReturnKey"
-        latin:keyActionFlags="noKeyPreview"
-        latin:backgroundType="functional" />
-    <switch>
-        <case
-            latin:mode="im"
-        >
-            <!-- Smiley key. -->
-            <switch>
-                <case
-                    latin:keyboardSetElement="alphabetManualShifted|alphabetShiftLockShifted"
-                >
-                    <key-style
-                        latin:styleName="returnKeyStyle"
-                        latin:parentStyle="defaultReturnKeyStyle" />
-                </case>
-                <default>
-                    <key-style
-                        latin:styleName="returnKeyStyle"
-                        latin:keyLabel=":-)"
-                        latin:keyOutputText=":-) "
-                        latin:keyLabelFlags="hasPopupHint|preserveCase"
-                        latin:moreKeys="@string/more_keys_for_smiley"
-                        latin:maxMoreKeysColumn="5"
-                        latin:backgroundType="functional" />
-                </default>
-            </switch>
-        </case>
-        <case
-            latin:imeAction="actionGo"
-        >
-            <key-style
-                latin:styleName="returnKeyStyle"
-                latin:code="@integer/key_return"
-                latin:keyLabel="@string/label_go_key"
-                latin:keyLabelFlags="autoXScale|preserveCase"
-                latin:keyActionFlags="noKeyPreview"
-                latin:backgroundType="action" />
-        </case>
-        <case
-            latin:imeAction="actionNext"
-        >
-            <key-style
-                latin:styleName="returnKeyStyle"
-                latin:code="@integer/key_return"
-                latin:keyLabel="@string/label_next_key"
-                latin:keyLabelFlags="autoXScale|preserveCase"
-                latin:keyActionFlags="noKeyPreview"
-                latin:backgroundType="action" />
-        </case>
-        <case
-            latin:imeAction="actionDone"
-        >
-            <key-style
-                latin:styleName="returnKeyStyle"
-                latin:code="@integer/key_return"
-                latin:keyLabel="@string/label_done_key"
-                latin:keyLabelFlags="autoXScale|preserveCase"
-                latin:keyActionFlags="noKeyPreview"
-                latin:backgroundType="action" />
-        </case>
-        <case
-            latin:imeAction="actionSend"
-        >
-            <key-style
-                latin:styleName="returnKeyStyle"
-                latin:code="@integer/key_return"
-                latin:keyLabel="@string/label_send_key"
-                latin:keyLabelFlags="autoXScale|preserveCase"
-                latin:keyActionFlags="noKeyPreview"
-                latin:backgroundType="action" />
-        </case>
-        <case
-            latin:imeAction="actionSearch"
-        >
-            <key-style
-                latin:styleName="returnKeyStyle"
-                latin:code="@integer/key_return"
-                latin:keyIcon="iconSearchKey"
-                latin:keyLabelFlags="autoXScale"
-                latin:keyActionFlags="noKeyPreview"
-                latin:backgroundType="action" />
-        </case>
-        <default>
-            <key-style
-                latin:styleName="returnKeyStyle"
-                latin:parentStyle="defaultReturnKeyStyle" />
-        </default>
-    </switch>
+    <include
+        latin:keyboardLayout="@xml/key_styles_enter_phone" />
     <key-style
         latin:styleName="spaceKeyStyle"
         latin:code="@integer/key_space"
diff --git a/java/res/xml/key_styles_enter_phone.xml b/java/res/xml/key_styles_enter_phone.xml
new file mode 100644
index 0000000..6d94334
--- /dev/null
+++ b/java/res/xml/key_styles_enter_phone.xml
@@ -0,0 +1,124 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2012, 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.
+*/
+-->
+
+<merge
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+>
+    <!-- Enter key style -->
+    <key-style
+        latin:styleName="defaultEnterKeyStyle"
+        latin:code="@integer/key_return"
+        latin:keyIcon="iconReturnKey"
+        latin:keyLabelFlags="autoXScale|preserveCase"
+        latin:keyActionFlags="noKeyPreview"
+        latin:backgroundType="functional" />
+    <key-style
+        latin:styleName="defaultActionKeyStyle"
+        latin:code="@integer/key_action_enter"
+        latin:keyIcon="iconUndefined"
+        latin:backgroundType="action"
+        latin:parentStyle="defaultEnterKeyStyle" />
+    <switch>
+        <!-- Shift + Enter in textMultiLine field. -->
+        <case
+            latin:isMultiLine="true"
+            latin:keyboardSetElement="alphabetManualShifted|alphabetShiftLockShifted"
+        >
+            <key-style
+                latin:styleName="enterKeyStyle"
+                latin:parentStyle="defaultEnterKeyStyle" />
+        </case>
+        <!-- Smiley in textShortMessage field. -->
+        <case
+            latin:mode="im"
+        >
+            <key-style
+                latin:styleName="enterKeyStyle"
+                latin:keyLabel=":-)"
+                latin:keyOutputText=":-) "
+                latin:keyLabelFlags="hasPopupHint"
+                latin:moreKeys="@string/more_keys_for_smiley"
+                latin:maxMoreKeysColumn="5"
+                latin:backgroundType="functional" />
+        </case>
+        <case
+            latin:imeAction="actionGo"
+        >
+            <key-style
+                latin:styleName="enterKeyStyle"
+                latin:keyLabel="@string/label_go_key"
+                latin:parentStyle="defaultActionKeyStyle" />
+        </case>
+        <case
+            latin:imeAction="actionNext"
+        >
+            <key-style
+                latin:styleName="enterKeyStyle"
+                latin:keyLabel="@string/label_next_key"
+                latin:parentStyle="defaultActionKeyStyle" />
+        </case>
+        <case
+            latin:imeAction="actionPrevious"
+        >
+            <key-style
+                latin:styleName="enterKeyStyle"
+                latin:keyLabel="@string/label_previous_key"
+                latin:parentStyle="defaultActionKeyStyle" />
+        </case>
+        <case
+            latin:imeAction="actionDone"
+        >
+            <key-style
+                latin:styleName="enterKeyStyle"
+                latin:keyLabel="@string/label_done_key"
+                latin:parentStyle="defaultActionKeyStyle" />
+        </case>
+        <case
+            latin:imeAction="actionSend"
+        >
+            <key-style
+                latin:styleName="enterKeyStyle"
+                latin:keyLabel="@string/label_send_key"
+                latin:parentStyle="defaultActionKeyStyle" />
+        </case>
+        <case
+            latin:imeAction="actionSearch"
+        >
+            <key-style
+                latin:styleName="enterKeyStyle"
+                latin:keyIcon="iconSearchKey"
+                latin:parentStyle="defaultActionKeyStyle" />
+        </case>
+        <case
+            latin:imeAction="actionCustomLabel"
+        >
+            <key-style
+                latin:styleName="enterKeyStyle"
+                latin:keyLabelFlags="fromCustomActionLabel"
+                latin:parentStyle="defaultActionKeyStyle" />
+        </case>
+        <!-- imeAction is either actionNone or actionUnspecified. -->
+        <default>
+            <key-style
+                latin:styleName="enterKeyStyle"
+                latin:parentStyle="defaultEnterKeyStyle" />
+        </default>
+    </switch>
+</merge>
diff --git a/java/res/xml/key_styles_enter_tablet.xml b/java/res/xml/key_styles_enter_tablet.xml
new file mode 100644
index 0000000..e54de29
--- /dev/null
+++ b/java/res/xml/key_styles_enter_tablet.xml
@@ -0,0 +1,111 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2012, 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.
+*/
+-->
+
+<merge
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+>
+    <!-- Enter key style -->
+    <key-style
+        latin:styleName="defaultEnterKeyStyle"
+        latin:code="@integer/key_return"
+        latin:keyIcon="iconReturnKey"
+        latin:keyLabelFlags="autoXScale|preserveCase"
+        latin:keyActionFlags="noKeyPreview"
+        latin:backgroundType="functional" />
+    <key-style
+        latin:styleName="defaultActionKeyStyle"
+        latin:code="@integer/key_action_enter"
+        latin:keyIcon="iconUndefined"
+        latin:backgroundType="action"
+        latin:parentStyle="defaultEnterKeyStyle" />
+    <switch>
+        <!-- Shift + Enter in textMultiLine field. -->
+        <case
+            latin:isMultiLine="true"
+            latin:keyboardSetElement="alphabetManualShifted|alphabetShiftLockShifted"
+        >
+            <key-style
+                latin:styleName="enterKeyStyle"
+                latin:parentStyle="defaultEnterKeyStyle" />
+        </case>
+        <case
+            latin:imeAction="actionGo"
+        >
+            <key-style
+                latin:styleName="enterKeyStyle"
+                latin:keyLabel="@string/label_go_key"
+                latin:parentStyle="defaultActionKeyStyle" />
+        </case>
+        <case
+            latin:imeAction="actionNext"
+        >
+            <key-style
+                latin:styleName="enterKeyStyle"
+                latin:keyLabel="@string/label_next_key"
+                latin:parentStyle="defaultActionKeyStyle" />
+        </case>
+        <case
+            latin:imeAction="actionPrevious"
+        >
+            <key-style
+                latin:styleName="enterKeyStyle"
+                latin:keyLabel="@string/label_previous_key"
+                latin:parentStyle="defaultActionKeyStyle" />
+        </case>
+        <case
+            latin:imeAction="actionDone"
+        >
+            <key-style
+                latin:styleName="enterKeyStyle"
+                latin:keyLabel="@string/label_done_key"
+                latin:parentStyle="defaultActionKeyStyle" />
+        </case>
+        <case
+            latin:imeAction="actionSend"
+        >
+            <key-style
+                latin:styleName="enterKeyStyle"
+                latin:keyLabel="@string/label_send_key"
+                latin:parentStyle="defaultActionKeyStyle" />
+        </case>
+        <case
+            latin:imeAction="actionSearch"
+        >
+            <key-style
+                latin:styleName="enterKeyStyle"
+                latin:keyIcon="iconSearchKey"
+                latin:parentStyle="defaultActionKeyStyle" />
+        </case>
+        <case
+            latin:imeAction="actionCustomLabel"
+        >
+            <key-style
+                latin:styleName="enterKeyStyle"
+                latin:keyLabelFlags="fromCustomActionLabel"
+                latin:parentStyle="defaultActionKeyStyle" />
+        </case>
+        <!-- imeAction is either actionNone or actionUnspecified. -->
+        <default>
+            <key-style
+                latin:styleName="enterKeyStyle"
+                latin:parentStyle="defaultEnterKeyStyle" />
+        </default>
+    </switch>
+</merge>
diff --git a/java/res/xml/row_qwerty4.xml b/java/res/xml/row_qwerty4.xml
index 65500a6..8c20a72 100644
--- a/java/res/xml/row_qwerty4.xml
+++ b/java/res/xml/row_qwerty4.xml
@@ -40,7 +40,7 @@
                 <Key
                     latin:keyStyle="punctuationKeyStyle" />
                 <Key
-                    latin:keyStyle="returnKeyStyle"
+                    latin:keyStyle="enterKeyStyle"
                     latin:keyWidth="fillRight" />
             </case>
             <!-- hasSettingsKey="true" or navigateAction="true" -->
@@ -59,7 +59,7 @@
                     latin:keyStyle="punctuationKeyStyle"
                     latin:keyWidth="9.2%p" />
                 <Key
-                    latin:keyStyle="returnKeyStyle"
+                    latin:keyStyle="enterKeyStyle"
                     latin:keyWidth="fillRight" />
             </default>
         </switch>
diff --git a/java/res/xml/row_symbols4.xml b/java/res/xml/row_symbols4.xml
index 8b094dd..be0c94f 100644
--- a/java/res/xml/row_symbols4.xml
+++ b/java/res/xml/row_symbols4.xml
@@ -40,7 +40,7 @@
                 <Key
                     latin:keyStyle="punctuationKeyStyle" />
                 <Key
-                    latin:keyStyle="returnKeyStyle"
+                    latin:keyStyle="enterKeyStyle"
                     latin:keyWidth="fillRight" />
             </case>
             <!-- hasSettingsKey="true" or navigateAction="true" -->
@@ -59,7 +59,7 @@
                     latin:keyStyle="punctuationKeyStyle"
                     latin:keyWidth="9.2%p" />
                 <Key
-                    latin:keyStyle="returnKeyStyle"
+                    latin:keyStyle="enterKeyStyle"
                     latin:keyWidth="fillRight" />
             </default>
         </switch>
diff --git a/java/res/xml/row_symbols_shift4.xml b/java/res/xml/row_symbols_shift4.xml
index 4e13ac7..dd13b71 100644
--- a/java/res/xml/row_symbols_shift4.xml
+++ b/java/res/xml/row_symbols_shift4.xml
@@ -45,7 +45,7 @@
                     latin:keyLabel="…"
                     latin:backgroundType="functional" />
                 <Key
-                    latin:keyStyle="returnKeyStyle"
+                    latin:keyStyle="enterKeyStyle"
                     latin:keyWidth="fillRight" />
             </case>
             <!-- hasSettingsKey="true" or navigateAction="true" -->
@@ -70,7 +70,7 @@
                     latin:keyWidth="9.2%p"
                     latin:backgroundType="functional" />
                 <Key
-                    latin:keyStyle="returnKeyStyle"
+                    latin:keyStyle="enterKeyStyle"
                     latin:keyWidth="fillRight" />
             </default>
         </switch>
diff --git a/java/res/xml/rows_number_normal.xml b/java/res/xml/rows_number_normal.xml
index 054b564..b581fb5 100644
--- a/java/res/xml/rows_number_normal.xml
+++ b/java/res/xml/rows_number_normal.xml
@@ -75,7 +75,7 @@
             latin:keyLabel="."
             latin:keyStyle="numKeyStyle" />
         <Key
-            latin:keyStyle="returnKeyStyle"
+            latin:keyStyle="enterKeyStyle"
             latin:keyWidth="fillRight" />
     </Row>
 </merge>
diff --git a/java/res/xml/rows_number_password.xml b/java/res/xml/rows_number_password.xml
index ebc13c6..e4272ed 100644
--- a/java/res/xml/rows_number_password.xml
+++ b/java/res/xml/rows_number_password.xml
@@ -56,7 +56,7 @@
             latin:keyStyle="num0KeyStyle" />
         <Spacer />
         <Key
-            latin:keyStyle="returnKeyStyle"
+            latin:keyStyle="enterKeyStyle"
             latin:keyWidth="fillRight" />
     </Row>
 </merge>
diff --git a/java/res/xml/rows_phone.xml b/java/res/xml/rows_phone.xml
index 18e4c9d..60296d0 100644
--- a/java/res/xml/rows_phone.xml
+++ b/java/res/xml/rows_phone.xml
@@ -68,7 +68,7 @@
         <Key
             latin:keyStyle="numSpaceKeyStyle" />
         <Key
-            latin:keyStyle="returnKeyStyle"
+            latin:keyStyle="enterKeyStyle"
             latin:keyWidth="fillRight" />
     </Row>
 </merge>
diff --git a/java/res/xml/rows_phone_symbols.xml b/java/res/xml/rows_phone_symbols.xml
index dfa1349..7841c56 100644
--- a/java/res/xml/rows_phone_symbols.xml
+++ b/java/res/xml/rows_phone_symbols.xml
@@ -78,7 +78,7 @@
         <Key
             latin:keyStyle="numSpaceKeyStyle" />
         <Key
-            latin:keyStyle="returnKeyStyle"
+            latin:keyStyle="enterKeyStyle"
             latin:keyWidth="fillRight" />
     </Row>
 </merge>
diff --git a/java/src/com/android/inputmethod/compat/EditorInfoCompatUtils.java b/java/src/com/android/inputmethod/compat/EditorInfoCompatUtils.java
index e1db360..3247997 100644
--- a/java/src/com/android/inputmethod/compat/EditorInfoCompatUtils.java
+++ b/java/src/com/android/inputmethod/compat/EditorInfoCompatUtils.java
@@ -67,42 +67,34 @@
         ic.performEditorAction(OBJ_IME_ACTION_PREVIOUS);
     }
 
-    public static String imeOptionsName(int imeOptions) {
-        if (imeOptions == -1)
-            return null;
+    public static String imeActionName(int imeOptions) {
         final int actionId = imeOptions & EditorInfo.IME_MASK_ACTION;
-        final String action;
         switch (actionId) {
-            case EditorInfo.IME_ACTION_UNSPECIFIED:
-                action = "actionUnspecified";
-                break;
-            case EditorInfo.IME_ACTION_NONE:
-                action = "actionNone";
-                break;
-            case EditorInfo.IME_ACTION_GO:
-                action = "actionGo";
-                break;
-            case EditorInfo.IME_ACTION_SEARCH:
-                action = "actionSearch";
-                break;
-            case EditorInfo.IME_ACTION_SEND:
-                action = "actionSend";
-                break;
-            case EditorInfo.IME_ACTION_NEXT:
-                action = "actionNext";
-                break;
-            case EditorInfo.IME_ACTION_DONE:
-                action = "actionDone";
-                break;
-            default: {
-                if (OBJ_IME_ACTION_PREVIOUS != null && actionId == OBJ_IME_ACTION_PREVIOUS) {
-                    action = "actionPrevious";
-                } else {
-                    action = "actionUnknown(" + actionId + ")";
-                }
-                break;
+        case EditorInfo.IME_ACTION_UNSPECIFIED:
+            return "actionUnspecified";
+        case EditorInfo.IME_ACTION_NONE:
+            return "actionNone";
+        case EditorInfo.IME_ACTION_GO:
+            return "actionGo";
+        case EditorInfo.IME_ACTION_SEARCH:
+            return "actionSearch";
+        case EditorInfo.IME_ACTION_SEND:
+            return "actionSend";
+        case EditorInfo.IME_ACTION_NEXT:
+            return "actionNext";
+        case EditorInfo.IME_ACTION_DONE:
+            return "actionDone";
+        default:
+            if (OBJ_IME_ACTION_PREVIOUS != null && actionId == OBJ_IME_ACTION_PREVIOUS) {
+                return "actionPrevious";
+            } else {
+                return "actionUnknown(" + actionId + ")";
             }
         }
+    }
+
+    public static String imeOptionsName(int imeOptions) {
+        final String action = imeActionName(imeOptions);
         final StringBuilder flags = new StringBuilder();
         if ((imeOptions & EditorInfo.IME_FLAG_NO_ENTER_ACTION) != 0) {
             flags.append("flagNoEnterAction|");
@@ -116,6 +108,6 @@
         if (hasFlagForceAscii(imeOptions)) {
             flags.append("flagForceAscii|");
         }
-        return flags + action;
+        return (action != null) ? flags + action : flags.toString();
     }
 }
diff --git a/java/src/com/android/inputmethod/keyboard/Key.java b/java/src/com/android/inputmethod/keyboard/Key.java
index 5656c38..b90d45d 100644
--- a/java/src/com/android/inputmethod/keyboard/Key.java
+++ b/java/src/com/android/inputmethod/keyboard/Key.java
@@ -72,6 +72,7 @@
     private static final int LABEL_FLAGS_AUTO_X_SCALE = 0x4000;
     private static final int LABEL_FLAGS_PRESERVE_CASE = 0x8000;
     private static final int LABEL_FLAGS_SHIFTED_LETTER_ACTIVATED = 0x10000;
+    private static final int LABEL_FLAGS_FROM_CUSTOM_ACTION_LABEL = 0x20000;
 
     /** Icon to display instead of a label. Icon takes precedence over a label */
     private final int mIconId;
@@ -247,14 +248,18 @@
         mMaxMoreKeysColumn = style.getInt(keyAttr,
                 R.styleable.Keyboard_Key_maxMoreKeysColumn, params.mMaxMoreKeysKeyboardColumn);
 
-        mLabel = adjustCaseOfStringForKeyboardId(style.getString(keyAttr,
-                R.styleable.Keyboard_Key_keyLabel), preserveCase, params.mId);
-        mHintLabel = adjustCaseOfStringForKeyboardId(style.getString(keyAttr,
-                R.styleable.Keyboard_Key_keyHintLabel), preserveCase, params.mId);
-        String outputText = adjustCaseOfStringForKeyboardId(style.getString(keyAttr,
-                R.styleable.Keyboard_Key_keyOutputText), preserveCase, params.mId);
-        final int code = style.getInt(keyAttr,
-                R.styleable.Keyboard_Key_code, Keyboard.CODE_UNSPECIFIED);
+        if ((mLabelFlags & LABEL_FLAGS_FROM_CUSTOM_ACTION_LABEL) != 0) {
+            mLabel = params.mId.mCustomActionLabel;
+        } else {
+            mLabel = adjustCaseOfStringForKeyboardId(style.getString(
+                    keyAttr, R.styleable.Keyboard_Key_keyLabel), preserveCase, params.mId);
+        }
+        mHintLabel = adjustCaseOfStringForKeyboardId(style.getString(
+                keyAttr, R.styleable.Keyboard_Key_keyHintLabel), preserveCase, params.mId);
+        String outputText = adjustCaseOfStringForKeyboardId(style.getString(
+                keyAttr, R.styleable.Keyboard_Key_keyOutputText), preserveCase, params.mId);
+        final int code = style.getInt(
+                keyAttr, R.styleable.Keyboard_Key_code, Keyboard.CODE_UNSPECIFIED);
         // Choose the first letter of the label as primary code if not specified.
         if (code == Keyboard.CODE_UNSPECIFIED && TextUtils.isEmpty(outputText)
                 && !TextUtils.isEmpty(mLabel)) {
diff --git a/java/src/com/android/inputmethod/keyboard/Keyboard.java b/java/src/com/android/inputmethod/keyboard/Keyboard.java
index cb6a545..28f71f4 100644
--- a/java/src/com/android/inputmethod/keyboard/Keyboard.java
+++ b/java/src/com/android/inputmethod/keyboard/Keyboard.java
@@ -28,7 +28,6 @@
 import android.util.Xml;
 import android.view.InflateException;
 
-import com.android.inputmethod.compat.EditorInfoCompatUtils;
 import com.android.inputmethod.keyboard.internal.KeyStyles;
 import com.android.inputmethod.keyboard.internal.KeyboardIconsSet;
 import com.android.inputmethod.latin.LatinImeLogger;
@@ -95,10 +94,11 @@
      */
     public static final int CODE_SHIFT = -1;
     public static final int CODE_SWITCH_ALPHA_SYMBOL = -2;
-    public static final int CODE_OUTPUT_TEXT = -4;
-    public static final int CODE_DELETE = -5;
-    public static final int CODE_SETTINGS = -6;
-    public static final int CODE_SHORTCUT = -7;
+    public static final int CODE_OUTPUT_TEXT = -3;
+    public static final int CODE_DELETE = -4;
+    public static final int CODE_SETTINGS = -5;
+    public static final int CODE_SHORTCUT = -6;
+    public static final int CODE_ACTION_ENTER = -7;
     // Code value representing the code is not specified.
     public static final int CODE_UNSPECIFIED = -9;
 
@@ -380,6 +380,7 @@
         case CODE_DELETE: return "delete";
         case CODE_SETTINGS: return "settings";
         case CODE_SHORTCUT: return "shortcut";
+        case CODE_ACTION_ENTER: return "actionEnter";
         case CODE_UNSPECIFIED: return "unspec";
         case CODE_TAB: return "tab";
         case CODE_ENTER: return "enter";
@@ -475,7 +476,7 @@
      */
 
     public static class Builder<KP extends Params> {
-        private static final String TAG = Builder.class.getSimpleName();
+        private static final String BUILDER_TAG = "Keyboard.Builder";
         private static final boolean DEBUG = false;
 
         // Keyboard XML Tags
@@ -643,7 +644,7 @@
             a.recycle();
             if (resourceId == 0) {
                 if (LatinImeLogger.sDBG)
-                    Log.e(TAG, "touchPositionCorrectionData is not defined");
+                    Log.e(BUILDER_TAG, "touchPositionCorrectionData is not defined");
                 return;
             }
 
@@ -680,10 +681,10 @@
             try {
                 parseKeyboard(parser);
             } catch (XmlPullParserException e) {
-                Log.w(TAG, "keyboard XML parse error: " + e);
+                Log.w(BUILDER_TAG, "keyboard XML parse error: " + e);
                 throw new IllegalArgumentException(e);
             } catch (IOException e) {
-                Log.w(TAG, "keyboard XML parse error: " + e);
+                Log.w(BUILDER_TAG, "keyboard XML parse error: " + e);
                 throw new RuntimeException(e);
             } finally {
                 parser.close();
@@ -699,9 +700,29 @@
             return new Keyboard(mParams);
         }
 
+        private int mIndent;
+        private static final String SPACES = "                                             ";
+
+        private static String spaces(int count) {
+            return (count < SPACES.length()) ? SPACES.substring(0, count) : SPACES;
+        }
+
+        private void startTag(String format, Object ... args) {
+            Log.d(BUILDER_TAG, String.format(spaces(++mIndent * 2) + format, args));
+        }
+
+        private void endTag(String format, Object ... args) {
+            Log.d(BUILDER_TAG, String.format(spaces(mIndent-- * 2) + format, args));
+        }
+
+        private void startEndTag(String format, Object ... args) {
+            Log.d(BUILDER_TAG, String.format(spaces(++mIndent * 2) + format, args));
+            mIndent--;
+        }
+
         private void parseKeyboard(XmlPullParser parser)
                 throws XmlPullParserException, IOException {
-            if (DEBUG) Log.d(TAG, String.format("<%s> %s", TAG_KEYBOARD, mParams.mId));
+            if (DEBUG) startTag("<%s> %s", TAG_KEYBOARD, mParams.mId);
             int event;
             while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
                 if (event == XmlPullParser.START_TAG) {
@@ -788,9 +809,10 @@
                     final String tag = parser.getName();
                     if (TAG_ROW.equals(tag)) {
                         Row row = parseRowAttributes(parser);
-                        if (DEBUG) Log.d(TAG, String.format("<%s>", TAG_ROW));
-                        if (!skip)
+                        if (DEBUG) startTag("<%s>%s", TAG_ROW, skip ? " skipped" : "");
+                        if (!skip) {
                             startRow(row);
+                        }
                         parseRowContent(parser, row, skip);
                     } else if (TAG_INCLUDE.equals(tag)) {
                         parseIncludeKeyboardContent(parser, skip);
@@ -803,15 +825,13 @@
                     }
                 } else if (event == XmlPullParser.END_TAG) {
                     final String tag = parser.getName();
+                    if (DEBUG) endTag("</%s>", tag);
                     if (TAG_KEYBOARD.equals(tag)) {
                         endKeyboard();
                         break;
                     } else if (TAG_CASE.equals(tag) || TAG_DEFAULT.equals(tag)
                             || TAG_MERGE.equals(tag)) {
-                        if (DEBUG) Log.d(TAG, String.format("</%s>", tag));
                         break;
-                    } else if (TAG_KEY_STYLE.equals(tag)) {
-                        continue;
                     } else {
                         throw new XmlParseUtils.IllegalEndTag(parser, TAG_ROW);
                     }
@@ -854,17 +874,15 @@
                     }
                 } else if (event == XmlPullParser.END_TAG) {
                     final String tag = parser.getName();
+                    if (DEBUG) endTag("</%s>", tag);
                     if (TAG_ROW.equals(tag)) {
-                        if (DEBUG) Log.d(TAG, String.format("</%s>", TAG_ROW));
-                        if (!skip)
+                        if (!skip) {
                             endRow(row);
+                        }
                         break;
                     } else if (TAG_CASE.equals(tag) || TAG_DEFAULT.equals(tag)
                             || TAG_MERGE.equals(tag)) {
-                        if (DEBUG) Log.d(TAG, String.format("</%s>", tag));
                         break;
-                    } else if (TAG_KEY_STYLE.equals(tag)) {
-                        continue;
                     } else {
                         throw new XmlParseUtils.IllegalEndTag(parser, TAG_KEY);
                     }
@@ -876,11 +894,14 @@
                 throws XmlPullParserException, IOException {
             if (skip) {
                 XmlParseUtils.checkEndTag(TAG_KEY, parser);
+                if (DEBUG) startEndTag("<%s /> skipped", TAG_KEY);
             } else {
                 final Key key = new Key(mResources, mParams, row, parser, mKeyStyles);
-                if (DEBUG) Log.d(TAG, String.format("<%s%s keyLabel=%s code=%d moreKeys=%s />",
-                        TAG_KEY, (key.isEnabled() ? "" : " disabled"), key.mLabel, key.mCode,
-                        Arrays.toString(key.mMoreKeys)));
+                if (DEBUG) {
+                    startEndTag("<%s%s %s moreKeys=%s />", TAG_KEY,
+                            (key.isEnabled() ? "" : " disabled"), key,
+                            Arrays.toString(key.mMoreKeys));
+                }
                 XmlParseUtils.checkEndTag(TAG_KEY, parser);
                 endKey(key);
             }
@@ -890,10 +911,11 @@
                 throws XmlPullParserException, IOException {
             if (skip) {
                 XmlParseUtils.checkEndTag(TAG_SPACER, parser);
+                if (DEBUG) startEndTag("<%s /> skipped", TAG_SPACER);
             } else {
                 final Key.Spacer spacer = new Key.Spacer(
                         mResources, mParams, row, parser, mKeyStyles);
-                if (DEBUG) Log.d(TAG, String.format("<%s />", TAG_SPACER));
+                if (DEBUG) startEndTag("<%s />", TAG_SPACER);
                 XmlParseUtils.checkEndTag(TAG_SPACER, parser);
                 endKey(spacer);
             }
@@ -913,6 +935,7 @@
                 throws XmlPullParserException, IOException {
             if (skip) {
                 XmlParseUtils.checkEndTag(TAG_INCLUDE, parser);
+                if (DEBUG) startEndTag("</%s> skipped", TAG_INCLUDE);
             } else {
                 final AttributeSet attr = Xml.asAttributeSet(parser);
                 final TypedArray keyboardAttr = mResources.obtainAttributes(attr,
@@ -944,8 +967,10 @@
                 }
 
                 XmlParseUtils.checkEndTag(TAG_INCLUDE, parser);
-                if (DEBUG) Log.d(TAG, String.format("<%s keyboardLayout=%s />",
-                        TAG_INCLUDE, mResources.getResourceEntryName(keyboardLayout)));
+                if (DEBUG) {
+                    startEndTag("<%s keyboardLayout=%s />",TAG_INCLUDE,
+                            mResources.getResourceEntryName(keyboardLayout));
+                }
                 final XmlResourceParser parserForInclude = mResources.getXml(keyboardLayout);
                 try {
                     parseMerge(parserForInclude, row, skip);
@@ -961,6 +986,7 @@
 
         private void parseMerge(XmlPullParser parser, Row row, boolean skip)
                 throws XmlPullParserException, IOException {
+            if (DEBUG) startTag("<%s>", TAG_MERGE);
             int event;
             while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
                 if (event == XmlPullParser.START_TAG) {
@@ -992,7 +1018,7 @@
 
         private void parseSwitchInternal(XmlPullParser parser, Row row, boolean skip)
                 throws XmlPullParserException, IOException {
-            if (DEBUG) Log.d(TAG, String.format("<%s> %s", TAG_SWITCH, mParams.mId));
+            if (DEBUG) startTag("<%s> %s", TAG_SWITCH, mParams.mId);
             boolean selected = false;
             int event;
             while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
@@ -1008,7 +1034,7 @@
                 } else if (event == XmlPullParser.END_TAG) {
                     final String tag = parser.getName();
                     if (TAG_SWITCH.equals(tag)) {
-                        if (DEBUG) Log.d(TAG, String.format("</%s>", TAG_SWITCH));
+                        if (DEBUG) endTag("</%s>", TAG_SWITCH);
                         break;
                     } else {
                         throw new XmlParseUtils.IllegalEndTag(parser, TAG_KEY);
@@ -1057,10 +1083,8 @@
                         R.styleable.Keyboard_Case_shortcutKeyEnabled, id.mShortcutKeyEnabled);
                 final boolean hasShortcutKeyMatched = matchBoolean(a,
                         R.styleable.Keyboard_Case_hasShortcutKey, id.mHasShortcutKey);
-                // As noted at {@link KeyboardId} class, we are interested only in enum value
-                // masked by {@link android.view.inputmethod.EditorInfo#IME_MASK_ACTION} and
-                // {@link android.view.inputmethod.EditorInfo#IME_FLAG_NO_ENTER_ACTION}. So matching
-                // this attribute with id.mImeOptions as integer value is enough for our purpose.
+                final boolean isMultiLineMatched = matchBoolean(a,
+                        R.styleable.Keyboard_Case_isMultiLine, id.isMultiLine());
                 final boolean imeActionMatched = matchInteger(a,
                         R.styleable.Keyboard_Case_imeAction, id.imeAction());
                 final boolean localeCodeMatched = matchString(a,
@@ -1072,30 +1096,42 @@
                 final boolean selected = keyboardSetElementMatched && modeMatched
                         && navigateActionMatched && passwordInputMatched && hasSettingsKeyMatched
                         && f2KeyModeMatched && clobberSettingsKeyMatched
-                        && shortcutKeyEnabledMatched && hasShortcutKeyMatched && imeActionMatched
-                        && localeCodeMatched && languageCodeMatched && countryCodeMatched;
+                        && shortcutKeyEnabledMatched && hasShortcutKeyMatched && isMultiLineMatched
+                        && imeActionMatched && localeCodeMatched && languageCodeMatched
+                        && countryCodeMatched;
 
-                if (DEBUG) Log.d(TAG, String.format("<%s%s%s%s%s%s%s%s%s%s%s%s%s%s> %s", TAG_CASE,
-                        textAttr(a.getString(R.styleable.Keyboard_Case_keyboardSetElement),
-                                "keyboardSetElement"),
-                        textAttr(a.getString(R.styleable.Keyboard_Case_mode), "mode"),
-                        booleanAttr(a, R.styleable.Keyboard_Case_navigateAction, "navigateAction"),
-                        booleanAttr(a, R.styleable.Keyboard_Case_passwordInput, "passwordInput"),
-                        booleanAttr(a, R.styleable.Keyboard_Case_hasSettingsKey, "hasSettingsKey"),
-                        textAttr(KeyboardId.f2KeyModeName(
-                                a.getInt(R.styleable.Keyboard_Case_f2KeyMode, -1)), "f2KeyMode"),
-                        booleanAttr(a, R.styleable.Keyboard_Case_clobberSettingsKey,
-                                "clobberSettingsKey"),
-                        booleanAttr(a, R.styleable.Keyboard_Case_shortcutKeyEnabled,
-                                "shortcutKeyEnabled"),
-                        booleanAttr(a, R.styleable.Keyboard_Case_hasShortcutKey, "hasShortcutKey"),
-                        textAttr(EditorInfoCompatUtils.imeOptionsName(
-                                a.getInt(R.styleable.Keyboard_Case_imeAction, -1)), "imeAction"),
-                        textAttr(a.getString(R.styleable.Keyboard_Case_localeCode), "localeCode"),
-                        textAttr(a.getString(R.styleable.Keyboard_Case_languageCode),
-                                "languageCode"),
-                        textAttr(a.getString(R.styleable.Keyboard_Case_countryCode), "countryCode"),
-                        Boolean.toString(selected)));
+                if (DEBUG) {
+                    startTag("<%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s>%s", TAG_CASE,
+                            textAttr(a.getString(R.styleable.Keyboard_Case_keyboardSetElement),
+                                    "keyboardSetElement"),
+                            textAttr(a.getString(R.styleable.Keyboard_Case_mode), "mode"),
+                            booleanAttr(a, R.styleable.Keyboard_Case_navigateAction,
+                                    "navigateAction"),
+                            booleanAttr(a, R.styleable.Keyboard_Case_passwordInput,
+                                    "passwordInput"),
+                            booleanAttr(a, R.styleable.Keyboard_Case_hasSettingsKey,
+                                    "hasSettingsKey"),
+                            textAttr(KeyboardId.f2KeyModeName(
+                                    a.getInt(R.styleable.Keyboard_Case_f2KeyMode, -1)),
+                                    "f2KeyMode"),
+                            booleanAttr(a, R.styleable.Keyboard_Case_clobberSettingsKey,
+                                    "clobberSettingsKey"),
+                            booleanAttr(a, R.styleable.Keyboard_Case_shortcutKeyEnabled,
+                                    "shortcutKeyEnabled"),
+                            booleanAttr(a, R.styleable.Keyboard_Case_hasShortcutKey,
+                                    "hasShortcutKey"),
+                            booleanAttr(a, R.styleable.Keyboard_Case_isMultiLine,
+                                    "isMultiLine"),
+                            textAttr(a.getString(R.styleable.Keyboard_Case_imeAction),
+                                    "imeAction"),
+                            textAttr(a.getString(R.styleable.Keyboard_Case_localeCode),
+                                    "localeCode"),
+                            textAttr(a.getString(R.styleable.Keyboard_Case_languageCode),
+                                    "languageCode"),
+                            textAttr(a.getString(R.styleable.Keyboard_Case_countryCode),
+                                    "countryCode"),
+                            selected ? "" : " skipped");
+                }
 
                 return selected;
             } finally {
@@ -1148,7 +1184,7 @@
 
         private boolean parseDefault(XmlPullParser parser, Row row, boolean skip)
                 throws XmlPullParserException, IOException {
-            if (DEBUG) Log.d(TAG, String.format("<%s>", TAG_DEFAULT));
+            if (DEBUG) startTag("<%s>", TAG_DEFAULT);
             if (row == null) {
                 parseKeyboardContent(parser, skip);
             } else {
@@ -1158,7 +1194,7 @@
         }
 
         private void parseKeyStyle(XmlPullParser parser, boolean skip)
-                throws XmlPullParserException {
+                throws XmlPullParserException, IOException {
             TypedArray keyStyleAttr = mResources.obtainAttributes(Xml.asAttributeSet(parser),
                     R.styleable.Keyboard_KeyStyle);
             TypedArray keyAttrs = mResources.obtainAttributes(Xml.asAttributeSet(parser),
@@ -1167,12 +1203,18 @@
                 if (!keyStyleAttr.hasValue(R.styleable.Keyboard_KeyStyle_styleName))
                     throw new XmlParseUtils.ParseException("<" + TAG_KEY_STYLE
                             + "/> needs styleName attribute", parser);
+                if (DEBUG) {
+                    startEndTag("<%s styleName=%s />%s", TAG_KEY_STYLE,
+                        keyStyleAttr.getString(R.styleable.Keyboard_KeyStyle_styleName),
+                        skip ? " skipped" : "");
+                }
                 if (!skip)
                     mKeyStyles.parseKeyStyleAttributes(keyStyleAttr, keyAttrs, parser);
             } finally {
                 keyStyleAttr.recycle();
                 keyAttrs.recycle();
             }
+            XmlParseUtils.checkEndTag(TAG_KEY_STYLE, parser);
         }
 
         private void startKeyboard() {
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardId.java b/java/src/com/android/inputmethod/keyboard/KeyboardId.java
index edca87f..a000eae 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardId.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardId.java
@@ -16,6 +16,7 @@
 
 package com.android.inputmethod.keyboard;
 
+import android.text.InputType;
 import android.text.TextUtils;
 import android.view.inputmethod.EditorInfo;
 
@@ -25,10 +26,8 @@
 import java.util.Arrays;
 import java.util.Locale;
 
-// TODO: Move to com.android.inputmethod.keyboard.internal package.
 /**
- * Represents the parameters necessary to construct a new LatinKeyboard,
- * which also serve as a unique identifier for each keyboard type.
+ * Unique identifier for each keyboard type.
  */
 public class KeyboardId {
     public static final int MODE_TEXT = 0;
@@ -54,34 +53,37 @@
     private static final int F2KEY_MODE_SHORTCUT_IME = 2;
     private static final int F2KEY_MODE_SHORTCUT_IME_OR_SETTINGS = 3;
 
+    private static final int IME_ACTION_CUSTOM_LABEL = EditorInfo.IME_MASK_ACTION + 1;
+
     public final Locale mLocale;
     public final int mOrientation;
     public final int mWidth;
     public final int mMode;
     public final int mElementId;
-    private final int mInputType;
-    private final int mImeOptions;
+    private final EditorInfo mEditorInfo;
     private final boolean mSettingsKeyEnabled;
     public final boolean mClobberSettingsKey;
     public final boolean mShortcutKeyEnabled;
     public final boolean mHasShortcutKey;
+    public final String mCustomActionLabel;
 
     private final int mHashCode;
 
     public KeyboardId(int elementId, Locale locale, int orientation, int width, int mode,
-            int inputType, int imeOptions, boolean settingsKeyEnabled, boolean clobberSettingsKey,
+            EditorInfo editorInfo, boolean settingsKeyEnabled, boolean clobberSettingsKey,
             boolean shortcutKeyEnabled, boolean hasShortcutKey) {
         this.mLocale = locale;
         this.mOrientation = orientation;
         this.mWidth = width;
         this.mMode = mode;
         this.mElementId = elementId;
-        this.mInputType = inputType;
-        this.mImeOptions = imeOptions;
+        this.mEditorInfo = editorInfo;
         this.mSettingsKeyEnabled = settingsKeyEnabled;
         this.mClobberSettingsKey = clobberSettingsKey;
         this.mShortcutKeyEnabled = shortcutKeyEnabled;
         this.mHasShortcutKey = hasShortcutKey;
+        this.mCustomActionLabel = (editorInfo.actionLabel != null)
+                ? editorInfo.actionLabel.toString() : null;
 
         this.mHashCode = hashCode(this);
     }
@@ -98,6 +100,7 @@
                 id.mClobberSettingsKey,
                 id.mShortcutKeyEnabled,
                 id.mHasShortcutKey,
+                id.isMultiLine(),
                 id.imeAction(),
                 id.mLocale
         });
@@ -116,6 +119,7 @@
                 && other.mClobberSettingsKey == this.mClobberSettingsKey
                 && other.mShortcutKeyEnabled == this.mShortcutKeyEnabled
                 && other.mHasShortcutKey == this.mHasShortcutKey
+                && other.isMultiLine() == this.isMultiLine()
                 && other.imeAction() == this.imeAction()
                 && other.mLocale.equals(this.mLocale);
     }
@@ -151,22 +155,30 @@
 
     public boolean navigateAction() {
         // Note: Turn off checking navigation flag to show TAB key for now.
-        boolean navigateAction = InputTypeCompatUtils.isWebInputType(mInputType);
+        boolean navigateAction = InputTypeCompatUtils.isWebInputType(mEditorInfo.inputType);
 //                || EditorInfoCompatUtils.hasFlagNavigateNext(mImeOptions)
 //                || EditorInfoCompatUtils.hasFlagNavigatePrevious(mImeOptions);
         return navigateAction;
     }
 
     public boolean passwordInput() {
-        return InputTypeCompatUtils.isPasswordInputType(mInputType)
-                || InputTypeCompatUtils.isVisiblePasswordInputType(mInputType);
+        final int inputType = mEditorInfo.inputType;
+        return InputTypeCompatUtils.isPasswordInputType(inputType)
+                || InputTypeCompatUtils.isVisiblePasswordInputType(inputType);
+    }
+
+    public boolean isMultiLine() {
+        return (mEditorInfo.inputType & InputType.TYPE_TEXT_FLAG_MULTI_LINE) != 0;
     }
 
     public int imeAction() {
-        // We are interested only in {@link EditorInfo#IME_MASK_ACTION} enum value and
-        // {@link EditorInfo#IME_FLAG_NO_ENTER_ACTION}.
-        return mImeOptions & (
-                EditorInfo.IME_MASK_ACTION | EditorInfo.IME_FLAG_NO_ENTER_ACTION);
+        if ((mEditorInfo.imeOptions & EditorInfo.IME_FLAG_NO_ENTER_ACTION) != 0) {
+            return EditorInfo.IME_ACTION_NONE;
+        } else if (mEditorInfo.actionLabel != null) {
+            return IME_ACTION_CUSTOM_LABEL;
+        } else {
+            return mEditorInfo.imeOptions & EditorInfo.IME_MASK_ACTION;
+        }
     }
 
     public boolean hasSettingsKey() {
@@ -205,7 +217,7 @@
                 mLocale,
                 (mOrientation == 1 ? "port" : "land"), mWidth,
                 modeName(mMode),
-                EditorInfoCompatUtils.imeOptionsName(imeAction()),
+                imeAction(),
                 f2KeyModeName(f2KeyMode()),
                 (mClobberSettingsKey ? " clobberSettingsKey" : ""),
                 (navigateAction() ? " navigateAction" : ""),
@@ -252,6 +264,11 @@
         }
     }
 
+    public static String actionName(int actionId) {
+        return (actionId == IME_ACTION_CUSTOM_LABEL) ? "actionCustomLabel"
+                : EditorInfoCompatUtils.imeActionName(actionId);
+    }
+
     public static String f2KeyModeName(int f2KeyMode) {
         switch (f2KeyMode) {
         case F2KEY_MODE_NONE: return "none";
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSet.java b/java/src/com/android/inputmethod/keyboard/KeyboardSet.java
index 82eaa1d..f27170a 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardSet.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardSet.java
@@ -62,6 +62,8 @@
             new HashMap<KeyboardId, SoftReference<Keyboard>>();
     private static final KeysCache sKeysCache = new KeysCache();
 
+    private static final EditorInfo EMPTY_EDITOR_INFO = new EditorInfo();
+
     public static class KeyboardSetException extends RuntimeException {
         public final KeyboardId mKeyboardId;
         public KeyboardSetException(Throwable cause, KeyboardId keyboardId) {
@@ -94,8 +96,7 @@
 
     static class Params {
         int mMode;
-        int mInputType;
-        int mImeOptions;
+        EditorInfo mEditorInfo;
         boolean mTouchPositionCorrectionEnabled;
         boolean mSettingsKeyEnabled;
         boolean mVoiceKeyEnabled;
@@ -192,9 +193,8 @@
         final boolean hasShortcutKey = params.mVoiceKeyEnabled
                 && (isSymbols != params.mVoiceKeyOnMain);
         return new KeyboardId(keyboardSetElementId, params.mLocale, params.mOrientation,
-                params.mWidth, params.mMode, params.mInputType, params.mImeOptions,
-                params.mSettingsKeyEnabled, params.mNoSettingsKey, params.mVoiceKeyEnabled,
-                hasShortcutKey);
+                params.mWidth, params.mMode, params.mEditorInfo, params.mSettingsKeyEnabled,
+                params.mNoSettingsKey, params.mVoiceKeyEnabled, hasShortcutKey);
     }
 
     public static class Builder {
@@ -213,10 +213,7 @@
             final Params params = mParams;
 
             params.mMode = Utils.getKeyboardMode(editorInfo);
-            if (editorInfo != null) {
-                params.mInputType = editorInfo.inputType;
-                params.mImeOptions = editorInfo.imeOptions;
-            }
+            params.mEditorInfo = (editorInfo != null) ? editorInfo : EMPTY_EDITOR_INFO;
             params.mNoSettingsKey = Utils.inPrivateImeOptions(
                     mPackageName, LatinIME.IME_OPTION_NO_SETTINGS_KEY, mEditorInfo);
         }
@@ -232,7 +229,8 @@
                 boolean touchPositionCorrectionEnabled) {
             final boolean deprecatedForceAscii = Utils.inPrivateImeOptions(
                     mPackageName, LatinIME.IME_OPTION_FORCE_ASCII, mEditorInfo);
-            final boolean forceAscii = EditorInfoCompatUtils.hasFlagForceAscii(mParams.mImeOptions)
+            final boolean forceAscii = EditorInfoCompatUtils.hasFlagForceAscii(
+                    mParams.mEditorInfo.imeOptions)
                     || deprecatedForceAscii;
             mParams.mLocale = (forceAscii && !asciiCapable) ? Locale.US : inputLocale;
             mParams.mTouchPositionCorrectionEnabled = touchPositionCorrectionEnabled;
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 5b76b54..8b8c024 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -1224,6 +1224,41 @@
         return mOptionsDialog != null && mOptionsDialog.isShowing();
     }
 
+    private void insertPunctuationFromSuggestionStrip(final int code) {
+        onCodeInput(code, new int[] { code },
+                KeyboardActionListener.SUGGESTION_STRIP_COORDINATE,
+                KeyboardActionListener.SUGGESTION_STRIP_COORDINATE);
+    }
+
+    private static int getEditorActionId(EditorInfo editorInfo) {
+        if (editorInfo == null) return 0;
+        return (editorInfo.actionLabel != null)
+                ? editorInfo.actionId
+                : (editorInfo.imeOptions & EditorInfo.IME_MASK_ACTION);
+    }
+
+    private void performeEditorAction(int actionId) {
+        final InputConnection ic = getCurrentInputConnection();
+        if (ic != null) {
+            ic.performEditorAction(actionId);
+        }
+    }
+
+    private void sendKeyCodePoint(int code) {
+        // TODO: Remove this special handling of digit letters.
+        // For backward compatibility. See {@link InputMethodService#sendKeyChar(char)}.
+        if (code >= '0' && code <= '9') {
+            super.sendKeyChar((char)code);
+            return;
+        }
+
+        final InputConnection ic = getCurrentInputConnection();
+        if (ic != null) {
+            final String text = new String(new int[] { code }, 0, 1);
+            ic.commitText(text, text.length());
+        }
+    }
+
     // Implementation of {@link KeyboardActionListener}.
     @Override
     public void onCodeInput(int primaryCode, int[] keyCodes, int x, int y) {
@@ -1264,6 +1299,9 @@
         case Keyboard.CODE_SHORTCUT:
             mSubtypeSwitcher.switchToShortcutIME();
             break;
+        case Keyboard.CODE_ACTION_ENTER:
+            performeEditorAction(getEditorActionId(getCurrentInputEditorInfo()));
+            break;
         case Keyboard.CODE_TAB:
             handleTab();
             // There are two cases for tab. Either we send a "next" event, that may change the
@@ -1301,7 +1339,7 @@
         commitTyped(ic);
         text = specificTldProcessingOnTextInput(ic, text);
         if (SPACE_STATE_PHANTOM == mSpaceState) {
-            sendKeyChar((char)Keyboard.CODE_SPACE);
+            sendKeyCodePoint(Keyboard.CODE_SPACE);
         }
         ic.commitText(text, 1);
         ic.endBatchEdit();
@@ -1448,10 +1486,12 @@
         }
     }
 
+    // TODO: Implement next and previous actions using other key code than tab's code.
     private void handleTab() {
         final int imeOptions = getCurrentInputEditorInfo().imeOptions;
         if (!EditorInfoCompatUtils.hasFlagNavigateNext(imeOptions)
                 && !EditorInfoCompatUtils.hasFlagNavigatePrevious(imeOptions)) {
+            // TODO: This should be {@link #sendKeyCodePoint(int)}.
             sendDownUpKeyEvents(KeyEvent.KEYCODE_TAB);
             return;
         }
@@ -1513,7 +1553,7 @@
                 // Sanity check
                 throw new RuntimeException("Should not be composing here");
             }
-            sendKeyChar((char)Keyboard.CODE_SPACE);
+            sendKeyCodePoint(Keyboard.CODE_SPACE);
         }
 
         if ((isAlphabet(primaryCode)
@@ -1549,7 +1589,7 @@
             final boolean swapWeakSpace = maybeStripSpaceWhileInBatchEdit(ic, primaryCode,
                     spaceState, KeyboardActionListener.SUGGESTION_STRIP_COORDINATE == x);
 
-            sendKeyChar((char)primaryCode);
+            sendKeyCodePoint(primaryCode);
 
             if (swapWeakSpace) {
                 swapSwapperAndSpaceWhileInBatchEdit(ic);
@@ -1595,11 +1635,7 @@
         final boolean swapWeakSpace = maybeStripSpaceWhileInBatchEdit(ic, primaryCode, spaceState,
                 KeyboardActionListener.SUGGESTION_STRIP_COORDINATE == x);
 
-        // TODO: rethink interactions of sendKeyChar, commitText("\n") and actions. sendKeyChar
-        // with a CODE_ENTER parameter will have the default InputMethodService implementation
-        // possibly redirect on the keyboard action. That may be the right thing to do, but
-        // on Shift+Enter, it's generally not, so we may want to do the redirection right here.
-        sendKeyChar((char)primaryCode);
+        sendKeyCodePoint(primaryCode);
 
         if (Keyboard.CODE_SPACE == primaryCode) {
             if (isSuggestionsRequested()) {