diff options
author | 2011-02-10 17:31:37 +0900 | |
---|---|---|
committer | 2011-02-10 17:31:37 +0900 | |
commit | a7ea6cad8269c2b621dbf585ed90121b495f6ebc (patch) | |
tree | efe9106dacb45cf2cf4f17d80f870a030436ca53 /java/src/com/android/inputmethod/voice | |
parent | b7b83125544964bf128e07456edfd0170d889dc4 (diff) | |
parent | 03b5579b3b7380f8e12519cca1662317282c6bd9 (diff) | |
download | latinime-a7ea6cad8269c2b621dbf585ed90121b495f6ebc.tar.gz latinime-a7ea6cad8269c2b621dbf585ed90121b495f6ebc.tar.xz latinime-a7ea6cad8269c2b621dbf585ed90121b495f6ebc.zip |
Merge remote branch 'goog/master' into merge
Conflicts:
java/res/xml/method.xml
java/src/com/android/inputmethod/latin/LatinIME.java
java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
java/src/com/android/inputmethod/voice/VoiceIMEConnector.java
Change-Id: I9b62da120dcc08327fde92459ee5c7564a5eb6b8
Diffstat (limited to 'java/src/com/android/inputmethod/voice')
3 files changed, 135 insertions, 101 deletions
diff --git a/java/src/com/android/inputmethod/voice/RecognitionView.java b/java/src/com/android/inputmethod/voice/RecognitionView.java index 98db9365f..95a79f463 100644 --- a/java/src/com/android/inputmethod/voice/RecognitionView.java +++ b/java/src/com/android/inputmethod/voice/RecognitionView.java @@ -16,6 +16,9 @@ package com.android.inputmethod.voice; +import com.android.inputmethod.latin.R; +import com.android.inputmethod.latin.SubtypeSwitcher; + import android.content.Context; import android.content.res.Resources; import android.graphics.Bitmap; @@ -35,13 +38,11 @@ import android.widget.ImageView; import android.widget.ProgressBar; import android.widget.TextView; -import com.android.inputmethod.latin.R; - import java.io.ByteArrayOutputStream; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.ShortBuffer; -import java.util.List; +import java.util.Locale; /** * The user interface for the "Speak now" and "working" states. @@ -60,6 +61,7 @@ public class RecognitionView { private ImageView mImage; private View mProgress; private SoundIndicator mSoundIndicator; + private TextView mLanguage; private Button mButton; private Drawable mInitializing; @@ -105,6 +107,7 @@ public class RecognitionView { mButton = (Button) mView.findViewById(R.id.button); mButton.setOnClickListener(clickListener); mText = (TextView) mView.findViewById(R.id.text); + mLanguage = (TextView) mView.findViewById(R.id.language); mContext = context; } @@ -184,9 +187,14 @@ public class RecognitionView { private void prepareDialog(CharSequence text, Drawable image, CharSequence btnTxt) { + + /* + * The mic of INIT and of LISTENING has to be displayed in the same position. To accomplish + * that, some text visibility are not set as GONE but as INVISIBLE. + */ switch (mState) { case INIT: - mText.setVisibility(View.GONE); + mText.setVisibility(View.INVISIBLE); mProgress.setVisibility(View.GONE); @@ -196,6 +204,8 @@ public class RecognitionView { mSoundIndicator.setVisibility(View.GONE); mSoundIndicator.stop(); + mLanguage.setVisibility(View.INVISIBLE); + mPopupLayout.setBackgroundDrawable(mListeningBorder); break; case LISTENING: @@ -209,6 +219,11 @@ public class RecognitionView { mSoundIndicator.setVisibility(View.VISIBLE); mSoundIndicator.start(); + Locale locale = SubtypeSwitcher.getInstance().getInputLocale(); + + mLanguage.setVisibility(View.VISIBLE); + mLanguage.setText(SubtypeSwitcher.getFullDisplayName(locale, true)); + mPopupLayout.setBackgroundDrawable(mListeningBorder); break; case WORKING: @@ -223,6 +238,8 @@ public class RecognitionView { mSoundIndicator.setVisibility(View.GONE); mSoundIndicator.stop(); + mLanguage.setVisibility(View.GONE); + mPopupLayout.setBackgroundDrawable(mWorkingBorder); break; case READY: @@ -237,6 +254,8 @@ public class RecognitionView { mSoundIndicator.setVisibility(View.GONE); mSoundIndicator.stop(); + mLanguage.setVisibility(View.GONE); + mPopupLayout.setBackgroundDrawable(mErrorBorder); break; default: diff --git a/java/src/com/android/inputmethod/voice/VoiceIMEConnector.java b/java/src/com/android/inputmethod/voice/VoiceIMEConnector.java index fe6e318c9..267bef21d 100644 --- a/java/src/com/android/inputmethod/voice/VoiceIMEConnector.java +++ b/java/src/com/android/inputmethod/voice/VoiceIMEConnector.java @@ -20,6 +20,7 @@ import com.android.inputmethod.keyboard.KeyboardSwitcher; import com.android.inputmethod.latin.EditingUtils; import com.android.inputmethod.latin.LatinIME; import com.android.inputmethod.latin.LatinIME.UIHandler; +import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.SharedPreferencesCompat; import com.android.inputmethod.latin.SubtypeSwitcher; @@ -32,19 +33,18 @@ import android.content.Intent; import android.content.SharedPreferences; import android.content.res.Configuration; import android.net.Uri; +import android.os.AsyncTask; 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.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; @@ -78,9 +78,10 @@ public class VoiceIMEConnector implements VoiceInput.UiListener { // given text field. For instance this is specified by the search dialog when the // dialog is already showing a voice search button. 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 final boolean DEBUG = LatinImeLogger.sDBG; private boolean mAfterVoiceInput; private boolean mHasUsedVoiceInput; @@ -172,7 +173,7 @@ public class VoiceIMEConnector implements VoiceInput.UiListener { 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() { @@ -210,90 +211,80 @@ public class VoiceIMEConnector implements VoiceInput.UiListener { 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; + @Override + public AlertDialog.Builder setMessage(CharSequence message) { + return super.setMessage(replaceURLSpan(message)); + } + + 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 ssb; } - // 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])); + 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()); } - return true; - } else { - Selection.removeSelection(buffer); } + }); + 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); } - return super.onTouchEvent(widget, buffer, event); } } @@ -437,7 +428,7 @@ public class VoiceIMEConnector implements VoiceInput.UiListener { builder.addWord(word); } } else { - builder.addWords(suggestions); + builder.addWords(suggestions, null); } builder.setTypedWordValid(true).setHasMinimalSuggestion(true); mService.setSuggestions(builder.build()); @@ -543,10 +534,14 @@ public class VoiceIMEConnector implements VoiceInput.UiListener { // As we add mm, we don't know how the rounding is going to work // thus we may end up with few pixels extra (or less). if (keyboardView != null) { - int h = keyboardView.getHeight(); - if (h > 0) { - View popupLayout = v.findViewById(R.id.popup_layout); - popupLayout.getLayoutParams().height = h; + View popupLayout = v.findViewById(R.id.popup_layout); + final int displayHeight = + mService.getResources().getDisplayMetrics().heightPixels; + final int currentHeight = popupLayout.getLayoutParams().height; + final int keyboardHeight = keyboardView.getHeight(); + if (keyboardHeight > currentHeight || keyboardHeight + > (displayHeight / RECOGNITIONVIEW_HEIGHT_THRESHOLD_RATIO)) { + popupLayout.getLayoutParams().height = keyboardHeight; } } mService.setInputView(v); @@ -645,14 +640,17 @@ public class VoiceIMEConnector implements VoiceInput.UiListener { } } - public void onStartInputView(IBinder token) { + public void onStartInputView(IBinder keyboardViewToken) { + // If keyboardViewToken is null, keyboardView is not attached but voiceView is attached. + IBinder windowToken = keyboardViewToken != null ? keyboardViewToken + : mVoiceInput.getView().getWindowToken(); // If IME is in voice mode, but still needs to show the voice warning dialog, // keep showing the warning. - if (mSubtypeSwitcher.isVoiceMode() && token != null) { + if (mSubtypeSwitcher.isVoiceMode() && windowToken != null) { // Close keyboard view if it is been shown. if (KeyboardSwitcher.getInstance().isInputViewShown()) KeyboardSwitcher.getInstance().getInputView().purgeKeyboardAndClosing(); - startListening(false, token); + startListening(false, windowToken); } // If we have no token, onAttachedToWindow will take care of showing dialog and start // listening. @@ -701,7 +699,7 @@ public class VoiceIMEConnector implements VoiceInput.UiListener { mHandler.updateVoiceResults(); } - public FieldContext makeFieldContext() { + private FieldContext makeFieldContext() { SubtypeSwitcher switcher = SubtypeSwitcher.getInstance(); return new FieldContext(mService.getCurrentInputConnection(), mService.getCurrentInputEditorInfo(), switcher.getInputLocaleStr(), diff --git a/java/src/com/android/inputmethod/voice/VoiceInput.java b/java/src/com/android/inputmethod/voice/VoiceInput.java index ffa349fde..2df9e8588 100644 --- a/java/src/com/android/inputmethod/voice/VoiceInput.java +++ b/java/src/com/android/inputmethod/voice/VoiceInput.java @@ -17,6 +17,7 @@ package com.android.inputmethod.voice; import com.android.inputmethod.latin.EditingUtils; +import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.R; import android.content.ContentResolver; @@ -58,6 +59,7 @@ public class VoiceInput implements OnClickListener { private static final String EXTRA_CALLING_PACKAGE = "calling_package"; private static final String EXTRA_ALTERNATES = "android.speech.extra.ALTERNATES"; private static final int MAX_ALT_LIST_LENGTH = 6; + private static boolean DBG = LatinImeLogger.sDBG; private static final String DEFAULT_RECOMMENDED_PACKAGES = "com.android.mms " + @@ -128,19 +130,14 @@ public class VoiceInput implements OnClickListener { private int mState = DEFAULT; - private final static int MSG_CLOSE_ERROR_DIALOG = 1; - - private final static int MSG_RESET = 2; + private final static int MSG_RESET = 1; private final Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { - if (msg.what == MSG_RESET || msg.what == MSG_CLOSE_ERROR_DIALOG) { + if (msg.what == MSG_RESET) { mState = DEFAULT; mRecognitionView.finish(); - } - - if (msg.what == MSG_CLOSE_ERROR_DIALOG) { mUiListener.onCancelVoice(); } } @@ -313,8 +310,18 @@ public class VoiceInput implements OnClickListener { * @param swipe whether this voice input was started by swipe, for logging purposes */ public void startListening(FieldContext context, boolean swipe) { - mState = DEFAULT; - + if (DBG) { + Log.d(TAG, "startListening: " + context); + } + + if (mState != DEFAULT) { + Log.w(TAG, "startListening in the wrong status " + mState); + } + + // If everything works ok, the voice input should be already in the correct state. As this + // class can be called by third-party, we call reset just to be on the safe side. + reset(); + Locale locale = Locale.getDefault(); String localeString = locale.getLanguage() + "-" + locale.getCountry(); @@ -499,6 +506,21 @@ public class VoiceInput implements OnClickListener { } /** + * Reset the current voice recognition. + */ + public void reset() { + if (mState != DEFAULT) { + mState = DEFAULT; + + // Remove all pending tasks (e.g., timers to cancel voice input) + mHandler.removeMessages(MSG_RESET); + + mSpeechRecognizer.cancel(); + mRecognitionView.finish(); + } + } + + /** * Cancel in-progress speech recognition. */ public void cancel() { @@ -513,14 +535,9 @@ public class VoiceInput implements OnClickListener { mLogger.cancelDuringError(); break; } - mState = DEFAULT; - - // Remove all pending tasks (e.g., timers to cancel voice input) - mHandler.removeMessages(MSG_RESET); - mSpeechRecognizer.cancel(); + reset(); mUiListener.onCancelVoice(); - mRecognitionView.finish(); } private int getErrorStringId(int errorType, boolean endpointed) { @@ -555,7 +572,7 @@ public class VoiceInput implements OnClickListener { mState = ERROR; mRecognitionView.showError(error); // Wait a couple seconds and then automatically dismiss message. - mHandler.sendMessageDelayed(Message.obtain(mHandler, MSG_CLOSE_ERROR_DIALOG), 2000); + mHandler.sendMessageDelayed(Message.obtain(mHandler, MSG_RESET), 2000); } private class ImeRecognitionListener implements RecognitionListener { |