From 717cef79ead5d63a01d09b47caab0a3d719c69df Mon Sep 17 00:00:00 2001
From: "Tadashi G. Takaoka" <takaoka@google.com>
Date: Tue, 26 Apr 2011 14:27:35 +0900
Subject: [PATCH] Fix quotation marks

This change
* Allows snap back from symbols shifted to symbol layout.
* Add "left single", "right single", "single low" and "single high
  reversed" quotation marks to popup characters of "single quote".
* Add "double low" and "double high reversed" quatation marks to popup
  characters of "double quote".
* Add "prime" and "double prime" to popup characters of "degree".
* Disable non-ASCII key of symbol more layout on passowrd input.

Bug: 4345054
Bug: 4347045
Change-Id: I8168ce6a74a9536e4966f7f9d1099ac0132925c7
---
 java/res/xml-xlarge/kbd_symbols.xml           |   3 +-
 java/res/xml-xlarge/kbd_symbols_shift.xml     |   3 +-
 java/res/xml/kbd_key_styles.xml               |  23 ++++
 java/res/xml/kbd_symbols.xml                  |   6 +-
 java/res/xml/kbd_symbols_shift.xml            |  16 ++-
 java/res/xml/kbd_symbols_shift_row4.xml       |  10 +-
 .../keyboard/KeyboardSwitcher.java            | 127 +++++++++++++-----
 .../inputmethod/keyboard/PointerTracker.java  |   2 +-
 8 files changed, 146 insertions(+), 44 deletions(-)

diff --git a/java/res/xml-xlarge/kbd_symbols.xml b/java/res/xml-xlarge/kbd_symbols.xml
index 1061178e09..f1deae0f97 100644
--- a/java/res/xml-xlarge/kbd_symbols.xml
+++ b/java/res/xml-xlarge/kbd_symbols.xml
@@ -211,9 +211,10 @@
                     latin:keyLabel="-" />
             </case>
             <default>
+                <!-- Note: DroidSans doesn't have double-high-reversed-quotation '\u201f' glyph. -->
                 <Key
                     latin:keyLabel="&quot;"
-                    latin:popupCharacters="“,”,«,»,˝" />
+                    latin:popupCharacters="“,”,„,‟,«,»,‘,’,‚,‛" />
                 <Key
                     latin:keyLabel="_" />
             </default>
diff --git a/java/res/xml-xlarge/kbd_symbols_shift.xml b/java/res/xml-xlarge/kbd_symbols_shift.xml
index 8359b75714..cc23358a52 100644
--- a/java/res/xml-xlarge/kbd_symbols_shift.xml
+++ b/java/res/xml-xlarge/kbd_symbols_shift.xml
@@ -99,7 +99,8 @@
             latin:popupCharacters="↑,↓,←,→" />
         <Key
             latin:keyStyle="nonPasswordSymbolKeyStyle"
-            latin:keyLabel="°" />
+            latin:keyLabel="°"
+            latin:popupCharacters="′,″" />
         <Key
             latin:keyStyle="nonPasswordSymbolKeyStyle"
             latin:keyLabel="±"
diff --git a/java/res/xml/kbd_key_styles.xml b/java/res/xml/kbd_key_styles.xml
index 4792e1f006..d4d25d4a29 100644
--- a/java/res/xml/kbd_key_styles.xml
+++ b/java/res/xml/kbd_key_styles.xml
@@ -312,4 +312,27 @@
         latin:popupCharacters="@string/alternates_for_smiley"
         latin:maxPopupKeyboardColumn="5"
         latin:parentStyle="functionalKeyStyle" />
+    <switch>
+        <case
+            latin:passwordInput="true"
+        >
+            <key-style
+                latin:styleName="nonPasswordSymbolKeyStyle"
+                latin:enabled="false" />
+            <key-style
+                latin:styleName="nonPasswordFunctionalKeyStyle"
+                latin:enabled="false"
+                latin:parentStyle="functionalKeyStyle" />
+        </case>
+        <!-- latin:passwordInput="false" -->
+        <default>
+            <key-style
+                latin:styleName="nonPasswordSymbolKeyStyle"
+                latin:enabled="true" />
+            <key-style
+                latin:styleName="nonPasswordFunctionalKeyStyle"
+                latin:enabled="true"
+                latin:parentStyle="functionalKeyStyle" />
+        </default>
+    </switch>
 </merge>
