aboutsummaryrefslogtreecommitdiffstats
path: root/java/src/com/android/inputmethod/deprecated/voice/RecognitionView.java
diff options
context:
space:
mode:
authorsatok <satok@google.com>2011-03-16 14:57:08 -0700
committersatok <satok@google.com>2011-03-16 23:00:59 -0700
commit9807ab27eac3a10b299382af8280eb54dca50608 (patch)
tree126e1eaecacc892a55abb06062d3041a88352dd4 /java/src/com/android/inputmethod/deprecated/voice/RecognitionView.java
parent071f47140cec02197de5e163f45c77990b39457d (diff)
downloadlatinime-9807ab27eac3a10b299382af8280eb54dca50608.tar.gz
latinime-9807ab27eac3a10b299382af8280eb54dca50608.tar.xz
latinime-9807ab27eac3a10b299382af8280eb54dca50608.zip
(Refactor 1) Moved voice related codes to deprecated/voice
Change-Id: I008ac7099c815fb74a9ab374419617b336453f97
Diffstat (limited to 'java/src/com/android/inputmethod/deprecated/voice/RecognitionView.java')
-rw-r--r--java/src/com/android/inputmethod/deprecated/voice/RecognitionView.java355
1 files changed, 355 insertions, 0 deletions
diff --git a/java/src/com/android/inputmethod/deprecated/voice/RecognitionView.java b/java/src/com/android/inputmethod/deprecated/voice/RecognitionView.java
new file mode 100644
index 000000000..52c73ce90
--- /dev/null
+++ b/java/src/com/android/inputmethod/deprecated/voice/RecognitionView.java
@@ -0,0 +1,355 @@
+/*
+ * Copyright (C) 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.inputmethod.deprecated.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;
+import android.graphics.Canvas;
+import android.graphics.CornerPathEffect;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.PathEffect;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+
+import java.io.ByteArrayOutputStream;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.ShortBuffer;
+import java.util.Locale;
+
+/**
+ * The user interface for the "Speak now" and "working" states.
+ * Displays a recognition dialog (with waveform, voice meter, etc.),
+ * plays beeps, shows errors, etc.
+ */
+public class RecognitionView {
+ @SuppressWarnings("unused")
+ private static final String TAG = "RecognitionView";
+
+ private Handler mUiHandler; // Reference to UI thread
+ private View mView;
+ private Context mContext;
+
+ private TextView mText;
+ private ImageView mImage;
+ private View mProgress;
+ private SoundIndicator mSoundIndicator;
+ private TextView mLanguage;
+ private Button mButton;
+
+ private Drawable mInitializing;
+ private Drawable mError;
+
+ private static final int INIT = 0;
+ private static final int LISTENING = 1;
+ private static final int WORKING = 2;
+ private static final int READY = 3;
+
+ private int mState = INIT;
+
+ private final View mPopupLayout;
+
+ private final Drawable mListeningBorder;
+ private final Drawable mWorkingBorder;
+ private final Drawable mErrorBorder;
+
+ public RecognitionView(Context context, OnClickListener clickListener) {
+ mUiHandler = new Handler();
+
+ LayoutInflater inflater = (LayoutInflater) context.getSystemService(
+ Context.LAYOUT_INFLATER_SERVICE);
+
+ mView = inflater.inflate(R.layout.recognition_status, null);
+
+ mPopupLayout= mView.findViewById(R.id.popup_layout);
+
+ // Pre-load volume level images
+ Resources r = context.getResources();
+
+ mListeningBorder = r.getDrawable(R.drawable.vs_dialog_red);
+ mWorkingBorder = r.getDrawable(R.drawable.vs_dialog_blue);
+ mErrorBorder = r.getDrawable(R.drawable.vs_dialog_yellow);
+
+ mInitializing = r.getDrawable(R.drawable.mic_slash);
+ mError = r.getDrawable(R.drawable.caution);
+
+ mImage = (ImageView) mView.findViewById(R.id.image);
+ mProgress = mView.findViewById(R.id.progress);
+ mSoundIndicator = (SoundIndicator) mView.findViewById(R.id.sound_indicator);
+
+ 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;
+ }
+
+ public View getView() {
+ return mView;
+ }
+
+ public void restoreState() {
+ mUiHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ // Restart the spinner
+ if (mState == WORKING) {
+ ((ProgressBar) mProgress).setIndeterminate(false);
+ ((ProgressBar) mProgress).setIndeterminate(true);
+ }
+ }
+ });
+ }
+
+ public void showInitializing() {
+ mUiHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mState = INIT;
+ prepareDialog(mContext.getText(R.string.voice_initializing), mInitializing,
+ mContext.getText(R.string.cancel));
+ }
+ });
+ }
+
+ public void showListening() {
+ Log.d(TAG, "#showListening");
+ mUiHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mState = LISTENING;
+ prepareDialog(mContext.getText(R.string.voice_listening), null,
+ mContext.getText(R.string.cancel));
+ }
+ });
+ }
+
+ public void updateVoiceMeter(float rmsdB) {
+ mSoundIndicator.setRmsdB(rmsdB);
+ }
+
+ public void showError(final String message) {
+ mUiHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mState = READY;
+ prepareDialog(message, mError, mContext.getText(R.string.ok));
+ }
+ });
+ }
+
+ public void showWorking(
+ final ByteArrayOutputStream waveBuffer,
+ final int speechStartPosition,
+ final int speechEndPosition) {
+ mUiHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mState = WORKING;
+ prepareDialog(mContext.getText(R.string.voice_working), null, mContext
+ .getText(R.string.cancel));
+ final ShortBuffer buf = ByteBuffer.wrap(waveBuffer.toByteArray()).order(
+ ByteOrder.nativeOrder()).asShortBuffer();
+ buf.position(0);
+ waveBuffer.reset();
+ showWave(buf, speechStartPosition / 2, speechEndPosition / 2);
+ }
+ });
+ }
+
+ 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.INVISIBLE);
+
+ mProgress.setVisibility(View.GONE);
+
+ mImage.setVisibility(View.VISIBLE);
+ mImage.setImageResource(R.drawable.mic_slash);
+
+ mSoundIndicator.setVisibility(View.GONE);
+ mSoundIndicator.stop();
+
+ mLanguage.setVisibility(View.INVISIBLE);
+
+ mPopupLayout.setBackgroundDrawable(mListeningBorder);
+ break;
+ case LISTENING:
+ mText.setVisibility(View.VISIBLE);
+ mText.setText(text);
+
+ mProgress.setVisibility(View.GONE);
+
+ mImage.setVisibility(View.GONE);
+
+ 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:
+
+ mText.setVisibility(View.VISIBLE);
+ mText.setText(text);
+
+ mProgress.setVisibility(View.VISIBLE);
+
+ mImage.setVisibility(View.VISIBLE);
+
+ mSoundIndicator.setVisibility(View.GONE);
+ mSoundIndicator.stop();
+
+ mLanguage.setVisibility(View.GONE);
+
+ mPopupLayout.setBackgroundDrawable(mWorkingBorder);
+ break;
+ case READY:
+ mText.setVisibility(View.VISIBLE);
+ mText.setText(text);
+
+ mProgress.setVisibility(View.GONE);
+
+ mImage.setVisibility(View.VISIBLE);
+ mImage.setImageResource(R.drawable.caution);
+
+ mSoundIndicator.setVisibility(View.GONE);
+ mSoundIndicator.stop();
+
+ mLanguage.setVisibility(View.GONE);
+
+ mPopupLayout.setBackgroundDrawable(mErrorBorder);
+ break;
+ default:
+ Log.w(TAG, "Unknown state " + mState);
+ }
+ mPopupLayout.requestLayout();
+ mButton.setText(btnTxt);
+ }
+
+ /**
+ * @return an average abs of the specified buffer.
+ */
+ private static int getAverageAbs(ShortBuffer buffer, int start, int i, int npw) {
+ int from = start + i * npw;
+ int end = from + npw;
+ int total = 0;
+ for (int x = from; x < end; x++) {
+ total += Math.abs(buffer.get(x));
+ }
+ return total / npw;
+ }
+
+
+ /**
+ * Shows waveform of input audio.
+ *
+ * Copied from version in VoiceSearch's RecognitionActivity.
+ *
+ * TODO: adjust stroke width based on the size of data.
+ * TODO: use dip rather than pixels.
+ */
+ private void showWave(ShortBuffer waveBuffer, int startPosition, int endPosition) {
+ final int w = ((View) mImage.getParent()).getWidth();
+ final int h = ((View) mImage.getParent()).getHeight();
+ if (w <= 0 || h <= 0) {
+ // view is not visible this time. Skip drawing.
+ return;
+ }
+ final Bitmap b = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
+ final Canvas c = new Canvas(b);
+ final Paint paint = new Paint();
+ paint.setColor(0xFFFFFFFF); // 0xAARRGGBB
+ paint.setAntiAlias(true);
+ paint.setStyle(Paint.Style.STROKE);
+ paint.setAlpha(80);
+
+ final PathEffect effect = new CornerPathEffect(3);
+ paint.setPathEffect(effect);
+
+ final int numSamples = waveBuffer.remaining();
+ int endIndex;
+ if (endPosition == 0) {
+ endIndex = numSamples;
+ } else {
+ endIndex = Math.min(endPosition, numSamples);
+ }
+
+ int startIndex = startPosition - 2000; // include 250ms before speech
+ if (startIndex < 0) {
+ startIndex = 0;
+ }
+ final int numSamplePerWave = 200; // 8KHz 25ms = 200 samples
+ final float scale = 10.0f / 65536.0f;
+
+ final int count = (endIndex - startIndex) / numSamplePerWave;
+ final float deltaX = 1.0f * w / count;
+ int yMax = h / 2;
+ Path path = new Path();
+ c.translate(0, yMax);
+ float x = 0;
+ path.moveTo(x, 0);
+ for (int i = 0; i < count; i++) {
+ final int avabs = getAverageAbs(waveBuffer, startIndex, i , numSamplePerWave);
+ int sign = ( (i & 01) == 0) ? -1 : 1;
+ final float y = Math.min(yMax, avabs * h * scale) * sign;
+ path.lineTo(x, y);
+ x += deltaX;
+ path.lineTo(x, y);
+ }
+ if (deltaX > 4) {
+ paint.setStrokeWidth(2);
+ } else {
+ paint.setStrokeWidth(Math.max(0, (int) (deltaX -.05)));
+ }
+ c.drawPath(path, paint);
+ mImage.setImageBitmap(b);
+ }
+
+ public void finish() {
+ mUiHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mSoundIndicator.stop();
+ }
+ });
+ }
+}