From 6a6075caba3865383eeeb52cccc63a28e4ae5900 Mon Sep 17 00:00:00 2001
From: Amith Yamasani <yamasani@google.com>
Date: Wed, 3 Feb 2010 15:35:49 -0800
Subject: [PATCH] Suggest punctuations between word suggestions, for easy
 access to ?,!, etc.

Also fix a visual bug with the dividers between suggested words.
---
 .../keyboard_suggest_strip_divider.png        | Bin 172 -> 2852 bytes
 res/layout/candidates.xml                     |   4 +--
 res/values/donottranslate.xml                 |   2 ++
 .../inputmethod/latin/CandidateView.java      |  18 ++++++++----
 .../inputmethod/latin/KeyboardSwitcher.java   |   1 -
 .../android/inputmethod/latin/LatinIME.java   |  26 ++++++++++++++++--
 6 files changed, 41 insertions(+), 10 deletions(-)
 mode change 100755 => 100644 res/drawable-hdpi/keyboard_suggest_strip_divider.png

diff --git a/res/drawable-hdpi/keyboard_suggest_strip_divider.png b/res/drawable-hdpi/keyboard_suggest_strip_divider.png
old mode 100755
new mode 100644
index 1a03c52e64df4681aacb8bf1c15cb741eaf0b568..a5980388a979b9348d43a7c9e8b97b312c0fa00e
GIT binary patch
literal 2852
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
CI81o}

