Merge "Tweak tablet keyboard design"
diff --git a/java/res/values-xlarge/bools.xml b/java/res/values-xlarge/bools.xml
index 66cfd9d..8c68d9d 100644
--- a/java/res/values-xlarge/bools.xml
+++ b/java/res/values-xlarge/bools.xml
@@ -21,4 +21,5 @@
     <!-- Whether or not Popup on key press is enabled by default -->
     <bool name="default_popup_preview">false</bool>
     <bool name="config_enable_show_settings_key_option">false</bool>
+    <bool name="config_enable_show_voice_key_option">false</bool>
 </resources>
diff --git a/java/res/values/bools.xml b/java/res/values/bools.xml
index 64d05bd..2be9545 100644
--- a/java/res/values/bools.xml
+++ b/java/res/values/bools.xml
@@ -31,4 +31,5 @@
     <bool name="default_recorrection_enabled">true</bool>
     <bool name="config_long_press_comma_for_settings_enabled">true</bool>
     <bool name="config_enable_show_settings_key_option">true</bool>
+    <bool name="config_enable_show_voice_key_option">true</bool>
 </resources>
diff --git a/java/res/values/strings.xml b/java/res/values/strings.xml
index ff22098..63c9f42 100644
--- a/java/res/values/strings.xml
+++ b/java/res/values/strings.xml
@@ -248,15 +248,15 @@
     
     <!-- Message of the warning dialog that shows when a user initiates voice input for
          the first time, or turns it on in settings. -->
-    <string name="voice_warning_may_not_understand">Voice input is an experimental feature using Google\'s networked speech recognition.</string>
+    <string name="voice_warning_may_not_understand">Voice input uses Google\'s speech recognition. <a href="http://m.google.com/privacy">The Mobile Privacy Policy</a> applies.</string>
     
     <!-- An additional part of the warning dialog for voice input that only shows when the user
          actually initiates voice input, rather than just turning it on in settings. -->
-    <string name="voice_warning_how_to_turn_off">To turn off voice input, go to keyboard settings.</string>
-    
-    <!-- Message to show when user clicks the swiping hint (which says
-        "Swipe across keyboard to speak"). Also shown when enabling settings. -->
-    <string name="voice_hint_dialog_message">To use voice input, press the microphone button or slide your finger across the on-screen keyboard.</string>
+    <string name="voice_warning_how_to_turn_off">To turn off voice input, go to input method settings.</string>
+
+    <!-- Message to show when user enables the voice input settings (which says
+        "Press the microphone button"). -->
+    <string name="voice_hint_dialog_message">To use voice input, press the microphone button.</string>
     
     <!-- Short message to tell the user the system is ready for them to speak. -->
     <string name="voice_listening">Speak now</string>
diff --git a/java/src/com/android/inputmethod/latin/LatinIMESettings.java b/java/src/com/android/inputmethod/latin/LatinIMESettings.java
index 669192f..ae66461 100644
--- a/java/src/com/android/inputmethod/latin/LatinIMESettings.java
+++ b/java/src/com/android/inputmethod/latin/LatinIMESettings.java
@@ -32,7 +32,10 @@
 import android.preference.PreferenceGroup;
 import android.speech.SpeechRecognizer;
 import android.text.AutoText;
+import android.text.TextUtils;
+import android.text.method.LinkMovementMethod;
 import android.util.Log;
+import android.widget.TextView;
 
 import java.util.Locale;
 
@@ -60,6 +63,8 @@
     private CheckBoxPreference mBigramSuggestion;
     private boolean mVoiceOn;
 
+    private AlertDialog mDialog;
+
     private VoiceInputLogger mLogger;
 
     private boolean mOkClicked = false;
@@ -91,8 +96,15 @@
 
         final boolean showSettingsKeyOption = getResources().getBoolean(
                 R.bool.config_enable_show_settings_key_option);