\ No newline at end of file
diff --git a/java/res/xml/kbd_symbols.xml b/java/res/xml/kbd_symbols.xml
index 0b8b899696..9748bce8b4 100644
--- a/java/res/xml/kbd_symbols.xml
+++ b/java/res/xml/kbd_symbols.xml
@@ -105,12 +105,14 @@
         <Key
             latin:keyLabel="!"
             latin:popupCharacters="¡" />
+        <!-- Note: DroidSans doesn't have double-high-reversed-quotation '\u201f' glyph. -->
         <Key
             latin:keyLabel="&quot;"
-            latin:popupCharacters="“,”,«,»,˝" />
+            latin:popupCharacters="“,”,„,‟,«,»"
+            latin:maxPopupKeyboardColumn="6" />
         <Key
             latin:keyLabel="\'"
-            latin:popupCharacters="‘,’" />
+            latin:popupCharacters="‘,’,‚,‛,´" />
         <Key
             latin:keyLabel=":" />
         <Key
diff --git a/java/res/xml/kbd_symbols_shift.xml b/java/res/xml/kbd_symbols_shift.xml
index 36f0c64a96..3978f17767 100644
--- a/java/res/xml/kbd_symbols_shift.xml
+++ b/java/res/xml/kbd_symbols_shift.xml
@@ -42,16 +42,21 @@
         <Key
             latin:keyLabel="|" />
         <Key
+            latin:keyStyle="nonPasswordSymbolKeyStyle"
             latin:keyLabel="•"
             latin:popupCharacters="♪,♥,♠,♦,♣" />
         <Key
+            latin:keyStyle="nonPasswordSymbolKeyStyle"
             latin:keyLabel="√" />
         <Key
+            latin:keyStyle="nonPasswordSymbolKeyStyle"
             latin:keyLabel="Ï€"
             latin:popupCharacters="Π" />
         <Key
+            latin:keyStyle="nonPasswordSymbolKeyStyle"
             latin:keyLabel="÷" />
         <Key
+            latin:keyStyle="nonPasswordSymbolKeyStyle"
             latin:keyLabel="×" />
         <Key
             latin:keyLabel="{" />
@@ -64,13 +69,18 @@
             latin:keyStyle="nonSpecialBackgroundTabKeyStyle"
             latin:keyEdgeFlags="left" />
         <Key
+            latin:keyStyle="nonPasswordSymbolKeyStyle"
             latin:keyLabel="£" />
         <Key
+            latin:keyStyle="nonPasswordSymbolKeyStyle"
             latin:keyLabel="¢" />
         <Key
+            latin:keyStyle="nonPasswordSymbolKeyStyle"
             latin:keyLabel="€" />
         <Key
-            latin:keyLabel="°" />
+            latin:keyStyle="nonPasswordSymbolKeyStyle"
+            latin:keyLabel="°"
+            latin:popupCharacters="′,″" />
         <Key
             latin:keyLabel="^"
             latin:popupCharacters="↑,↓,←,→" />
@@ -92,12 +102,16 @@
             latin:visualInsetsRight="1%p"
             latin:keyEdgeFlags="left" />
         <Key
+            latin:keyStyle="nonPasswordSymbolKeyStyle"
             latin:keyLabel="â„¢" />
         <Key
+            latin:keyStyle="nonPasswordSymbolKeyStyle"
             latin:keyLabel="®" />
         <Key
+            latin:keyStyle="nonPasswordSymbolKeyStyle"
             latin:keyLabel="©" />
         <Key
+            latin:keyStyle="nonPasswordSymbolKeyStyle"
             latin:keyLabel="¶"
             latin:popupCharacters="§" />
         <Key
diff --git a/java/res/xml/kbd_symbols_shift_row4.xml b/java/res/xml/kbd_symbols_shift_row4.xml
index 9159bab24d..4f8567d58a 100644
--- a/java/res/xml/kbd_symbols_shift_row4.xml
+++ b/java/res/xml/kbd_symbols_shift_row4.xml
@@ -34,13 +34,14 @@
                     latin:keyEdgeFlags="left" />
                 <Key
                     latin:keyLabel="„"
-                    latin:keyStyle="functionalKeyStyle" />
+                    latin:popupCharacters="“,”,„,‟,«,»,‘,’,‚,‛"
+                    latin:keyStyle="nonPasswordFunctionalKeyStyle" />
                 <Key
                     latin:keyStyle="spaceKeyStyle"
                     latin:keyWidth="40%p" />
                 <Key
                     latin:keyLabel="…"
-                    latin:keyStyle="functionalKeyStyle" />
+                    latin:keyStyle="nonPasswordFunctionalKeyStyle" />
                 <switch>
                     <case
                         latin:mode="im"
