diff --git a/java/res/values-xlarge/bools.xml b/java/res/values-xlarge/bools.xml index 66cfd9d79..8c68d9dc3 100644 --- a/java/res/values-xlarge/bools.xml +++ b/java/res/values-xlarge/bools.xml @@ -21,4 +21,5 @@ false false + false diff --git a/java/res/values/bools.xml b/java/res/values/bools.xml index 64d05bd17..2be95458e 100644 --- a/java/res/values/bools.xml +++ b/java/res/values/bools.xml @@ -31,4 +31,5 @@ true true true + true diff --git a/java/res/values/strings.xml b/java/res/values/strings.xml index ff2209887..63c9f4213 100644 --- a/java/res/values/strings.xml +++ b/java/res/values/strings.xml @@ -248,15 +248,15 @@ - Voice input is an experimental feature using Google\'s networked speech recognition. + Voice input uses Google\'s speech recognition. The Mobile Privacy Policy applies. - To turn off voice input, go to keyboard settings. - - - To use voice input, press the microphone button or slide your finger across the on-screen keyboard. + To turn off voice input, go to input method settings. + + + To use voice input, press the microphone button. Speak now diff --git a/java/src/com/android/inputmethod/latin/LatinIMESettings.java b/java/src/com/android/inputmethod/latin/LatinIMESettings.java index 669192f59..ae6646113 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.PreferenceActivity; 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 @@ public class LatinIMESettings extends PreferenceActivity private CheckBoxPreference mBigramSuggestion; private boolean mVoiceOn; + private AlertDialog mDialog; + private VoiceInputLogger mLogger; private boolean mOkClicked = false; @@ -91,8 +96,15 @@ public class LatinIMESettings extends PreferenceActivity 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 @@ public class LatinIMESettings extends PreferenceActivity 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 @@ public class LatinIMESettings extends PreferenceActivity 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 fd9559dae..5574a21de 100644 --- a/java/src/com/android/inputmethod/voice/VoiceIMEConnector.java +++ b/java/src/com/android/inputmethod/voice/VoiceIMEConnector.java @@ -27,11 +27,23 @@ import com.android.inputmethod.latin.SubtypeSwitcher; 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.EditorInfo; 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 @@ public class VoiceIMEConnector implements VoiceInput.UiListener { 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 @@ public class VoiceIMEConnector implements VoiceInput.UiListener { } }); + 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 @@ public class VoiceIMEConnector implements VoiceInput.UiListener { 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() {