1a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout/*
2a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout * Copyright (C) 2014 The Android Open Source Project
3a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout *
4a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout * in compliance with the License. You may obtain a copy of the License at
6a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout *
7a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout * http://www.apache.org/licenses/LICENSE-2.0
8a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout *
9a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout * Unless required by applicable law or agreed to in writing, software distributed under the License
10a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout * or implied. See the License for the specific language governing permissions and limitations under
12a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout * the License.
13a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout */
14a088f574b916685bd0a75ff613ac2009a3bad483Craig Stoutpackage android.support.v17.leanback.widget;
15a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
16a088f574b916685bd0a75ff613ac2009a3bad483Craig Stoutimport android.support.v17.leanback.R;
17a088f574b916685bd0a75ff613ac2009a3bad483Craig Stoutimport android.animation.ObjectAnimator;
18a088f574b916685bd0a75ff613ac2009a3bad483Craig Stoutimport android.content.Context;
19a088f574b916685bd0a75ff613ac2009a3bad483Craig Stoutimport android.graphics.Bitmap;
20a088f574b916685bd0a75ff613ac2009a3bad483Craig Stoutimport android.graphics.BitmapFactory;
21a088f574b916685bd0a75ff613ac2009a3bad483Craig Stoutimport android.graphics.Canvas;
22a088f574b916685bd0a75ff613ac2009a3bad483Craig Stoutimport android.graphics.Paint;
23a088f574b916685bd0a75ff613ac2009a3bad483Craig Stoutimport android.text.SpannableStringBuilder;
24a088f574b916685bd0a75ff613ac2009a3bad483Craig Stoutimport android.text.Spanned;
25a088f574b916685bd0a75ff613ac2009a3bad483Craig Stoutimport android.text.SpannedString;
26a088f574b916685bd0a75ff613ac2009a3bad483Craig Stoutimport android.text.style.ForegroundColorSpan;
27a088f574b916685bd0a75ff613ac2009a3bad483Craig Stoutimport android.text.style.ReplacementSpan;
28a088f574b916685bd0a75ff613ac2009a3bad483Craig Stoutimport android.util.AttributeSet;
29a088f574b916685bd0a75ff613ac2009a3bad483Craig Stoutimport android.util.Log;
30a088f574b916685bd0a75ff613ac2009a3bad483Craig Stoutimport android.util.Property;
31a088f574b916685bd0a75ff613ac2009a3bad483Craig Stoutimport android.view.View;
32a088f574b916685bd0a75ff613ac2009a3bad483Craig Stoutimport android.view.accessibility.AccessibilityNodeInfo;
33a088f574b916685bd0a75ff613ac2009a3bad483Craig Stoutimport android.widget.EditText;
34a088f574b916685bd0a75ff613ac2009a3bad483Craig Stoutimport android.widget.TextView;
35a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
36a088f574b916685bd0a75ff613ac2009a3bad483Craig Stoutimport java.util.List;
37a088f574b916685bd0a75ff613ac2009a3bad483Craig Stoutimport java.util.Random;
38a088f574b916685bd0a75ff613ac2009a3bad483Craig Stoutimport java.util.regex.Matcher;
39a088f574b916685bd0a75ff613ac2009a3bad483Craig Stoutimport java.util.regex.Pattern;
40a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
41a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout/**
42a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout * Shows the recognized text as a continuous stream of words.
43a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout */
44a088f574b916685bd0a75ff613ac2009a3bad483Craig Stoutclass StreamingTextView extends EditText {
45a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
46a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    private static final boolean DEBUG = false;
47a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    private static final String TAG = "StreamingTextView";
48a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
49a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    private static final float TEXT_DOT_SCALE = 1.3F;
50a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    private static final boolean DOTS_FOR_STABLE = false;
51a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    private static final boolean DOTS_FOR_PENDING = true;
52a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    private static final boolean ANIMATE_DOTS_FOR_PENDING = true;
53a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
54a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    private static final long STREAM_UPDATE_DELAY_MILLIS = 50;
55a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
56a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    private static final Pattern SPLIT_PATTERN = Pattern.compile("\\S+");
57a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
58a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    private static final Property<StreamingTextView,Integer> STREAM_POSITION_PROPERTY =
59a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout            new Property<StreamingTextView,Integer>(Integer.class, "streamPosition") {
60a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
61a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        @Override
62a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        public Integer get(StreamingTextView view) {
63a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout            return view.getStreamPosition();
64a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        }
65a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
66a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        @Override
67a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        public void set(StreamingTextView view, Integer value) {
68a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout            view.setStreamPosition(value);
69a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        }
70a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    };
71a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
72a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    private final Random mRandom = new Random();
73a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
74a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    private Bitmap mOneDot;
75a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    private Bitmap mTwoDot;
76a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
77a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    private int mStreamPosition;
78a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    private ObjectAnimator mStreamingAnimation;
79a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
80a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    public StreamingTextView(Context context, AttributeSet attrs) {
81a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        super(context, attrs);
82a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    }
83a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
84a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    public StreamingTextView(Context context, AttributeSet attrs, int defStyle) {
85a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        super(context, attrs, defStyle);
86a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    }
87a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
88a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    @Override
89a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    protected void onFinishInflate() {
90a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        super.onFinishInflate();
91a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
92a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        mOneDot = getScaledBitmap(R.drawable.lb_text_dot_one, TEXT_DOT_SCALE);
93a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        mTwoDot = getScaledBitmap(R.drawable.lb_text_dot_two, TEXT_DOT_SCALE);
94a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
95a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        reset();
96a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    }
97a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
98a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    private Bitmap getScaledBitmap(int resourceId, float scaled) {
99a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), resourceId);
100a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        return Bitmap.createScaledBitmap(bitmap, (int) (bitmap.getWidth() * scaled),
101a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout                (int) (bitmap.getHeight() * scaled), false);
102a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    }
103a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
104a00bada00bff4a58436a39472ab14ccb7a8f619dCraig Stout    /**
105a00bada00bff4a58436a39472ab14ccb7a8f619dCraig Stout     * Resets the text view.
106a00bada00bff4a58436a39472ab14ccb7a8f619dCraig Stout     */
107a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    public void reset() {
108a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        if (DEBUG) Log.d(TAG, "#reset");
109a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
110a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        mStreamPosition = -1;
111a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        cancelStreamAnimation();
112a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        setText("");
113a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    }
114a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
115a00bada00bff4a58436a39472ab14ccb7a8f619dCraig Stout    /**
116a00bada00bff4a58436a39472ab14ccb7a8f619dCraig Stout     * Updates the recognized text.
117a00bada00bff4a58436a39472ab14ccb7a8f619dCraig Stout     */
118a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    public void updateRecognizedText(String stableText, String pendingText) {
119a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        if (DEBUG) Log.d(TAG, "updateText(" + stableText + "," + pendingText + ")");
120a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
121a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        if (stableText == null) {
122a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout            stableText = "";
123a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        }
124a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
125a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        SpannableStringBuilder displayText = new SpannableStringBuilder(stableText);
126a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
127a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        if (DOTS_FOR_STABLE) {
128a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout            addDottySpans(displayText, stableText, 0);
129a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        }
130a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
131a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        if (pendingText != null) {
132a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout            int pendingTextStart = displayText.length();
133a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout            displayText.append(pendingText);
134a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout            if (DOTS_FOR_PENDING) {
135a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout                addDottySpans(displayText, pendingText, pendingTextStart);
136a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout            } else {
137a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout                int pendingColor = getResources().getColor(
138a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout                        R.color.lb_search_plate_hint_text_color);
139a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout                addColorSpan(displayText, pendingColor, pendingText, pendingTextStart);
140a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout            }
141a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        }
142a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
143a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        // Start streaming in dots from beginning of partials, or current position,
144a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        // whichever is larger
145a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        mStreamPosition = Math.max(stableText.length(), mStreamPosition);
146a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
147a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        // Copy the text and spans to a SpannedString, since editable text
148a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        // doesn't redraw in invalidate() when hardware accelerated
149a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        // if the text or spans havent't changed. (probably a framework bug)
150a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        updateText(new SpannedString(displayText));
151a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
152a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        if (ANIMATE_DOTS_FOR_PENDING) {
153a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout            startStreamAnimation();
154a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        }
155a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    }
156a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
157a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    private int getStreamPosition() {
158a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        return mStreamPosition;
159a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    }
160a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
161a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    private void setStreamPosition(int streamPosition) {
162a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        mStreamPosition = streamPosition;
163a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        invalidate();
164a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    }
165a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
166a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    private void startStreamAnimation() {
167a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        cancelStreamAnimation();
168a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        int pos = getStreamPosition();
169a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        int totalLen = length();
170a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        int animLen = totalLen - pos;
171a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        if (animLen > 0) {
172a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout            if (mStreamingAnimation == null) {
173a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout                mStreamingAnimation = new ObjectAnimator();
174a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout                mStreamingAnimation.setTarget(this);
175a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout                mStreamingAnimation.setProperty(STREAM_POSITION_PROPERTY);
176a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout            }
177a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout            mStreamingAnimation.setIntValues(pos, totalLen);
178a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout            mStreamingAnimation.setDuration(STREAM_UPDATE_DELAY_MILLIS * animLen);
179a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout            mStreamingAnimation.start();
180a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        }
181a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    }
182a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
183a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    private void cancelStreamAnimation() {
184a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        if (mStreamingAnimation != null) {
185a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout            mStreamingAnimation.cancel();
186a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        }
187a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    }
188a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
189a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    private void addDottySpans(SpannableStringBuilder displayText, String text, int textStart) {
190a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        Matcher m = SPLIT_PATTERN.matcher(text);
191a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        while (m.find()) {
192a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout            int wordStart = textStart + m.start();
193a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout            int wordEnd = textStart + m.end();
194a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout            DottySpan span = new DottySpan(text.charAt(m.start()), wordStart);
195a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout            displayText.setSpan(span, wordStart, wordEnd, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
196a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        }
197a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    }
198a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
199a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    private void addColorSpan(SpannableStringBuilder displayText, int color, String text,
200a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout            int textStart) {
201a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        ForegroundColorSpan span = new ForegroundColorSpan(color);
202a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        int start = textStart;
203a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        int end = textStart + text.length();
204a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        displayText.setSpan(span, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
205a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    }
206a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
207a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    /**
208a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout     * Sets the final, non changing, full text result. This should only happen at the very end of
209a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout     * a recognition.
210a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout     *
211a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout     * @param finalText to the view to.
212a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout     */
213a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    public void setFinalRecognizedText(CharSequence finalText) {
214a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        if (DEBUG) Log.d(TAG, "setFinalRecognizedText(" + finalText + ")");
215a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
216a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        updateText(finalText);
217a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    }
218a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
219a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    private void updateText(CharSequence displayText) {
220a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        setText(displayText);
221a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        bringPointIntoView(length());
222a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    }
223a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
224a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    /**
225a00bada00bff4a58436a39472ab14ccb7a8f619dCraig Stout     * This is required to make the View findable by uiautomator.
226a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout     */
227a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    @Override
228a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
229a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        super.onInitializeAccessibilityNodeInfo(info);
230a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        info.setClassName(StreamingTextView.class.getCanonicalName());
231a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    }
232a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
233a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    private class DottySpan extends ReplacementSpan {
234a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
235a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        private final int mSeed;
236a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        private final int mPosition;
237a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
238a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        public DottySpan(int seed, int pos) {
239a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout            mSeed = seed;
240a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout            mPosition = pos;
241a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        }
242a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
243a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        @Override
244a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        public void draw(Canvas canvas, CharSequence text, int start, int end,
245a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout                float x, int top, int y, int bottom, Paint paint) {
246a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
247a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout            int width = (int) paint.measureText(text, start, end);
248a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
249a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout            int dotWidth = mOneDot.getWidth();
250a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout            int sliceWidth = 2 * dotWidth;
251a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout            int sliceCount = width / sliceWidth;
252a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout            int excess = width % sliceWidth;
253a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout            int prop = excess / 2;
254a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout            boolean rtl = isLayoutRtl(StreamingTextView.this);
255a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
256a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout            mRandom.setSeed(mSeed);
257a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout            int oldAlpha = paint.getAlpha();
258a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout            for (int i = 0; i < sliceCount; i++) {
259a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout                if (ANIMATE_DOTS_FOR_PENDING) {
260a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout                    if (mPosition + i >= mStreamPosition) break;
261a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout                }
262a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
263a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout                float left = i * sliceWidth + prop + dotWidth / 2;
264a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout                float dotLeft = rtl ? x + width - left - dotWidth : x + left;
265a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
266a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout                // give the dots some visual variety
267a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout                paint.setAlpha((mRandom.nextInt(4) + 1) * 63);
268a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
269a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout                if (mRandom.nextBoolean()) {
270a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout                    canvas.drawBitmap(mTwoDot, dotLeft, y - mTwoDot.getHeight(), paint);
271a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout                } else {
272a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout                    canvas.drawBitmap(mOneDot, dotLeft, y - mOneDot.getHeight(), paint);
273a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout                }
274a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout            }
275a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout            paint.setAlpha(oldAlpha);
276a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        }
277a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
278a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        @Override
279a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        public int getSize(Paint paint, CharSequence text, int start, int end,
280a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout                Paint.FontMetricsInt fontMetricsInt) {
281a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout            return (int) paint.measureText(text, start, end);
282a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        }
283a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    }
284a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
285a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    public static boolean isLayoutRtl(View view) {
286a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) {
287a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout            return View.LAYOUT_DIRECTION_RTL == view.getLayoutDirection();
288a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        } else {
289a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout            return false;
290a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        }
291a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    }
292a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
293a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    public void updateRecognizedText(String stableText, List<Float> rmsValues) {}
294a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout}
295