@@ -69,13 +70,14 @@
                     latin:keyStyle="settingsKeyStyle" />
                 <Key
                     latin:keyLabel="„"
-                    latin:keyStyle="functionalKeyStyle" />
+                    latin:popupCharacters="“,”,„,‟,«,»,‘,’,‚,‛"
+                    latin:keyStyle="nonPasswordFunctionalKeyStyle" />
                 <Key
                     latin:keyStyle="spaceKeyStyle"
                     latin:keyWidth="30%p" />
                 <Key
                     latin:keyLabel="…"
-                    latin:keyStyle="functionalKeyStyle" />
+                    latin:keyStyle="nonPasswordFunctionalKeyStyle" />
                 <switch>
                     <case
                         latin:mode="im"
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
index e163457d8c..825b4659c7 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
@@ -57,6 +57,7 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
     private LatinKeyboardView mInputView;
     private LatinIME mInputMethodService;
 
+    // TODO: Combine these key state objects with auto mode switch state.
     private ShiftKeyState mShiftKeyState = new ShiftKeyState("Shift");
     private ModifierKeyState mSymbolKeyState = new ModifierKeyState("Symbol");
 
@@ -75,13 +76,17 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
     private boolean mVoiceKeyEnabled;
     private boolean mVoiceButtonOnPrimary;
 
-    private static final int AUTO_MODE_SWITCH_STATE_ALPHA = 0;
-    private static final int AUTO_MODE_SWITCH_STATE_SYMBOL_BEGIN = 1;
-    private static final int AUTO_MODE_SWITCH_STATE_SYMBOL = 2;
+    // TODO: Encapsulate these state handling to separate class and combine with ShiftKeyState
+    // and ModifierKeyState.
+    private static final int SWITCH_STATE_ALPHA = 0;
+    private static final int SWITCH_STATE_SYMBOL_BEGIN = 1;
+    private static final int SWITCH_STATE_SYMBOL = 2;
     // The following states are used only on the distinct multi-touch panel devices.
-    private static final int AUTO_MODE_SWITCH_STATE_MOMENTARY = 3;
-    private static final int AUTO_MODE_SWITCH_STATE_CHORDING = 4;
-    private int mAutoModeSwitchState = AUTO_MODE_SWITCH_STATE_ALPHA;
+    private static final int SWITCH_STATE_MOMENTARY_ALPHA_AND_SYMBOL = 3;
+    private static final int SWITCH_STATE_MOMENTARY_SYMBOL_AND_MORE = 4;
+    private static final int SWITCH_STATE_CHORDING_ALPHA = 5;
+    private static final int SWITCH_STATE_CHORDING_SYMBOL = 6;
+    private int mSwitchState = SWITCH_STATE_ALPHA;
 
     // Indicates whether or not we have the settings key in option of settings
     private boolean mSettingsKeyEnabledInSettings;
@@ -124,7 +129,7 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
 
     public void loadKeyboard(EditorInfo attribute, boolean voiceKeyEnabled,
             boolean voiceButtonOnPrimary) {
-        mAutoModeSwitchState = AUTO_MODE_SWITCH_STATE_ALPHA;
+        mSwitchState = SWITCH_STATE_ALPHA;
         try {
             loadKeyboardInternal(attribute, voiceKeyEnabled, voiceButtonOnPrimary, false);
         } catch (RuntimeException e) {
@@ -465,6 +470,7 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
             // In symbol mode, just toggle symbol and symbol more keyboard.
             shiftKeyState.onPress();
             toggleShift();
+            mSwitchState = SWITCH_STATE_MOMENTARY_SYMBOL_AND_MORE;
         }
     }
 
@@ -496,6 +502,12 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
                 // transited from automatic temporary upper case.
                 toggleShift();
             }
+        } else {
+            // In symbol mode, snap back to the previous keyboard mode if the user chords the shift
+            // key and another key, then releases the shift key.
+            if (mSwitchState == SWITCH_STATE_CHORDING_SYMBOL) {
+                toggleShift();
+            }
         }
         shiftKeyState.onRelease();
     }
@@ -510,7 +522,7 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
                     + " symbolKeyState=" + mSymbolKeyState);
         changeKeyboardMode();
         mSymbolKeyState.onPress();
-        mAutoModeSwitchState = AUTO_MODE_SWITCH_STATE_MOMENTARY;
+        mSwitchState = SWITCH_STATE_MOMENTARY_ALPHA_AND_SYMBOL;
     }
 
     public void onReleaseSymbol() {
@@ -522,9 +534,10 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
                     + " keyboard=" + getLatinKeyboard().getKeyboardShiftState()
                     + " symbolKeyState=" + mSymbolKeyState);
         // Snap back to the previous keyboard mode if the user chords the mode change key and