delta 157
zcmZ1?wuW(nc)cYD69WUo<jh=IASGGi8c`CQpH@<ySd_}(n3A8As^FQMn4TJxnwU~q
zcrw)nsL0RL#WAGfR??1|pP$yp@1ODS|Nrv`4lpcgVCYR^U^d}e$P&u8T455~BVR|2
zgX$hjcqN)F&bnkc6iMpkKl-9;cU$sc11E=r$Q1U43`}A?3{x7b>T1p9t^ti^@O1Ta
JS?83{1ON^8H>>~v

diff --git a/res/layout/candidates.xml b/res/layout/candidates.xml
index 3af967fd20..068c17e56b 100755
--- a/res/layout/candidates.xml
+++ b/res/layout/candidates.xml
@@ -22,7 +22,7 @@
         xmlns:android="http://schemas.android.com/apk/res/android"
         android:orientation="horizontal"
         android:layout_width="match_parent"
-        android:layout_height="wrap_content"
+        android:layout_height="@dimen/candidate_strip_height"
         android:background="@drawable/keyboard_suggest_strip"
         >
 
@@ -76,4 +76,4 @@
             />
     </LinearLayout>        
     
-</com.android.inputmethod.latin.CandidateViewContainer>
\ No newline at end of file
+</com.android.inputmethod.latin.CandidateViewContainer>
diff --git a/res/values/donottranslate.xml b/res/values/donottranslate.xml
index b9cfbd1359..edf230061d 100644
--- a/res/values/donottranslate.xml
+++ b/res/values/donottranslate.xml
@@ -22,6 +22,8 @@
     <string name="word_separators">.\u0020,;:!?\n()[]*&amp;@{}/&lt;&gt;_+=|\u0022</string>
     <!-- Symbols that are sentence separators, for purposes of making it hug the last sentence. -->
     <string name="sentence_separators">.,!?</string>
+    <!-- Symbols that are suggested between words -->
+    <string name="suggested_punctuations">!?,@_</string>
     <!-- Accented characters related to "d" -->
     <string name="alternates_for_d"></string>
     <!-- Accented characters related to "r" -->
diff --git a/src/com/android/inputmethod/latin/CandidateView.java b/src/com/android/inputmethod/latin/CandidateView.java
index e6a9d6bbf0..0b6b89e6bc 100755
--- a/src/com/android/inputmethod/latin/CandidateView.java
+++ b/src/com/android/inputmethod/latin/CandidateView.java
@@ -26,6 +26,7 @@ import android.graphics.Canvas;
 import android.graphics.Paint;
 import android.graphics.Rect;
 import android.graphics.Typeface;
+import android.graphics.Paint.Align;
 import android.graphics.drawable.Drawable;
 import android.os.Handler;
 import android.os.Message;
@@ -82,7 +83,9 @@ public class CandidateView extends View {
     private int mDescent;
     private boolean mScrolled;
     private int mTargetScrollX;
-    
+
+    private int mMinTouchableWidth;
+
     private int mTotalWidth;
     
     private GestureDetector mGestureDetector;
@@ -133,7 +136,10 @@ public class CandidateView extends View {
         mPaint.setAntiAlias(true);
         mPaint.setTextSize(mPreviewText.getTextSize());
         mPaint.setStrokeWidth(0);
+        mPaint.setTextAlign(Align.CENTER);
         mDescent = (int) mPaint.descent();
+        // 80 pixels for a 160dpi device would mean half an inch
+        mMinTouchableWidth = (int) (getResources().getDisplayMetrics().density * 50);
         
         mGestureDetector = new GestureDetector(new GestureDetector.SimpleOnGestureListener() {
             @Override
@@ -203,7 +209,7 @@ public class CandidateView extends View {
             if (getBackground() != null) {
                 getBackground().getPadding(mBgPadding);
             }
-            mDivider.setBounds(0, mBgPadding.top, mDivider.getIntrinsicWidth(), 
+            mDivider.setBounds(0, 0, mDivider.getIntrinsicWidth(),
                     mDivider.getIntrinsicHeight());
         }
         int x = 0;
@@ -233,7 +239,7 @@ public class CandidateView extends View {
                 wordWidth = mWordWidth[i];
             } else {
                 float textWidth =  paint.measureText(suggestion, 0, suggestion.length());
-                wordWidth = (int) textWidth + X_GAP * 2;
+                wordWidth = Math.max(mMinTouchableWidth, (int) textWidth + X_GAP * 2);
                 mWordWidth[i] = wordWidth;
             }
 
@@ -253,7 +259,7 @@ public class CandidateView extends View {
             }
 
             if (canvas != null) {
-                canvas.drawText(suggestion, 0, suggestion.length(), x + X_GAP, y, paint);
+                canvas.drawText(suggestion, 0, suggestion.length(), x + wordWidth / 2, y, paint);
                 paint.setColor(mColorOther);
                 canvas.translate(x + wordWidth, 0);
                 mDivider.draw(canvas);
@@ -462,7 +468,8 @@ public class CandidateView extends View {
                         + mPreviewText.getPaddingLeft() + mPreviewText.getPaddingRight();
                 final int popupHeight = mPreviewText.getMeasuredHeight();
                 //mPreviewText.setVisibility(INVISIBLE);
-                mPopupPreviewX = mWordX[wordIndex] - mPreviewText.getPaddingLeft() - getScrollX();
+                mPopupPreviewX = mWordX[wordIndex] - mPreviewText.getPaddingLeft() - getScrollX()
+                        + (mWordWidth[wordIndex] - wordWidth) / 2;
                 mPopupPreviewY = - popupHeight;
                 mHandler.removeMessages(MSG_REMOVE_PREVIEW);
                 int [] offsetInWindow = new int[2];
@@ -488,6 +495,7 @@ public class CandidateView extends View {
     
     private void longPressFirstWord() {
         CharSequence word = mSuggestions.get(0);
+        if (word.length() < 2) return;
         if (mService.addWordToDictionary(word.toString())) {
             showPreview(0, getContext().getResources().getString(R.string.added_word, word));
         }
diff --git a/src/com/android/inputmethod/latin/KeyboardSwitcher.java b/src/com/android/inputmethod/latin/KeyboardSwitcher.java
index 0ebfe4a9ab..529edeb817 100644
--- a/src/com/android/inputmethod/latin/KeyboardSwitcher.java
+++ b/src/com/android/inputmethod/latin/KeyboardSwitcher.java
@@ -159,7 +159,6 @@ public class KeyboardSwitcher {
 
     void setVoiceMode(boolean enableVoice, boolean voiceOnPrimary) {
         if (enableVoice != mHasVoice || voiceOnPrimary != mVoiceOnPrimary) {
-            System.err.println("Clearing keyboards");
             mKeyboards.clear();
         }
         mHasVoice = enableVoice;
diff --git a/src/com/android/inputmethod/latin/LatinIME.java b/src/com/android/inputmethod/latin/LatinIME.java
index ee525b4952..9924561832 100644
--- a/src/com/android/inputmethod/latin/LatinIME.java
+++ b/src/com/android/inputmethod/latin/LatinIME.java
@@ -215,6 +215,7 @@ public class LatinIME extends InputMethodService
     private boolean mEnableVoice = true;
     private boolean mVoiceOnPrimary;
     private int     mOrientation;
+    private List<CharSequence> mSuggestPuncList;
 
     // Indicates whether the suggestion strip is to be on in landscape
     private boolean mJustAccepted;
@@ -296,6 +297,7 @@ public class LatinIME extends InputMethodService
         }
         initSuggest(inputLanguage);
         mOrientation = conf.orientation;
+        initSuggestPuncList();
 
         mVibrateDuration = mResources.getInteger(R.integer.vibrate_duration_ms);
 
@@ -1337,7 +1339,7 @@ public class LatinIME extends InputMethodService
         }
 
         if (!mPredicting) {
-            setSuggestions(null, false, false, false);
+            setNextSuggestions();
             return;
         }
 
@@ -1397,6 +1399,12 @@ public class LatinIME extends InputMethodService
             updateShiftKeyState(getCurrentInputEditorInfo());
             return;
         }
+
+        // If this is a punctuation, apply it through the normal key press
+        if (suggestion.length() == 1 && isWordSeparator(suggestion.charAt(0))) {
+            onKey(suggestion.charAt(0), null);
+            return;
+        }
         pickSuggestion(suggestion);
         TextEntryState.acceptedSuggestion(mComposing.toString(), suggestion);
         // Follow it with a space
@@ -1430,10 +1438,14 @@ public class LatinIME extends InputMethodService
         }
         mPredicting = false;
         mCommittedLength = suggestion.length();
-        setSuggestions(null, false, false, false);
+        setNextSuggestions();
         updateShiftKeyState(getCurrentInputEditorInfo());
     }
 
+    private void setNextSuggestions() {
+        setSuggestions(mSuggestPuncList, false, false, false);
+    }
+
     private boolean isCursorTouchingWord() {
         InputConnection ic = getCurrentInputConnection();
         if (ic == null) return false;
@@ -1767,6 +1779,16 @@ public class LatinIME extends InputMethodService
         return sp.getString(PREF_SELECTED_LANGUAGES, null);
     }
 
+    private void initSuggestPuncList() {
+        mSuggestPuncList = new ArrayList<CharSequence>();
+        String suggestPuncs = mResources.getString(R.string.suggested_punctuations);
+        if (suggestPuncs != null) {
+            for (int i = 0; i < suggestPuncs.length(); i++) {
+                mSuggestPuncList.add(suggestPuncs.subSequence(i, i + 1));
+            }
+        }
+    }
+
     private void showOptionsMenu() {
         AlertDialog.Builder builder = new AlertDialog.Builder(this);
         builder.setCancelable(true);
-- 
GitLab