aboutsummaryrefslogtreecommitdiffstats
path: root/java/src/com/android/inputmethod/voice
diff options
context:
space:
mode:
authorsatok <satok@google.com>2011-02-10 17:31:37 +0900
committersatok <satok@google.com>2011-02-10 17:31:37 +0900
commita7ea6cad8269c2b621dbf585ed90121b495f6ebc (patch)
treeefe9106dacb45cf2cf4f17d80f870a030436ca53 /java/src/com/android/inputmethod/voice
parentb7b83125544964bf128e07456edfd0170d889dc4 (diff)
parent03b5579b3b7380f8e12519cca1662317282c6bd9 (diff)
downloadlatinime-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')
-rw-r--r--java/src/com/android/inputmethod/voice/RecognitionView.java27
-rw-r--r--java/src/com/android/inputmethod/voice/VoiceIMEConnector.java160
-rw-r--r--java/src/com/android/inputmethod/voice/VoiceInput.java49
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 {