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
104a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    public void reset() {
105a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        if (DEBUG) Log.d(TAG, "#reset");
106a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
107a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        mStreamPosition = -1;
108a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        cancelStreamAnimation();
109a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        setText("");
110a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    }
111a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
112a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    public void updateRecognizedText(String stableText, String pendingText) {
113a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        if (DEBUG) Log.d(TAG, "updateText(" + stableText + "," + pendingText + ")");
114a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
115a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        if (stableText == null) {
116a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout            stableText = "";
117a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        }
118a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
119a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        SpannableStringBuilder displayText = new SpannableStringBuilder(stableText);
120a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
121a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        if (DOTS_FOR_STABLE) {
122a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout            addDottySpans(displayText, stableText, 0);
123a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        }
124a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
125a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        if (pendingText != null) {
126a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout            int pendingTextStart = displayText.length();
127a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout            displayText.append(pendingText);
128a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout            if (DOTS_FOR_PENDING) {
129a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout                addDottySpans(displayText, pendingText, pendingTextStart);
130a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout            } else {
131a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout                int pendingColor = getResources().getColor(
132a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout                        R.color.lb_search_plate_hint_text_color);
133a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout                addColorSpan(displayText, pendingColor, pendingText, pendingTextStart);
134a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout            }
135a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        }
136a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
137a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        // Start streaming in dots from beginning of partials, or current position,
138a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        // whichever is larger
139a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        mStreamPosition = Math.max(stableText.length(), mStreamPosition);
140a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
141a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        // Copy the text and spans to a SpannedString, since editable text
142a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        // doesn't redraw in invalidate() when hardware accelerated
143a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        // if the text or spans havent't changed. (probably a framework bug)
144a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        updateText(new SpannedString(displayText));
145a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
146a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        if (ANIMATE_DOTS_FOR_PENDING) {
147a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout            startStreamAnimation();
148a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        }
149a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    }
150a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
151a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    private int getStreamPosition() {
152a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        return mStreamPosition;
153a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    }
154a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
155a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    private void setStreamPosition(int streamPosition) {
156a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        mStreamPosition = streamPosition;
157a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        invalidate();
158a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    }
159a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
160a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    private void startStreamAnimation() {
161a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        cancelStreamAnimation();
162a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        int pos = getStreamPosition();
163a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        int totalLen = length();
164a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        int animLen = totalLen - pos;
165a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        if (animLen > 0) {
166a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout            if (mStreamingAnimation == null) {
167a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout                mStreamingAnimation = new ObjectAnimator();
168a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout                mStreamingAnimation.setTarget(this);
169a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout                mStreamingAnimation.setProperty(STREAM_POSITION_PROPERTY);
170a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout            }
171a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout            mStreamingAnimation.setIntValues(pos, totalLen);
172a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout            mStreamingAnimation.setDuration(STREAM_UPDATE_DELAY_MILLIS * animLen);
173a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout            mStreamingAnimation.start();
174a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        }
175a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    }
176a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
177a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    private void cancelStreamAnimation() {
178a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        if (mStreamingAnimation != null) {
179a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout            mStreamingAnimation.cancel();
180a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        }
181a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    }
182a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
183a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    private void addDottySpans(SpannableStringBuilder displayText, String text, int textStart) {
184a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        Matcher m = SPLIT_PATTERN.matcher(text);
185a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        while (m.find()) {
186a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout            int wordStart = textStart + m.start();
187a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout            int wordEnd = textStart + m.end();
188a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout            DottySpan span = new DottySpan(text.charAt(m.start()), wordStart);
189a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout            displayText.setSpan(span, wordStart, wordEnd, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
190a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        }
191a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    }
192a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
193a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    private void addColorSpan(SpannableStringBuilder displayText, int color, String text,
194a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout            int textStart) {
195a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        ForegroundColorSpan span = new ForegroundColorSpan(color);
196a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        int start = textStart;
197a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        int end = textStart + text.length();
198a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        displayText.setSpan(span, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
199a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    }
200a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
201a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    /**
202a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout     * Sets the final, non changing, full text result. This should only happen at the very end of
203a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout     * a recognition.
204a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout     *
205a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout     * @param finalText to the view to.
206a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout     */
207a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    public void setFinalRecognizedText(CharSequence finalText) {
208a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        if (DEBUG) Log.d(TAG, "setFinalRecognizedText(" + finalText + ")");
209a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
210a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        updateText(finalText);
211a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    }
212a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
213a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    private void updateText(CharSequence displayText) {
214a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        setText(displayText);
215a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        bringPointIntoView(length());
216a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    }
217a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
218a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    /**
219a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout     * This is required to make the View findable by uiautomator
220a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout     */
221a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    @Override
222a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
223a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        super.onInitializeAccessibilityNodeInfo(info);
224a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        info.setClassName(StreamingTextView.class.getCanonicalName());
225a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    }
226a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
227a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    private class DottySpan extends ReplacementSpan {
228a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
229a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        private final int mSeed;
230a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        private final int mPosition;
231a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
232a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        public DottySpan(int seed, int pos) {
233a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout            mSeed = seed;
234a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout            mPosition = pos;
235a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        }
236a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
237a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        @Override
238a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        public void draw(Canvas canvas, CharSequence text, int start, int end,
239a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout                float x, int top, int y, int bottom, Paint paint) {
240a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
241a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout            int width = (int) paint.measureText(text, start, end);
242a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
243a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout            int dotWidth = mOneDot.getWidth();
244a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout            int sliceWidth = 2 * dotWidth;
245a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout            int sliceCount = width / sliceWidth;
246a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout            int excess = width % sliceWidth;
247a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout            int prop = excess / 2;
248a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout            boolean rtl = isLayoutRtl(StreamingTextView.this);
249a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
250a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout            mRandom.setSeed(mSeed);
251a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout            int oldAlpha = paint.getAlpha();
252a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout            for (int i = 0; i < sliceCount; i++) {
253a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout                if (ANIMATE_DOTS_FOR_PENDING) {
254a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout                    if (mPosition + i >= mStreamPosition) break;
255a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout                }
256a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
257a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout                float left = i * sliceWidth + prop + dotWidth / 2;
258a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout                float dotLeft = rtl ? x + width - left - dotWidth : x + left;
259a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
260a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout                // give the dots some visual variety
261a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout                paint.setAlpha((mRandom.nextInt(4) + 1) * 63);
262a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
263a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout                if (mRandom.nextBoolean()) {
264a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout                    canvas.drawBitmap(mTwoDot, dotLeft, y - mTwoDot.getHeight(), paint);
265a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout                } else {
266a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout                    canvas.drawBitmap(mOneDot, dotLeft, y - mOneDot.getHeight(), paint);
267a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout                }
268a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout            }
269a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout            paint.setAlpha(oldAlpha);
270a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        }
271a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
272a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        @Override
273a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        public int getSize(Paint paint, CharSequence text, int start, int end,
274a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout                Paint.FontMetricsInt fontMetricsInt) {
275a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout            return (int) paint.measureText(text, start, end);
276a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        }
277a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    }
278a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
279a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    public static boolean isLayoutRtl(View view) {
280a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) {
281a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout            return View.LAYOUT_DIRECTION_RTL == view.getLayoutDirection();
282a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        } else {
283a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout            return false;
284a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout        }
285a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    }
286a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout
287a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout    public void updateRecognizedText(String stableText, List<Float> rmsValues) {}
288a088f574b916685bd0a75ff613ac2009a3bad483Craig Stout}
289