-        if (!showSettingsKeyOption)
+        if (!showSettingsKeyOption) {
             getPreferenceScreen().removePreference(mSettingsKeyPreference);
+        }
+
+        final boolean showVoiceKeyOption = getResources().getBoolean(
+                R.bool.config_enable_show_voice_key_option);
+        if (!showVoiceKeyOption) {
+            getPreferenceScreen().removePreference(mVoicePreference);
+        }
 
         Vibrator vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE);
         if (vibrator == null || !vibrator.hasVibrator()) {
@@ -149,6 +161,13 @@
     private void showVoiceConfirmation() {
         mOkClicked = false;
         showDialog(VOICE_INPUT_CONFIRM_DIALOG);
+        // Make URL in the dialog message clickable
+        if (mDialog != null) {
+            TextView textView = (TextView) mDialog.findViewById(android.R.id.message);
+            if (textView != null) {
+                textView.setMovementMethod(LinkMovementMethod.getInstance());
+            }
+        }
     }
 
     private void updateVoiceModeSummary() {
@@ -184,18 +203,20 @@
                 boolean localeSupported = SubtypeSwitcher.getInstance().isVoiceSupported(
                         Locale.getDefault().toString());
 
+                final CharSequence message;
                 if (localeSupported) {
-                    String message = getString(R.string.voice_warning_may_not_understand) + "\n\n" +
-                            getString(R.string.voice_hint_dialog_message);
-                    builder.setMessage(message);
+                    message = TextUtils.concat(
+                            getText(R.string.voice_warning_may_not_understand), "\n\n",
+                                    getText(R.string.voice_hint_dialog_message));
                 } else {
-                    String message = getString(R.string.voice_warning_locale_not_supported) +
-                            "\n\n" + getString(R.string.voice_warning_may_not_understand) + "\n\n" +
-                            getString(R.string.voice_hint_dialog_message);
-                    builder.setMessage(message);
+                    message = TextUtils.concat(
+                            getText(R.string.voice_warning_locale_not_supported), "\n\n",
+                                    getText(R.string.voice_warning_may_not_understand), "\n\n",
+                                            getText(R.string.voice_hint_dialog_message));
                 }
-
+                builder.setMessage(message);
                 AlertDialog dialog = builder.create();
+                mDialog = dialog;
                 dialog.setOnDismissListener(this);
                 mLogger.settingsWarningDialogShown();
                 return dialog;
diff --git a/java/src/com/android/inputmethod/voice/VoiceIMEConnector.java b/java/src/com/android/inputmethod/voice/VoiceIMEConnector.java
index fd9559d..5574a21 100644
--- a/java/src/com/android/inputmethod/voice/VoiceIMEConnector.java
+++ b/java/src/com/android/inputmethod/voice/VoiceIMEConnector.java
@@ -27,11 +27,23 @@
 import android.app.AlertDialog;
 import android.content.Context;
 import android.content.DialogInterface;
+import android.content.Intent;
 import android.content.SharedPreferences;
+import android.net.Uri;
 import android.os.IBinder;
 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.TextUtils;
+import android.text.method.LinkMovementMethod;
+import android.text.method.MovementMethod;
+import android.text.style.ClickableSpan;
+import android.text.style.URLSpan;
 import android.view.LayoutInflater;
+import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewParent;
@@ -41,6 +53,7 @@
 import android.view.inputmethod.ExtractedTextRequest;
 import android.view.inputmethod.InputConnection;
 import android.view.inputmethod.InputMethodManager;
+import android.widget.TextView;
 
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -173,7 +186,7 @@
                 switchToLastInputMethod();
             }
         });
-        // When the dialog is dismissed by user's cancellation, swith back to the last input method.
+        // When the dialog is dismissed by user's cancellation, switch back to the last input method
         builder.setOnCancelListener(new DialogInterface.OnCancelListener() {
             @Override
             public void onCancel(DialogInterface arg0) {
@@ -182,16 +195,18 @@
             }
         });
 