-        // other key, then released the mode change key.
-        if (mAutoModeSwitchState == AUTO_MODE_SWITCH_STATE_CHORDING)
+        // another key, then releases the mode change key.
+        if (mSwitchState == SWITCH_STATE_CHORDING_ALPHA) {
             changeKeyboardMode();
+        }
         mSymbolKeyState.onRelease();
     }
 
@@ -543,8 +556,13 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
 
     public void onCancelInput() {
         // Snap back to the previous keyboard mode if the user cancels sliding input.
-        if (mAutoModeSwitchState == AUTO_MODE_SWITCH_STATE_MOMENTARY && getPointerCount() == 1)
-            changeKeyboardMode();
+        if (getPointerCount() == 1) {
+            if (mSwitchState == SWITCH_STATE_MOMENTARY_ALPHA_AND_SYMBOL) {
+                changeKeyboardMode();
+            } else if (mSwitchState == SWITCH_STATE_MOMENTARY_SYMBOL_AND_MORE) {
+                toggleShift();
+            }
+        }
     }
 
     private void toggleShiftInSymbol() {
@@ -567,8 +585,9 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
         setKeyboard(keyboard);
     }
 
-    public boolean isInMomentaryAutoModeSwitchState() {
-        return mAutoModeSwitchState == AUTO_MODE_SWITCH_STATE_MOMENTARY;
+    public boolean isInMomentarySwitchState() {
+        return mSwitchState == SWITCH_STATE_MOMENTARY_ALPHA_AND_SYMBOL
+                || mSwitchState == SWITCH_STATE_MOMENTARY_SYMBOL_AND_MORE;
     }
 
     public boolean isVibrateAndSoundFeedbackRequired() {
@@ -582,9 +601,9 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
     private void toggleKeyboardMode() {
         loadKeyboardInternal(mAttribute, mVoiceKeyEnabled, mVoiceButtonOnPrimary, !mIsSymbols);
         if (mIsSymbols) {
-            mAutoModeSwitchState = AUTO_MODE_SWITCH_STATE_SYMBOL_BEGIN;
+            mSwitchState = SWITCH_STATE_SYMBOL_BEGIN;
         } else {
-            mAutoModeSwitchState = AUTO_MODE_SWITCH_STATE_ALPHA;
+            mSwitchState = SWITCH_STATE_ALPHA;
         }
     }
 
@@ -596,28 +615,52 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
         return mInputView != null && mInputView.hasDistinctMultitouch();
     }
 
+    private static boolean isSpaceCharacter(int c) {
+        return c == Keyboard.CODE_SPACE || c == Keyboard.CODE_ENTER;
+    }
+
+    private static boolean isQuoteCharacter(int c) {
+        // Apostrophe, quotation mark.
+        if (c == '\'' || c == '"')
+            return true;
+        // \u2018: Left single quotation mark
+        // \u2019: Right single quotation mark
+        // \u201a: Single low-9 quotation mark
+        // \u201b: Single high-reversed-9 quotation mark
+        // \u201c: Left double quotation mark
+        // \u201d: Right double quotation mark
+        // \u201e: Double low-9 quotation mark
+        // \u201f: Double high-reversed-9 quotation mark
+        if (c >= '\u2018' && c <= '\u201f')
+            return true;
+        // \u00ab: Left-pointing double angle quotation mark
+        // \u00bb: Right-pointing double angle quotation mark
+        if (c == '\u00ab' || c == '\u00bb')
+            return true;
+        return false;
+    }
+
     /**
      * Updates state machine to figure out when to automatically snap back to the previous mode.
      */
-    public void onKey(int key) {
+    public void onKey(int code) {
         if (DEBUG_STATE)
-            Log.d(TAG, "onKey: code=" + key + " autoModeSwitchState=" + mAutoModeSwitchState
+            Log.d(TAG, "onKey: code=" + code + " switchState=" + mSwitchState
                     + " pointers=" + getPointerCount());
-        switch (mAutoModeSwitchState) {
-        case AUTO_MODE_SWITCH_STATE_MOMENTARY:
+        switch (mSwitchState) {
+        case SWITCH_STATE_MOMENTARY_ALPHA_AND_SYMBOL:
             // Only distinct multi touch devices can be in this state.
             // On non-distinct multi touch devices, mode change key is handled by
             // {@link LatinIME#onCodeInput}, not by {@link LatinIME#onPress} and
-            // {@link LatinIME#onRelease}. So, on such devices, {@link #mAutoModeSwitchState} starts
-            // from {@link #AUTO_MODE_SWITCH_STATE_SYMBOL_BEGIN}, or
-            // {@link #AUTO_MODE_SWITCH_STATE_ALPHA}, not from
-            // {@link #AUTO_MODE_SWITCH_STATE_MOMENTARY}.
-            if (key == Keyboard.CODE_SWITCH_ALPHA_SYMBOL) {
+            // {@link LatinIME#onRelease}. So, on such devices, {@link #mSwitchState} starts
+            // from {@link #SWITCH_STATE_SYMBOL_BEGIN}, or {@link #SWITCH_STATE_ALPHA}, not from
+            // {@link #SWITCH_STATE_MOMENTARY}.
+            if (code == Keyboard.CODE_SWITCH_ALPHA_SYMBOL) {
                 // Detected only the mode change key has been pressed, and then released.
                 if (mIsSymbols) {
-                    mAutoModeSwitchState = AUTO_MODE_SWITCH_STATE_SYMBOL_BEGIN;
+                    mSwitchState = SWITCH_STATE_SYMBOL_BEGIN;
                 } else {
-                    mAutoModeSwitchState = AUTO_MODE_SWITCH_STATE_ALPHA;
+                    mSwitchState = SWITCH_STATE_ALPHA;
                 }
             } else if (getPointerCount() == 1) {
                 // Snap back to the previous keyboard mode if the user pressed the mode change key
@@ -628,18 +671,34 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
             } else {
                 // Chording input is being started. The keyboard mode will be snapped back to the
                 // previous mode in {@link onReleaseSymbol} when the mode change key is released.
-                mAutoModeSwitchState = AUTO_MODE_SWITCH_STATE_CHORDING;
+                mSwitchState = SWITCH_STATE_CHORDING_ALPHA;
+            }
+            break;
+        case SWITCH_STATE_MOMENTARY_SYMBOL_AND_MORE:
+            if (code == Keyboard.CODE_SHIFT) {
+                // Detected only the shift key has been pressed on symbol layout, and then released.
+                mSwitchState = SWITCH_STATE_SYMBOL_BEGIN;
+            } else if (getPointerCount() == 1) {
+                // Snap back to the previous keyboard mode if the user pressed the shift key on
+                // symbol mode and slid to other key, then released the finger.
+                toggleShift();
+                mSwitchState = SWITCH_STATE_SYMBOL;
+            } else {
+                // Chording input is being started. The keyboard mode will be snapped back to the
+                // previous mode in {@link onReleaseShift} when the shift key is released.
+                mSwitchState = SWITCH_STATE_CHORDING_SYMBOL;
             }
             break;
-        case AUTO_MODE_SWITCH_STATE_SYMBOL_BEGIN:
-            if (key != Keyboard.CODE_SPACE && key != Keyboard.CODE_ENTER && key >= 0) {
-                mAutoModeSwitchState = AUTO_MODE_SWITCH_STATE_SYMBOL;
+        case SWITCH_STATE_SYMBOL_BEGIN:
+            if (!isSpaceCharacter(code) && code >= 0) {
+                mSwitchState = SWITCH_STATE_SYMBOL;
             }
             break;
-        case AUTO_MODE_SWITCH_STATE_SYMBOL:
+        case SWITCH_STATE_SYMBOL:
+        case SWITCH_STATE_CHORDING_SYMBOL:
             // Snap back to alpha keyboard mode if user types one or more non-space/enter
-            // characters followed by a space/enter.
-            if (key == Keyboard.CODE_ENTER || key == Keyboard.CODE_SPACE) {
+            // characters followed by a space/enter or quotation mark.
+            if (isSpaceCharacter(code) || isQuoteCharacter(code)) {
                 changeKeyboardMode();
             }
             break;
diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
index e3161f6107..abd1ef286c 100644
--- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java
+++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
@@ -662,7 +662,7 @@ public class PointerTracker {
             // We need not start long press timer on the key which has manual temporary upper case
             // code defined and the keyboard is in manual temporary upper case mode.
             return;
-        } else if (mKeyboardSwitcher.isInMomentaryAutoModeSwitchState()) {
+        } else if (mKeyboardSwitcher.isInMomentarySwitchState()) {
             // We use longer timeout for sliding finger input started from the symbols mode key.
             mHandler.startLongPressTimer(mLongPressKeyTimeout * 3, keyIndex, this);
         } else {
-- 
GitLab