Merge "Cleanup keyboard drawing code"
diff --git a/java/src/com/android/inputmethod/latin/CandidateView.java b/java/src/com/android/inputmethod/latin/CandidateView.java
index fc45c7c..9699ad1 100644
--- a/java/src/com/android/inputmethod/latin/CandidateView.java
+++ b/java/src/com/android/inputmethod/latin/CandidateView.java
@@ -54,7 +54,7 @@
     private static final CharacterStyle UNDERLINE_SPAN = new UnderlineSpan();
     private static final int MAX_SUGGESTIONS = 16;
 
-    private static boolean DBG = LatinImeLogger.sDBG;
+    private static final boolean DBG = LatinImeLogger.sDBG;
 
     private final ArrayList<View> mWords = new ArrayList<View>();
     private final boolean mConfigCandidateHighlightFontColorEnabled;
@@ -226,10 +226,14 @@
                 }
                 final String debugString = info.getDebugString();
                 if (DBG) {
-                    if (!TextUtils.isEmpty(debugString)) {
+                    if (TextUtils.isEmpty(debugString)) {
+                        dv.setVisibility(GONE);
+                    } else {
                         dv.setText(debugString);
                         dv.setVisibility(VISIBLE);
                     }
+                } else {
+                    dv.setVisibility(GONE);
                 }
             } else {
                 dv.setVisibility(GONE);
@@ -249,8 +253,10 @@
         final TextView tv = (TextView)mWords.get(1).findViewById(R.id.candidate_word);
         final Spannable word = new SpannableString(autoCorrectedWord);
         final int wordLength = word.length();
-        word.setSpan(mInvertedBackgroundColorSpan, 0, wordLength, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
-        word.setSpan(mInvertedForegroundColorSpan, 0, wordLength, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
+        word.setSpan(mInvertedBackgroundColorSpan, 0, wordLength,
+                Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
+        word.setSpan(mInvertedForegroundColorSpan, 0, wordLength,
+                Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
         tv.setText(word);
         mShowingAutoCorrectionInverted = true;
     }
diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java
index ced355b..7bd6d91 100644
--- a/java/src/com/android/inputmethod/latin/Suggest.java
+++ b/java/src/com/android/inputmethod/latin/Suggest.java
@@ -64,7 +64,7 @@
 
     static final int LARGE_DICTIONARY_THRESHOLD = 200 * 1000;
 
-    private static boolean DBG = LatinImeLogger.sDBG;
+    private static final boolean DBG = LatinImeLogger.sDBG;
 
     private BinaryDictionary mMainDict;
 
@@ -224,6 +224,7 @@
             mLowerOriginalWord = "";
         }
 
+        double normalizedScore = Integer.MIN_VALUE;
         if (wordComposer.size() == 1 && (mCorrectionMode == CORRECTION_FULL_BIGRAM
                 || mCorrectionMode == CORRECTION_BASIC)) {
             // At first character typed, search only the bigrams
@@ -290,9 +291,9 @@
                     && mSuggestions.size() > 0 && mPriorities.length > 0) {
                 // TODO: when the normalized score of the first suggestion is nearly equals to
                 //       the normalized score of the second suggestion, behave less aggressive.
-                final double normalizedScore = Utils.calcNormalizedScore(
+                normalizedScore = Utils.calcNormalizedScore(
                         typedWord, mSuggestions.get(0), mPriorities[0]);
-                if (LatinImeLogger.sDBG) {
+                if (DBG) {
                     Log.d(TAG, "Normalized " + typedWord + "," + mSuggestions.get(0) + ","
                             + mPriorities[0] + ", " + normalizedScore
                             + "(" + mAutoCorrectionThreshold + ")");
@@ -354,7 +355,30 @@
             }
         }
         removeDupes();
-        return new SuggestedWords.Builder().addWords(mSuggestions, null);
+        if (DBG) {
+            ArrayList<SuggestedWords.SuggestedWordInfo> frequencyInfoList =
+                    new ArrayList<SuggestedWords.SuggestedWordInfo>();
+            frequencyInfoList.add(new SuggestedWords.SuggestedWordInfo("+", false));
+            final int priorityLength = mPriorities.length;
+            for (int i = 0; i < priorityLength; ++i) {
+                if (normalizedScore > 0) {
+                    final String priorityThreshold = Integer.toString(mPriorities[i]) + " (" +
+                            normalizedScore + ")";
+                    frequencyInfoList.add(
+                            new SuggestedWords.SuggestedWordInfo(priorityThreshold, false));
+                    normalizedScore = 0.0;
+                } else {
+                    final String priority = Integer.toString(mPriorities[i]);
+                    frequencyInfoList.add(new SuggestedWords.SuggestedWordInfo(priority, false));
+                }
+            }
+            for (int i = priorityLength; i < mSuggestions.size(); ++i) {
+                frequencyInfoList.add(new SuggestedWords.SuggestedWordInfo("--", false));
+            }
+            return new SuggestedWords.Builder().addWords(mSuggestions, frequencyInfoList);
+        } else {
+            return new SuggestedWords.Builder().addWords(mSuggestions, null);
+        }
     }
 
     public int[] getNextLettersFrequencies() {
diff --git a/java/src/com/android/inputmethod/voice/VoiceIMEConnector.java b/java/src/com/android/inputmethod/voice/VoiceIMEConnector.java
index 61a194a..4ceaa1b 100644
--- a/java/src/com/android/inputmethod/voice/VoiceIMEConnector.java
+++ b/java/src/com/android/inputmethod/voice/VoiceIMEConnector.java
@@ -38,16 +38,13 @@
 import android.preference.PreferenceManager;
 import android.provider.Browser;
 import android.speech.SpeechRecognizer;
-import android.text.Layout;
-import android.text.Selection;
-import android.text.Spannable;
+import android.text.SpannableStringBuilder;
+import android.text.Spanned;
 import android.text.TextUtils;
 import android.text.method.LinkMovementMethod;
-import android.text.style.ClickableSpan;
 import android.text.style.URLSpan;
 import android.util.Log;
 import android.view.LayoutInflater;
-import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewParent;
@@ -83,8 +80,7 @@
     private static final String IME_OPTION_NO_MICROPHONE = "nm";
     private static final int RECOGNITIONVIEW_HEIGHT_THRESHOLD_RATIO = 6;
 
-    @SuppressWarnings("unused")
-    private static final String TAG = "VoiceIMEConnector";
+    private static final String TAG = VoiceIMEConnector.class.getSimpleName();
     private static boolean DEBUG = LatinImeLogger.sDBG;
 
     private boolean mAfterVoiceInput;
@@ -177,7 +173,7 @@
         if (mVoiceWarningDialog != null && mVoiceWarningDialog.isShowing()) {
             return;
         }
-        AlertDialog.Builder builder = new AlertDialog.Builder(mService);
+        AlertDialog.Builder builder = new UrlLinkAlertDialogBuilder(mService);
         builder.setCancelable(true);
         builder.setIcon(R.drawable.ic_mic_dialog);
         builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@@ -215,90 +211,80 @@
                                     mService.getText(R.string.voice_warning_how_to_turn_off));
         }
         builder.setMessage(message);
-
         builder.setTitle(R.string.voice_warning_title);
         mVoiceWarningDialog = builder.create();
-        Window window = mVoiceWarningDialog.getWindow();
-        WindowManager.LayoutParams lp = window.getAttributes();
+        final Window window = mVoiceWarningDialog.getWindow();
+        final WindowManager.LayoutParams lp = window.getAttributes();
         lp.token = token;
         lp.type = WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
         window.setAttributes(lp);
         window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
         mVoiceInput.logKeyboardWarningDialogShown();
         mVoiceWarningDialog.show();
-        // Make URL in the dialog message clickable
-        TextView textView = (TextView) mVoiceWarningDialog.findViewById(android.R.id.message);
-        if (textView != null) {
-            final CustomLinkMovementMethod method = CustomLinkMovementMethod.getInstance();
-            method.setVoiceWarningDialog(mVoiceWarningDialog);
-            textView.setMovementMethod(method);
-        }
     }
 
-    private static class CustomLinkMovementMethod extends LinkMovementMethod {
-        private static CustomLinkMovementMethod sLinkMovementMethodInstance =
-                new CustomLinkMovementMethod();
+    private static class UrlLinkAlertDialogBuilder extends AlertDialog.Builder {
         private AlertDialog mAlertDialog;
 
-        public void setVoiceWarningDialog(AlertDialog alertDialog) {
-            mAlertDialog = alertDialog;
+        public UrlLinkAlertDialogBuilder(Context context) {
+            super(context);
         }
 
-        public static CustomLinkMovementMethod getInstance() {
-            return sLinkMovementMethodInstance;
-        }
-
-        // Almost the same as LinkMovementMethod.onTouchEvent(), but overrides it for
-        // FLAG_ACTIVITY_NEW_TASK and mAlertDialog.cancel().
         @Override
-        public boolean onTouchEvent(TextView widget, Spannable buffer, MotionEvent event) {
-            int action = event.getAction();
+        public AlertDialog.Builder setMessage(CharSequence message) {
+            return super.setMessage(replaceURLSpan(message));
+        }
 
-            if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_DOWN) {
-                int x = (int) event.getX();
-                int y = (int) event.getY();
-
-                x -= widget.getTotalPaddingLeft();
-                y -= widget.getTotalPaddingTop();
-
-                x += widget.getScrollX();
-                y += widget.getScrollY();
-
-                Layout layout = widget.getLayout();
-                int line = layout.getLineForVertical(y);
-                int off = layout.getOffsetForHorizontal(line, x);
-
-                ClickableSpan[] link = buffer.getSpans(off, off, ClickableSpan.class);
-
-                if (link.length != 0) {
-                    if (action == MotionEvent.ACTION_UP) {
-                        if (link[0] instanceof URLSpan) {
-                            URLSpan urlSpan = (URLSpan) link[0];
-                            Uri uri = Uri.parse(urlSpan.getURL());
-                            Context context = widget.getContext();
-                            Intent intent = new Intent(Intent.ACTION_VIEW, uri);
-                            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-                            intent.putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName());
-                            if (mAlertDialog != null) {
-                                // Go back to the previous IME for now.
-                                // TODO: If we can find a way to bring the new activity to front
-                                // while keeping the warning dialog, we don't need to cancel here.
-                                mAlertDialog.cancel();
-                            }
-                            context.startActivity(intent);
-                        } else {
-                            link[0].onClick(widget);
-                        }
-                    } else if (action == MotionEvent.ACTION_DOWN) {
-                        Selection.setSelection(buffer, buffer.getSpanStart(link[0]),
-                                buffer.getSpanEnd(link[0]));
-                    }
-                    return true;
-                } else {
-                    Selection.removeSelection(buffer);
-                }
+        private Spanned replaceURLSpan(CharSequence message) {
+            // Replace all spans with the custom span
+            final SpannableStringBuilder ssb = new SpannableStringBuilder(message);
+            for (URLSpan span : ssb.getSpans(0, ssb.length(), URLSpan.class)) {
+                int spanStart = ssb.getSpanStart(span);
+                int spanEnd = ssb.getSpanEnd(span);
+                int spanFlags = ssb.getSpanFlags(span);
+                ssb.removeSpan(span);
+                ssb.setSpan(new ClickableSpan(span.getURL()), spanStart, spanEnd, spanFlags);
             }
-            return super.onTouchEvent(widget, buffer, event);
+            return ssb;
+        }
+
+        @Override
+        public AlertDialog create() {
+            final AlertDialog dialog = super.create();
+
+            dialog.setOnShowListener(new DialogInterface.OnShowListener() {
+                @Override
+                public void onShow(DialogInterface dialogInterface) {
+                    // Make URL in the dialog message click-able.
+                    TextView textView = (TextView) mAlertDialog.findViewById(android.R.id.message);
+                    if (textView != null) {
+                        textView.setMovementMethod(LinkMovementMethod.getInstance());
+                    }
+                }
+            });
+            mAlertDialog = dialog;
+            return dialog;
+        }
+
+        class ClickableSpan extends URLSpan {
+            public ClickableSpan(String url) {
+                super(url);
+            }
+
+            @Override
+            public void onClick(View widget) {
+                Uri uri = Uri.parse(getURL());
+                Context context = widget.getContext();
+                Intent intent = new Intent(Intent.ACTION_VIEW, uri);
+                // Add this flag to start an activity from service
+                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                intent.putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName());
+                // Dismiss the warning dialog and go back to the previous IME.
+                // TODO: If we can find a way to bring the new activity to front while keeping
+                // the warning dialog, we don't need to dismiss it here.
+                mAlertDialog.cancel();
+                context.startActivity(intent);
+            }
         }
     }