+        final CharSequence message;
         if (mLocaleSupportedForVoiceInput) {
-            String message = mContext.getString(R.string.voice_warning_may_not_understand)
-                    + "\n\n" + mContext.getString(R.string.voice_warning_how_to_turn_off);
-            builder.setMessage(message);
+            message = TextUtils.concat(
+                    mContext.getText(R.string.voice_warning_may_not_understand), "\n\n",
+                            mContext.getText(R.string.voice_warning_how_to_turn_off));
         } else {
-            String message = mContext.getString(R.string.voice_warning_locale_not_supported)
-                    + "\n\n" + mContext.getString(R.string.voice_warning_may_not_understand)
-                    + "\n\n" + mContext.getString(R.string.voice_warning_how_to_turn_off);
-            builder.setMessage(message);
+            message = TextUtils.concat(
+                    mContext.getText(R.string.voice_warning_locale_not_supported), "\n\n",
+                            mContext.getText(R.string.voice_warning_may_not_understand), "\n\n",
+                                    mContext.getText(R.string.voice_warning_how_to_turn_off));
         }
+        builder.setMessage(message);
 
         builder.setTitle(R.string.voice_warning_title);
         mVoiceWarningDialog = builder.create();
@@ -203,6 +218,79 @@
         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 sInstance = new CustomLinkMovementMethod();
+        private AlertDialog mAlertDialog;
+
+        public void setVoiceWarningDialog(AlertDialog alertDialog) {
+            mAlertDialog = alertDialog;
+        }
+
+        public static CustomLinkMovementMethod getInstance() {
+            return sInstance;
+        }
+
+        // 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();
+
+            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);
+                }
+            }
+            return super.onTouchEvent(widget, buffer, event);
+        }
     }
 
     public void showPunctuationHintIfNecessary() {
diff --git a/native/Android.mk b/native/Android.mk
index b294469..97051c9 100644
--- a/native/Android.mk
+++ b/native/Android.mk
@@ -8,11 +8,22 @@
 	src/dictionary.cpp \
 	src/char_utils.cpp
 
-LOCAL_NDK_VERSION := 4
+#FLAG_DBG := true
+
+ifneq ($(FLAG_DBG), true)
+    LOCAL_NDK_VERSION := 4
+endif
+
 LOCAL_SDK_VERSION := 8
 
 LOCAL_MODULE := libjni_latinime
 
 LOCAL_MODULE_TAGS := user
 
+ifeq ($(FLAG_DBG), true)
+    $(warning "Making debug build.")
+    LOCAL_CFLAGS += -DFLAG_DBG
+    LOCAL_SHARED_LIBRARIES := libcutils libutils
+endif
+
 include $(BUILD_SHARED_LIBRARY)
diff --git a/native/src/dictionary.cpp b/native/src/dictionary.cpp
index 1a39f585b4..7f58fc1 100644
--- a/native/src/dictionary.cpp
+++ b/native/src/dictionary.cpp
@@ -19,15 +19,20 @@
 #include <fcntl.h>
 #include <sys/mman.h>
 #include <string.h>
-//#define LOG_TAG "dictionary.cpp"
-//#include <cutils/log.h>
+
+#ifdef FLAG_DBG
+#define LOG_TAG "LatinIME: dictionary.cpp"
+#include <cutils/log.h>
+#define DEBUG_DICT 1
+#else // FLAG_DBG
 #define LOGI
+#define DEBUG_DICT 0
+#endif // FLAG_DBG
 
 #include "dictionary.h"
 #include "basechars.h"
 #include "char_utils.h"
 
-#define DEBUG_DICT 0
 #define DICTIONARY_VERSION_MIN 200
 #define DICTIONARY_HEADER_SIZE 2
 #define NOT_VALID_WORD -99
@@ -36,6 +41,7 @@
 
 Dictionary::Dictionary(void *dict, int typedLetterMultiplier, int fullWordMultiplier)
 {
+    LOGI("Dictionary - constructor");
     mDict = (unsigned char*) dict;
     mTypedLetterMultiplier = typedLetterMultiplier;
     mFullWordMultiplier = fullWordMultiplier;