1d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette/*
2d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette * Copyright (C) 2013 The Android Open Source Project
3d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette *
4d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette * Licensed under the Apache License, Version 2.0 (the "License");
5d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette * you may not use this file except in compliance with the License.
6d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette * You may obtain a copy of the License at
7d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette *
8d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette *      http://www.apache.org/licenses/LICENSE-2.0
9d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette *
10d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette * Unless required by applicable law or agreed to in writing, software
11d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette * distributed under the License is distributed on an "AS IS" BASIS,
12d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette * See the License for the specific language governing permissions and
14d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette * limitations under the License.
15d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette */
16d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnarpackage android.media;
1876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
1976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnarimport android.content.Context;
207fe420f31b4de2bbd7a89194384d0c9ac6293226Alan Viveretteimport android.text.Layout.Alignment;
21d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viveretteimport android.text.SpannableStringBuilder;
22d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viveretteimport android.util.ArrayMap;
2376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnarimport android.util.AttributeSet;
2476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnarimport android.util.Log;
25d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viveretteimport android.view.Gravity;
2676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnarimport android.view.View;
27d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viveretteimport android.view.ViewGroup;
28d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viveretteimport android.view.accessibility.CaptioningManager;
29d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viveretteimport android.view.accessibility.CaptioningManager.CaptionStyle;
30d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viveretteimport android.view.accessibility.CaptioningManager.CaptioningChangeListener;
31d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viveretteimport android.widget.LinearLayout;
32d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
33d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viveretteimport com.android.internal.widget.SubtitleView;
3476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
35d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viveretteimport java.util.ArrayList;
3676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnarimport java.util.Arrays;
3776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnarimport java.util.HashMap;
3876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnarimport java.util.Map;
3976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnarimport java.util.Vector;
4076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
4176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar/** @hide */
4276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnarpublic class WebVttRenderer extends SubtitleController.Renderer {
43d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    private final Context mContext;
4476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
45d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    private WebVttRenderingWidget mRenderingWidget;
46d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
47d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    public WebVttRenderer(Context context) {
48d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        mContext = context;
4976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    }
5076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
5176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    @Override
5276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    public boolean supports(MediaFormat format) {
5376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        if (format.containsKey(MediaFormat.KEY_MIME)) {
5476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            return format.getString(MediaFormat.KEY_MIME).equals("text/vtt");
5576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        }
5676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        return false;
5776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    }
5876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
5976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    @Override
6076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    public SubtitleTrack createTrack(MediaFormat format) {
61d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        if (mRenderingWidget == null) {
62d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            mRenderingWidget = new WebVttRenderingWidget(mContext);
63d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        }
6476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
65d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        return new WebVttTrack(mRenderingWidget, format);
6676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    }
6776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar}
6876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
6976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar/** @hide */
7076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnarclass TextTrackCueSpan {
7176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    long mTimestampMs;
7276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    boolean mEnabled;
7376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    String mText;
7476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    TextTrackCueSpan(String text, long timestamp) {
7576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        mTimestampMs = timestamp;
7676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        mText = text;
7776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        // spans with timestamp will be enabled by Cue.onTime
7876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        mEnabled = (mTimestampMs < 0);
7976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    }
8076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
8176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    @Override
8276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    public boolean equals(Object o) {
8376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        if (!(o instanceof TextTrackCueSpan)) {
8476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            return false;
8576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        }
8676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        TextTrackCueSpan span = (TextTrackCueSpan) o;
8776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        return mTimestampMs == span.mTimestampMs &&
8876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                mText.equals(span.mText);
8976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    }
9076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar}
9176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
9276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar/**
9376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar * @hide
9476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar *
9576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar * Extract all text without style, but with timestamp spans.
9676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar */
9776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnarclass UnstyledTextExtractor implements Tokenizer.OnTokenListener {
9876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    StringBuilder mLine = new StringBuilder();
9976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    Vector<TextTrackCueSpan[]> mLines = new Vector<TextTrackCueSpan[]>();
10076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    Vector<TextTrackCueSpan> mCurrentLine = new Vector<TextTrackCueSpan>();
10176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    long mLastTimestamp;
10276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
10376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    UnstyledTextExtractor() {
10476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        init();
10576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    }
10676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
10776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    private void init() {
10876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        mLine.delete(0, mLine.length());
10976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        mLines.clear();
11076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        mCurrentLine.clear();
11176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        mLastTimestamp = -1;
11276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    }
11376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
11476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    @Override
11576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    public void onData(String s) {
11676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        mLine.append(s);
11776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    }
11876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
11976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    @Override
12076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    public void onStart(String tag, String[] classes, String annotation) { }
12176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
12276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    @Override
12376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    public void onEnd(String tag) { }
12476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
12576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    @Override
12676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    public void onTimeStamp(long timestampMs) {
12776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        // finish any prior span
12876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        if (mLine.length() > 0 && timestampMs != mLastTimestamp) {
12976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            mCurrentLine.add(
13076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    new TextTrackCueSpan(mLine.toString(), mLastTimestamp));
13176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            mLine.delete(0, mLine.length());
13276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        }
13376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        mLastTimestamp = timestampMs;
13476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    }
13576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
13676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    @Override
13776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    public void onLineEnd() {
13876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        // finish any pending span
13976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        if (mLine.length() > 0) {
14076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            mCurrentLine.add(
14176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    new TextTrackCueSpan(mLine.toString(), mLastTimestamp));
14276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            mLine.delete(0, mLine.length());
14376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        }
14476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
14576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        TextTrackCueSpan[] spans = new TextTrackCueSpan[mCurrentLine.size()];
14676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        mCurrentLine.toArray(spans);
14776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        mCurrentLine.clear();
14876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        mLines.add(spans);
14976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    }
15076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
15176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    public TextTrackCueSpan[][] getText() {
15276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        // for politeness, finish last cue-line if it ends abruptly
15376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        if (mLine.length() > 0 || mCurrentLine.size() > 0) {
15476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            onLineEnd();
15576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        }
15676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        TextTrackCueSpan[][] lines = new TextTrackCueSpan[mLines.size()][];
15776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        mLines.toArray(lines);
15876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        init();
15976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        return lines;
16076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    }
16176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar}
16276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
16376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar/**
16476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar * @hide
16576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar *
16676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar * Tokenizer tokenizes the WebVTT Cue Text into tags and data
16776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar */
16876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnarclass Tokenizer {
16976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    private static final String TAG = "Tokenizer";
17076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    private TokenizerPhase mPhase;
17176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    private TokenizerPhase mDataTokenizer;
17276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    private TokenizerPhase mTagTokenizer;
17376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
17476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    private OnTokenListener mListener;
17576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    private String mLine;
17676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    private int mHandledLen;
17776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
17876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    interface TokenizerPhase {
17976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        TokenizerPhase start();
18076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        void tokenize();
18176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    }
18276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
18376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    class DataTokenizer implements TokenizerPhase {
18476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        // includes both WebVTT data && escape state
18576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        private StringBuilder mData;
18676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
18776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        public TokenizerPhase start() {
18876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            mData = new StringBuilder();
18976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            return this;
19076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        }
19176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
19276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        private boolean replaceEscape(String escape, String replacement, int pos) {
19376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            if (mLine.startsWith(escape, pos)) {
19476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                mData.append(mLine.substring(mHandledLen, pos));
19576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                mData.append(replacement);
19676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                mHandledLen = pos + escape.length();
19776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                pos = mHandledLen - 1;
19876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                return true;
19976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            }
20076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            return false;
20176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        }
20276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
20376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        @Override
20476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        public void tokenize() {
20576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            int end = mLine.length();
20676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            for (int pos = mHandledLen; pos < mLine.length(); pos++) {
20776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                if (mLine.charAt(pos) == '&') {
20876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    if (replaceEscape("&amp;", "&", pos) ||
20976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                            replaceEscape("&lt;", "<", pos) ||
21076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                            replaceEscape("&gt;", ">", pos) ||
21176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                            replaceEscape("&lrm;", "\u200e", pos) ||
21276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                            replaceEscape("&rlm;", "\u200f", pos) ||
21376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                            replaceEscape("&nbsp;", "\u00a0", pos)) {
21476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                        continue;
21576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    }
21676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                } else if (mLine.charAt(pos) == '<') {
21776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    end = pos;
21876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    mPhase = mTagTokenizer.start();
21976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    break;
22076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                }
22176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            }
22276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            mData.append(mLine.substring(mHandledLen, end));
22376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            // yield mData
22476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            mListener.onData(mData.toString());
22576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            mData.delete(0, mData.length());
22676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            mHandledLen = end;
22776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        }
22876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    }
22976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
23076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    class TagTokenizer implements TokenizerPhase {
23176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        private boolean mAtAnnotation;
23276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        private String mName, mAnnotation;
23376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
23476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        public TokenizerPhase start() {
23576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            mName = mAnnotation = "";
23676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            mAtAnnotation = false;
23776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            return this;
23876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        }
23976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
24076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        @Override
24176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        public void tokenize() {
24276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            if (!mAtAnnotation)
24376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                mHandledLen++;
24476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            if (mHandledLen < mLine.length()) {
24576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                String[] parts;
24676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                /**
24776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                 * Collect annotations and end-tags to closing >.  Collect tag
24876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                 * name to closing bracket or next white-space.
24976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                 */
25076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                if (mAtAnnotation || mLine.charAt(mHandledLen) == '/') {
25176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    parts = mLine.substring(mHandledLen).split(">");
25276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                } else {
25376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    parts = mLine.substring(mHandledLen).split("[\t\f >]");
25476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                }
25576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                String part = mLine.substring(
25676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                            mHandledLen, mHandledLen + parts[0].length());
25776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                mHandledLen += parts[0].length();
25876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
25976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                if (mAtAnnotation) {
26076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    mAnnotation += " " + part;
26176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                } else {
26276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    mName = part;
26376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                }
26476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            }
26576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
26676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            mAtAnnotation = true;
26776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
26876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            if (mHandledLen < mLine.length() && mLine.charAt(mHandledLen) == '>') {
26976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                yield_tag();
27076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                mPhase = mDataTokenizer.start();
27176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                mHandledLen++;
27276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            }
27376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        }
27476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
27576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        private void yield_tag() {
27676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            if (mName.startsWith("/")) {
27776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                mListener.onEnd(mName.substring(1));
27876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            } else if (mName.length() > 0 && Character.isDigit(mName.charAt(0))) {
27976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                // timestamp
28076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                try {
28176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    long timestampMs = WebVttParser.parseTimestampMs(mName);
28276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    mListener.onTimeStamp(timestampMs);
28376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                } catch (NumberFormatException e) {
28476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    Log.d(TAG, "invalid timestamp tag: <" + mName + ">");
28576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                }
28676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            } else {
28776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                mAnnotation = mAnnotation.replaceAll("\\s+", " ");
28876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                if (mAnnotation.startsWith(" ")) {
28976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    mAnnotation = mAnnotation.substring(1);
29076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                }
29176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                if (mAnnotation.endsWith(" ")) {
29276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    mAnnotation = mAnnotation.substring(0, mAnnotation.length() - 1);
29376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                }
29476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
29576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                String[] classes = null;
29676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                int dotAt = mName.indexOf('.');
29776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                if (dotAt >= 0) {
29876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    classes = mName.substring(dotAt + 1).split("\\.");
29976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    mName = mName.substring(0, dotAt);
30076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                }
30176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                mListener.onStart(mName, classes, mAnnotation);
30276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            }
30376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        }
30476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    }
30576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
30676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    Tokenizer(OnTokenListener listener) {
30776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        mDataTokenizer = new DataTokenizer();
30876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        mTagTokenizer = new TagTokenizer();
30976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        reset();
31076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        mListener = listener;
31176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    }
31276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
31376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    void reset() {
31476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        mPhase = mDataTokenizer.start();
31576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    }
31676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
31776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    void tokenize(String s) {
31876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        mHandledLen = 0;
31976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        mLine = s;
32076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        while (mHandledLen < mLine.length()) {
32176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            mPhase.tokenize();
32276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        }
32376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        /* we are finished with a line unless we are in the middle of a tag */
32476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        if (!(mPhase instanceof TagTokenizer)) {
32576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            // yield END-OF-LINE
32676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            mListener.onLineEnd();
32776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        }
32876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    }
32976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
33076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    interface OnTokenListener {
33176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        void onData(String s);
33276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        void onStart(String tag, String[] classes, String annotation);
33376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        void onEnd(String tag);
33476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        void onTimeStamp(long timestampMs);
33576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        void onLineEnd();
33676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    }
33776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar}
33876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
33976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar/** @hide */
34076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnarclass TextTrackRegion {
34176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    final static int SCROLL_VALUE_NONE      = 300;
34276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    final static int SCROLL_VALUE_SCROLL_UP = 301;
34376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
34476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    String mId;
34576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    float mWidth;
34676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    int mLines;
34776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    float mAnchorPointX, mAnchorPointY;
34876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    float mViewportAnchorPointX, mViewportAnchorPointY;
34976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    int mScrollValue;
35076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
35176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    TextTrackRegion() {
35276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        mId = "";
35376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        mWidth = 100;
35476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        mLines = 3;
35576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        mAnchorPointX = mViewportAnchorPointX = 0.f;
35676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        mAnchorPointY = mViewportAnchorPointY = 100.f;
35776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        mScrollValue = SCROLL_VALUE_NONE;
35876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    }
35976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
36076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    public String toString() {
36176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        StringBuilder res = new StringBuilder(" {id:\"").append(mId)
36276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            .append("\", width:").append(mWidth)
36376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            .append(", lines:").append(mLines)
36476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            .append(", anchorPoint:(").append(mAnchorPointX)
36576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            .append(", ").append(mAnchorPointY)
36676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            .append("), viewportAnchorPoints:").append(mViewportAnchorPointX)
36776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            .append(", ").append(mViewportAnchorPointY)
36876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            .append("), scrollValue:")
36976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            .append(mScrollValue == SCROLL_VALUE_NONE ? "none" :
37076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    mScrollValue == SCROLL_VALUE_SCROLL_UP ? "scroll_up" :
37176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    "INVALID")
37276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            .append("}");
37376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        return res.toString();
37476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    }
37576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar}
37676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
37776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar/** @hide */
37876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnarclass TextTrackCue extends SubtitleTrack.Cue {
37976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    final static int WRITING_DIRECTION_HORIZONTAL  = 100;
38076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    final static int WRITING_DIRECTION_VERTICAL_RL = 101;
38176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    final static int WRITING_DIRECTION_VERTICAL_LR = 102;
38276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
38376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    final static int ALIGNMENT_MIDDLE = 200;
38476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    final static int ALIGNMENT_START  = 201;
38576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    final static int ALIGNMENT_END    = 202;
38676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    final static int ALIGNMENT_LEFT   = 203;
38776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    final static int ALIGNMENT_RIGHT  = 204;
38876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    private static final String TAG = "TTCue";
38976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
39076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    String  mId;
39176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    boolean mPauseOnExit;
39276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    int     mWritingDirection;
39376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    String  mRegionId;
39476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    boolean mSnapToLines;
39576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    Integer mLinePosition;  // null means AUTO
39676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    boolean mAutoLinePosition;
39776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    int     mTextPosition;
39876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    int     mSize;
39976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    int     mAlignment;
40076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    // Vector<String> mText;
40176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    String[] mStrings;
40276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    TextTrackCueSpan[][] mLines;
40376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    TextTrackRegion mRegion;
40476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
40576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    TextTrackCue() {
40676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        mId = "";
40776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        mPauseOnExit = false;
40876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        mWritingDirection = WRITING_DIRECTION_HORIZONTAL;
40976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        mRegionId = "";
41076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        mSnapToLines = true;
41176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        mLinePosition = null /* AUTO */;
41276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        mTextPosition = 50;
41376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        mSize = 100;
41476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        mAlignment = ALIGNMENT_MIDDLE;
41576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        mLines = null;
41676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        mRegion = null;
41776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    }
41876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
41976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    @Override
42076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    public boolean equals(Object o) {
42176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        if (!(o instanceof TextTrackCue)) {
42276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            return false;
42376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        }
42476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        if (this == o) {
42576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            return true;
42676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        }
42776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
42876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        try {
42976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            TextTrackCue cue = (TextTrackCue) o;
43076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            boolean res = mId.equals(cue.mId) &&
43176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    mPauseOnExit == cue.mPauseOnExit &&
43276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    mWritingDirection == cue.mWritingDirection &&
43376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    mRegionId.equals(cue.mRegionId) &&
43476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    mSnapToLines == cue.mSnapToLines &&
43576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    mAutoLinePosition == cue.mAutoLinePosition &&
43676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    (mAutoLinePosition || mLinePosition == cue.mLinePosition) &&
43776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    mTextPosition == cue.mTextPosition &&
43876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    mSize == cue.mSize &&
43976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    mAlignment == cue.mAlignment &&
44076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    mLines.length == cue.mLines.length;
44176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            if (res == true) {
44276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                for (int line = 0; line < mLines.length; line++) {
44376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    if (!Arrays.equals(mLines[line], cue.mLines[line])) {
44476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                        return false;
44576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    }
44676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                }
44776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            }
44876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            return res;
44976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        } catch(IncompatibleClassChangeError e) {
45076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            return false;
45176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        }
45276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    }
45376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
45476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    public StringBuilder appendStringsToBuilder(StringBuilder builder) {
45576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        if (mStrings == null) {
45676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            builder.append("null");
45776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        } else {
45876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            builder.append("[");
45976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            boolean first = true;
46076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            for (String s: mStrings) {
46176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                if (!first) {
46276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    builder.append(", ");
46376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                }
46476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                if (s == null) {
46576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    builder.append("null");
46676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                } else {
46776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    builder.append("\"");
46876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    builder.append(s);
46976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    builder.append("\"");
47076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                }
47176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                first = false;
47276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            }
47376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            builder.append("]");
47476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        }
47576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        return builder;
47676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    }
47776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
47876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    public StringBuilder appendLinesToBuilder(StringBuilder builder) {
47976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        if (mLines == null) {
48076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            builder.append("null");
48176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        } else {
48276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            builder.append("[");
48376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            boolean first = true;
48476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            for (TextTrackCueSpan[] spans: mLines) {
48576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                if (!first) {
48676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    builder.append(", ");
48776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                }
48876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                if (spans == null) {
48976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    builder.append("null");
49076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                } else {
49176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    builder.append("\"");
49276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    boolean innerFirst = true;
49376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    long lastTimestamp = -1;
49476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    for (TextTrackCueSpan span: spans) {
49576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                        if (!innerFirst) {
49676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                            builder.append(" ");
49776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                        }
49876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                        if (span.mTimestampMs != lastTimestamp) {
49976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                            builder.append("<")
50076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                                    .append(WebVttParser.timeToString(
50176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                                            span.mTimestampMs))
50276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                                    .append(">");
50376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                            lastTimestamp = span.mTimestampMs;
50476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                        }
50576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                        builder.append(span.mText);
50676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                        innerFirst = false;
50776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    }
50876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    builder.append("\"");
50976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                }
51076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                first = false;
51176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            }
51276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            builder.append("]");
51376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        }
51476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        return builder;
51576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    }
51676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
51776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    public String toString() {
51876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        StringBuilder res = new StringBuilder();
51976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
52076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        res.append(WebVttParser.timeToString(mStartTimeMs))
52176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                .append(" --> ").append(WebVttParser.timeToString(mEndTimeMs))
52276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                .append(" {id:\"").append(mId)
52376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                .append("\", pauseOnExit:").append(mPauseOnExit)
52476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                .append(", direction:")
52576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                .append(mWritingDirection == WRITING_DIRECTION_HORIZONTAL ? "horizontal" :
52676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                        mWritingDirection == WRITING_DIRECTION_VERTICAL_LR ? "vertical_lr" :
52776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                        mWritingDirection == WRITING_DIRECTION_VERTICAL_RL ? "vertical_rl" :
52876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                        "INVALID")
52976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                .append(", regionId:\"").append(mRegionId)
53076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                .append("\", snapToLines:").append(mSnapToLines)
53176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                .append(", linePosition:").append(mAutoLinePosition ? "auto" :
53276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                                                  mLinePosition)
53376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                .append(", textPosition:").append(mTextPosition)
53476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                .append(", size:").append(mSize)
53576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                .append(", alignment:")
53676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                .append(mAlignment == ALIGNMENT_END ? "end" :
53776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                        mAlignment == ALIGNMENT_LEFT ? "left" :
53876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                        mAlignment == ALIGNMENT_MIDDLE ? "middle" :
53976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                        mAlignment == ALIGNMENT_RIGHT ? "right" :
54076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                        mAlignment == ALIGNMENT_START ? "start" : "INVALID")
54176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                .append(", text:");
54276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        appendStringsToBuilder(res).append("}");
54376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        return res.toString();
54476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    }
54576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
54676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    @Override
54776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    public int hashCode() {
54876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        return toString().hashCode();
54976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    }
55076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
55176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    @Override
55276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    public void onTime(long timeMs) {
55376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        for (TextTrackCueSpan[] line: mLines) {
55476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            for (TextTrackCueSpan span: line) {
55576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                span.mEnabled = timeMs >= span.mTimestampMs;
55676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            }
55776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        }
55876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    }
55976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar}
56076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
56172bbe6cca6470e86b56081c3fbe3cbb4360f5271Lajos Molnar/**
56272bbe6cca6470e86b56081c3fbe3cbb4360f5271Lajos Molnar *  Supporting July 10 2013 draft version
56372bbe6cca6470e86b56081c3fbe3cbb4360f5271Lajos Molnar *
56472bbe6cca6470e86b56081c3fbe3cbb4360f5271Lajos Molnar *  @hide
56572bbe6cca6470e86b56081c3fbe3cbb4360f5271Lajos Molnar */
56676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnarclass WebVttParser {
56776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    private static final String TAG = "WebVttParser";
56876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    private Phase mPhase;
56976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    private TextTrackCue mCue;
57076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    private Vector<String> mCueTexts;
57176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    private WebVttCueListener mListener;
57276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    private String mBuffer;
57376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
57476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    WebVttParser(WebVttCueListener listener) {
57576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        mPhase = mParseStart;
57676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        mBuffer = "";   /* mBuffer contains up to 1 incomplete line */
57776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        mListener = listener;
57876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        mCueTexts = new Vector<String>();
57976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    }
58076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
58176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    /* parsePercentageString */
58276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    public static float parseFloatPercentage(String s)
58376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            throws NumberFormatException {
58476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        if (!s.endsWith("%")) {
58576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            throw new NumberFormatException("does not end in %");
58676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        }
58776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        s = s.substring(0, s.length() - 1);
58876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        // parseFloat allows an exponent or a sign
58976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        if (s.matches(".*[^0-9.].*")) {
59076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            throw new NumberFormatException("contains an invalid character");
59176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        }
59276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
59376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        try {
59476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            float value = Float.parseFloat(s);
59576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            if (value < 0.0f || value > 100.0f) {
59676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                throw new NumberFormatException("is out of range");
59776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            }
59876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            return value;
59976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        } catch (NumberFormatException e) {
60076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            throw new NumberFormatException("is not a number");
60176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        }
60276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    }
60376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
60476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    public static int parseIntPercentage(String s) throws NumberFormatException {
60576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        if (!s.endsWith("%")) {
60676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            throw new NumberFormatException("does not end in %");
60776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        }
60876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        s = s.substring(0, s.length() - 1);
60976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        // parseInt allows "-0" that returns 0, so check for non-digits
61076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        if (s.matches(".*[^0-9].*")) {
61176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            throw new NumberFormatException("contains an invalid character");
61276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        }
61376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
61476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        try {
61576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            int value = Integer.parseInt(s);
61676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            if (value < 0 || value > 100) {
61776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                throw new NumberFormatException("is out of range");
61876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            }
61976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            return value;
62076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        } catch (NumberFormatException e) {
62176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            throw new NumberFormatException("is not a number");
62276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        }
62376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    }
62476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
62576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    public static long parseTimestampMs(String s) throws NumberFormatException {
62676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        if (!s.matches("(\\d+:)?[0-5]\\d:[0-5]\\d\\.\\d{3}")) {
62776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            throw new NumberFormatException("has invalid format");
62876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        }
62976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
63076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        String[] parts = s.split("\\.", 2);
63176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        long value = 0;
63276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        for (String group: parts[0].split(":")) {
63376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            value = value * 60 + Long.parseLong(group);
63476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        }
63576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        return value * 1000 + Long.parseLong(parts[1]);
63676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    }
63776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
63876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    public static String timeToString(long timeMs) {
63976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        return String.format("%d:%02d:%02d.%03d",
64076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                timeMs / 3600000, (timeMs / 60000) % 60,
64176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                (timeMs / 1000) % 60, timeMs % 1000);
64276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    }
64376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
64476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    public void parse(String s) {
64576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        boolean trailingCR = false;
64676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        mBuffer = (mBuffer + s.replace("\0", "\ufffd")).replace("\r\n", "\n");
64776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
64876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        /* keep trailing '\r' in case matching '\n' arrives in next packet */
64976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        if (mBuffer.endsWith("\r")) {
65076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            trailingCR = true;
65176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            mBuffer = mBuffer.substring(0, mBuffer.length() - 1);
65276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        }
65376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
65476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        String[] lines = mBuffer.split("[\r\n]");
65576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        for (int i = 0; i < lines.length - 1; i++) {
65676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            mPhase.parse(lines[i]);
65776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        }
65876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
65976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        mBuffer = lines[lines.length - 1];
66076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        if (trailingCR)
66176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            mBuffer += "\r";
66276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    }
66376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
66476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    public void eos() {
66576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        if (mBuffer.endsWith("\r")) {
66676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            mBuffer = mBuffer.substring(0, mBuffer.length() - 1);
66776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        }
66876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
66976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        mPhase.parse(mBuffer);
67076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        mBuffer = "";
67176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
67276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        yieldCue();
67376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        mPhase = mParseStart;
67476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    }
67576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
67676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    public void yieldCue() {
67776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        if (mCue != null && mCueTexts.size() > 0) {
67876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            mCue.mStrings = new String[mCueTexts.size()];
67976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            mCueTexts.toArray(mCue.mStrings);
68076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            mCueTexts.clear();
68176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            mListener.onCueParsed(mCue);
68276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        }
68376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        mCue = null;
68476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    }
68576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
68676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    interface Phase {
68776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        void parse(String line);
68876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    }
68976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
69076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    final private Phase mSkipRest = new Phase() {
69176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        @Override
69276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        public void parse(String line) { }
69376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    };
69476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
69576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    final private Phase mParseStart = new Phase() { // 5-9
69676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        @Override
69776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        public void parse(String line) {
698283fe37aa20d6f5910ed1c86847c6228e151e1ffLajos Molnar            if (line.startsWith("\ufeff")) {
699283fe37aa20d6f5910ed1c86847c6228e151e1ffLajos Molnar                line = line.substring(1);
700283fe37aa20d6f5910ed1c86847c6228e151e1ffLajos Molnar            }
70176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            if (!line.equals("WEBVTT") &&
70276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    !line.startsWith("WEBVTT ") &&
70376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    !line.startsWith("WEBVTT\t")) {
70476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                log_warning("Not a WEBVTT header", line);
70576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                mPhase = mSkipRest;
70676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            } else {
70776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                mPhase = mParseHeader;
70876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            }
70976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        }
71076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    };
71176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
71276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    final private Phase mParseHeader = new Phase() { // 10-13
71376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        TextTrackRegion parseRegion(String s) {
71476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            TextTrackRegion region = new TextTrackRegion();
71576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            for (String setting: s.split(" +")) {
71676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                int equalAt = setting.indexOf('=');
71776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                if (equalAt <= 0 || equalAt == setting.length() - 1) {
71876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    continue;
71976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                }
72076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
72176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                String name = setting.substring(0, equalAt);
72276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                String value = setting.substring(equalAt + 1);
72376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                if (name.equals("id")) {
72476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    region.mId = value;
72576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                } else if (name.equals("width")) {
72676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    try {
72776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                        region.mWidth = parseFloatPercentage(value);
72876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    } catch (NumberFormatException e) {
72976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                        log_warning("region setting", name,
73076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                                "has invalid value", e.getMessage(), value);
73176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    }
73276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                } else if (name.equals("lines")) {
73372bbe6cca6470e86b56081c3fbe3cbb4360f5271Lajos Molnar                    if (value.matches(".*[^0-9].*")) {
73472bbe6cca6470e86b56081c3fbe3cbb4360f5271Lajos Molnar                        log_warning("lines", name, "contains an invalid character", value);
73572bbe6cca6470e86b56081c3fbe3cbb4360f5271Lajos Molnar                    } else {
73672bbe6cca6470e86b56081c3fbe3cbb4360f5271Lajos Molnar                        try {
73772bbe6cca6470e86b56081c3fbe3cbb4360f5271Lajos Molnar                            region.mLines = Integer.parseInt(value);
73872bbe6cca6470e86b56081c3fbe3cbb4360f5271Lajos Molnar                            assert(region.mLines >= 0); // lines contains only digits
73972bbe6cca6470e86b56081c3fbe3cbb4360f5271Lajos Molnar                        } catch (NumberFormatException e) {
74072bbe6cca6470e86b56081c3fbe3cbb4360f5271Lajos Molnar                            log_warning("region setting", name, "is not numeric", value);
74176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                        }
74276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    }
74376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                } else if (name.equals("regionanchor") ||
74476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                           name.equals("viewportanchor")) {
74576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    int commaAt = value.indexOf(",");
74676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    if (commaAt < 0) {
74776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                        log_warning("region setting", name, "contains no comma", value);
74876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                        continue;
74976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    }
75076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
75176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    String anchorX = value.substring(0, commaAt);
75276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    String anchorY = value.substring(commaAt + 1);
75376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    float x, y;
75476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
75576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    try {
75676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                        x = parseFloatPercentage(anchorX);
75776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    } catch (NumberFormatException e) {
75876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                        log_warning("region setting", name,
75976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                                "has invalid x component", e.getMessage(), anchorX);
76076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                        continue;
76176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    }
76276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    try {
76376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                        y = parseFloatPercentage(anchorY);
76476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    } catch (NumberFormatException e) {
76576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                        log_warning("region setting", name,
76676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                                "has invalid y component", e.getMessage(), anchorY);
76776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                        continue;
76876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    }
76976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
77076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    if (name.charAt(0) == 'r') {
77176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                        region.mAnchorPointX = x;
77276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                        region.mAnchorPointY = y;
77376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    } else {
77476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                        region.mViewportAnchorPointX = x;
77576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                        region.mViewportAnchorPointY = y;
77676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    }
77776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                } else if (name.equals("scroll")) {
77876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    if (value.equals("up")) {
77976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                        region.mScrollValue =
78076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                            TextTrackRegion.SCROLL_VALUE_SCROLL_UP;
78176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    } else {
78276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                        log_warning("region setting", name, "has invalid value", value);
78376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    }
78476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                }
78576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            }
78676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            return region;
78776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        }
78876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
78976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        @Override
79076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        public void parse(String line)  {
79176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            if (line.length() == 0) {
79276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                mPhase = mParseCueId;
79376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            } else if (line.contains("-->")) {
79476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                mPhase = mParseCueTime;
79576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                mPhase.parse(line);
79676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            } else {
79776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                int colonAt = line.indexOf(':');
79876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                if (colonAt <= 0 || colonAt >= line.length() - 1) {
79976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    log_warning("meta data header has invalid format", line);
80076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                }
80176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                String name = line.substring(0, colonAt);
80276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                String value = line.substring(colonAt + 1);
80376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
80476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                if (name.equals("Region")) {
80576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    TextTrackRegion region = parseRegion(value);
80676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    mListener.onRegionParsed(region);
80776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                }
80876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            }
80976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        }
81076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    };
81176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
81276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    final private Phase mParseCueId = new Phase() {
81376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        @Override
81476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        public void parse(String line) {
81576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            if (line.length() == 0) {
81676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                return;
81776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            }
81876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
81976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            assert(mCue == null);
82076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
82176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            if (line.equals("NOTE") || line.startsWith("NOTE ")) {
82276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                mPhase = mParseCueText;
82376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            }
82476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
82576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            mCue = new TextTrackCue();
82676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            mCueTexts.clear();
82776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
82876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            mPhase = mParseCueTime;
82976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            if (line.contains("-->")) {
83076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                mPhase.parse(line);
83176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            } else {
83276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                mCue.mId = line;
83376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            }
83476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        }
83576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    };
83676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
83776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    final private Phase mParseCueTime = new Phase() {
83876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        @Override
83976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        public void parse(String line) {
84076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            int arrowAt = line.indexOf("-->");
84176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            if (arrowAt < 0) {
84276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                mCue = null;
84376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                mPhase = mParseCueId;
84476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                return;
84576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            }
84676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
84776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            String start = line.substring(0, arrowAt).trim();
84876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            // convert only initial and first other white-space to space
84976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            String rest = line.substring(arrowAt + 3)
85076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    .replaceFirst("^\\s+", "").replaceFirst("\\s+", " ");
85176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            int spaceAt = rest.indexOf(' ');
85276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            String end = spaceAt > 0 ? rest.substring(0, spaceAt) : rest;
85376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            rest = spaceAt > 0 ? rest.substring(spaceAt + 1) : "";
85476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
85576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            mCue.mStartTimeMs = parseTimestampMs(start);
85676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            mCue.mEndTimeMs = parseTimestampMs(end);
85776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            for (String setting: rest.split(" +")) {
85876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                int colonAt = setting.indexOf(':');
85976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                if (colonAt <= 0 || colonAt == setting.length() - 1) {
86076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    continue;
86176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                }
86276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                String name = setting.substring(0, colonAt);
86376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                String value = setting.substring(colonAt + 1);
86476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
86576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                if (name.equals("region")) {
86676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    mCue.mRegionId = value;
86776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                } else if (name.equals("vertical")) {
86876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    if (value.equals("rl")) {
86976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                        mCue.mWritingDirection =
87076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                            TextTrackCue.WRITING_DIRECTION_VERTICAL_RL;
87176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    } else if (value.equals("lr")) {
87276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                        mCue.mWritingDirection =
87376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                            TextTrackCue.WRITING_DIRECTION_VERTICAL_LR;
87476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    } else {
87576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                        log_warning("cue setting", name, "has invalid value", value);
87676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    }
87776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                } else if (name.equals("line")) {
87876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    try {
87976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                        /* TRICKY: we know that there are no spaces in value */
88076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                        assert(value.indexOf(' ') < 0);
88176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                        if (value.endsWith("%")) {
88276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                            mCue.mSnapToLines = false;
88372bbe6cca6470e86b56081c3fbe3cbb4360f5271Lajos Molnar                            mCue.mLinePosition = parseIntPercentage(value);
88472bbe6cca6470e86b56081c3fbe3cbb4360f5271Lajos Molnar                        } else if (value.matches(".*[^0-9].*")) {
88572bbe6cca6470e86b56081c3fbe3cbb4360f5271Lajos Molnar                            log_warning("cue setting", name,
88672bbe6cca6470e86b56081c3fbe3cbb4360f5271Lajos Molnar                                    "contains an invalid character", value);
88776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                        } else {
88876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                            mCue.mSnapToLines = true;
88976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                            mCue.mLinePosition = Integer.parseInt(value);
89076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                        }
89176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    } catch (NumberFormatException e) {
89276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                        log_warning("cue setting", name,
89372bbe6cca6470e86b56081c3fbe3cbb4360f5271Lajos Molnar                                "is not numeric or percentage", value);
89476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    }
89572bbe6cca6470e86b56081c3fbe3cbb4360f5271Lajos Molnar                    // TODO: add support for optional alignment value [,start|middle|end]
89676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                } else if (name.equals("position")) {
89776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    try {
89876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                        mCue.mTextPosition = parseIntPercentage(value);
89976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    } catch (NumberFormatException e) {
90076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                        log_warning("cue setting", name,
90176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                               "is not numeric or percentage", value);
90276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    }
90376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                } else if (name.equals("size")) {
90476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    try {
90576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                        mCue.mSize = parseIntPercentage(value);
90676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    } catch (NumberFormatException e) {
90776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                        log_warning("cue setting", name,
90876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                               "is not numeric or percentage", value);
90976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    }
91076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                } else if (name.equals("align")) {
91176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    if (value.equals("start")) {
91276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                        mCue.mAlignment = TextTrackCue.ALIGNMENT_START;
91376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    } else if (value.equals("middle")) {
91476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                        mCue.mAlignment = TextTrackCue.ALIGNMENT_MIDDLE;
91576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    } else if (value.equals("end")) {
91676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                        mCue.mAlignment = TextTrackCue.ALIGNMENT_END;
91776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    } else if (value.equals("left")) {
91876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                        mCue.mAlignment = TextTrackCue.ALIGNMENT_LEFT;
91976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    } else if (value.equals("right")) {
92076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                        mCue.mAlignment = TextTrackCue.ALIGNMENT_RIGHT;
92176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    } else {
92276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                        log_warning("cue setting", name, "has invalid value", value);
92376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                        continue;
92476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    }
92576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                }
92676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            }
92776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
92876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            if (mCue.mLinePosition != null ||
92976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    mCue.mSize != 100 ||
93076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    (mCue.mWritingDirection !=
93176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                        TextTrackCue.WRITING_DIRECTION_HORIZONTAL)) {
93276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                mCue.mRegionId = "";
93376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            }
93476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
93576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            mPhase = mParseCueText;
93676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        }
93776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    };
93876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
93976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    /* also used for notes */
94076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    final private Phase mParseCueText = new Phase() {
94176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        @Override
94276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        public void parse(String line) {
94376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            if (line.length() == 0) {
94476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                yieldCue();
94576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                mPhase = mParseCueId;
94676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                return;
94776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            } else if (mCue != null) {
94876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                mCueTexts.add(line);
94976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            }
95076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        }
95176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    };
95276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
95376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    private void log_warning(
95476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            String nameType, String name, String message,
95576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            String subMessage, String value) {
95676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        Log.w(this.getClass().getName(), nameType + " '" + name + "' " +
95776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                message + " ('" + value + "' " + subMessage + ")");
95876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    }
95976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
96076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    private void log_warning(
96176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            String nameType, String name, String message, String value) {
96276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        Log.w(this.getClass().getName(), nameType + " '" + name + "' " +
96376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                message + " ('" + value + "')");
96476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    }
96576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
96676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    private void log_warning(String message, String value) {
96776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        Log.w(this.getClass().getName(), message + " ('" + value + "')");
96876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    }
96976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar}
97076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
97176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar/** @hide */
97276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnarinterface WebVttCueListener {
97376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    void onCueParsed(TextTrackCue cue);
97476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    void onRegionParsed(TextTrackRegion region);
97576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar}
97676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
97776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar/** @hide */
97876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnarclass WebVttTrack extends SubtitleTrack implements WebVttCueListener {
97976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    private static final String TAG = "WebVttTrack";
98076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
98176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    private final WebVttParser mParser = new WebVttParser(this);
98276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    private final UnstyledTextExtractor mExtractor =
98376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        new UnstyledTextExtractor();
98476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    private final Tokenizer mTokenizer = new Tokenizer(mExtractor);
98576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    private final Vector<Long> mTimestamps = new Vector<Long>();
986d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    private final WebVttRenderingWidget mRenderingWidget;
98776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
98876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    private final Map<String, TextTrackRegion> mRegions =
98976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        new HashMap<String, TextTrackRegion>();
99076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    private Long mCurrentRunID;
99176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
992d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    WebVttTrack(WebVttRenderingWidget renderingWidget, MediaFormat format) {
99376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        super(format);
994d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
995d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        mRenderingWidget = renderingWidget;
99676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    }
99776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
99876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    @Override
999d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    public WebVttRenderingWidget getRenderingWidget() {
1000d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        return mRenderingWidget;
100176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    }
100276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
100376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    @Override
1004079fa9683dc062f154333a661f5e84a5ac5e43c7Chong Zhang    public void onData(byte[] data, boolean eos, long runID) {
1005079fa9683dc062f154333a661f5e84a5ac5e43c7Chong Zhang        try {
1006079fa9683dc062f154333a661f5e84a5ac5e43c7Chong Zhang            String str = new String(data, "UTF-8");
1007079fa9683dc062f154333a661f5e84a5ac5e43c7Chong Zhang
1008079fa9683dc062f154333a661f5e84a5ac5e43c7Chong Zhang            // implement intermixing restriction for WebVTT only for now
1009079fa9683dc062f154333a661f5e84a5ac5e43c7Chong Zhang            synchronized(mParser) {
1010079fa9683dc062f154333a661f5e84a5ac5e43c7Chong Zhang                if (mCurrentRunID != null && runID != mCurrentRunID) {
1011079fa9683dc062f154333a661f5e84a5ac5e43c7Chong Zhang                    throw new IllegalStateException(
1012079fa9683dc062f154333a661f5e84a5ac5e43c7Chong Zhang                            "Run #" + mCurrentRunID +
1013079fa9683dc062f154333a661f5e84a5ac5e43c7Chong Zhang                            " in progress.  Cannot process run #" + runID);
1014079fa9683dc062f154333a661f5e84a5ac5e43c7Chong Zhang                }
1015079fa9683dc062f154333a661f5e84a5ac5e43c7Chong Zhang                mCurrentRunID = runID;
1016079fa9683dc062f154333a661f5e84a5ac5e43c7Chong Zhang                mParser.parse(str);
1017079fa9683dc062f154333a661f5e84a5ac5e43c7Chong Zhang                if (eos) {
1018079fa9683dc062f154333a661f5e84a5ac5e43c7Chong Zhang                    finishedRun(runID);
1019079fa9683dc062f154333a661f5e84a5ac5e43c7Chong Zhang                    mParser.eos();
1020079fa9683dc062f154333a661f5e84a5ac5e43c7Chong Zhang                    mRegions.clear();
1021079fa9683dc062f154333a661f5e84a5ac5e43c7Chong Zhang                    mCurrentRunID = null;
1022079fa9683dc062f154333a661f5e84a5ac5e43c7Chong Zhang                }
102376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            }
1024079fa9683dc062f154333a661f5e84a5ac5e43c7Chong Zhang        } catch (java.io.UnsupportedEncodingException e) {
1025079fa9683dc062f154333a661f5e84a5ac5e43c7Chong Zhang            Log.w(TAG, "subtitle data is not UTF-8 encoded: " + e);
102676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        }
102776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    }
102876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
102976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    @Override
103076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    public void onCueParsed(TextTrackCue cue) {
103176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        synchronized (mParser) {
103276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            // resolve region
103376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            if (cue.mRegionId.length() != 0) {
103476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                cue.mRegion = mRegions.get(cue.mRegionId);
103576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            }
103676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
103776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            if (DEBUG) Log.v(TAG, "adding cue " + cue);
103876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
103976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            // tokenize text track string-lines into lines of spans
104076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            mTokenizer.reset();
104176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            for (String s: cue.mStrings) {
104276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                mTokenizer.tokenize(s);
104376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            }
104476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            cue.mLines = mExtractor.getText();
104576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            if (DEBUG) Log.v(TAG, cue.appendLinesToBuilder(
104676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    cue.appendStringsToBuilder(
104776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                        new StringBuilder()).append(" simplified to: "))
104876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                            .toString());
104976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
105076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            // extract inner timestamps
105176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            for (TextTrackCueSpan[] line: cue.mLines) {
105276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                for (TextTrackCueSpan span: line) {
105376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    if (span.mTimestampMs > cue.mStartTimeMs &&
105476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                            span.mTimestampMs < cue.mEndTimeMs &&
105576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                            !mTimestamps.contains(span.mTimestampMs)) {
105676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                        mTimestamps.add(span.mTimestampMs);
105776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    }
105876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                }
105976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            }
106076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
106176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            if (mTimestamps.size() > 0) {
106276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                cue.mInnerTimesMs = new long[mTimestamps.size()];
106376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                for (int ix=0; ix < mTimestamps.size(); ++ix) {
106476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                    cue.mInnerTimesMs[ix] = mTimestamps.get(ix);
106576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                }
106676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                mTimestamps.clear();
106776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            } else {
106876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                cue.mInnerTimesMs = null;
106976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            }
107076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
107176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            cue.mRunID = mCurrentRunID;
107276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        }
107376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
107476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        addCue(cue);
107576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    }
107676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
107776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    @Override
107876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    public void onRegionParsed(TextTrackRegion region) {
107976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        synchronized(mParser) {
108076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            mRegions.put(region.mId, region);
108176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        }
108276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    }
108376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
1084d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    @Override
108576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    public void updateView(Vector<SubtitleTrack.Cue> activeCues) {
108676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        if (!mVisible) {
108776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            // don't keep the state if we are not visible
108876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            return;
108976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        }
109076935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
109176935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        if (DEBUG && mTimeProvider != null) {
109276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            try {
109376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                Log.d(TAG, "at " +
109476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                        (mTimeProvider.getCurrentTimeUs(false, true) / 1000) +
109576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                        " ms the active cues are:");
109676935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            } catch (IllegalStateException e) {
109776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                Log.d(TAG, "at (illegal state) the active cues are:");
109876935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            }
109976935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        }
1100d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
11013cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih        if (mRenderingWidget != null) {
11023cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih            mRenderingWidget.setActiveCues(activeCues);
11033cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih        }
1104d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    }
1105d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette}
1106d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1107d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette/**
1108d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette * Widget capable of rendering WebVTT captions.
1109d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette *
1110d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette * @hide
1111d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette */
1112d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viveretteclass WebVttRenderingWidget extends ViewGroup implements SubtitleTrack.RenderingWidget {
1113d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    private static final boolean DEBUG = false;
1114e88aee8ad85b01229b12dbc0c3cc2f0b8b490192Alan Viverette
1115e88aee8ad85b01229b12dbc0c3cc2f0b8b490192Alan Viverette    private static final CaptionStyle DEFAULT_CAPTION_STYLE = CaptionStyle.DEFAULT;
1116e88aee8ad85b01229b12dbc0c3cc2f0b8b490192Alan Viverette
1117d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    private static final int DEBUG_REGION_BACKGROUND = 0x800000FF;
1118d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    private static final int DEBUG_CUE_BACKGROUND = 0x80FF0000;
1119d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1120d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    /** WebVtt specifies line height as 5.3% of the viewport height. */
1121d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    private static final float LINE_HEIGHT_RATIO = 0.0533f;
1122d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1123d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    /** Map of active regions, used to determine enter/exit. */
1124d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    private final ArrayMap<TextTrackRegion, RegionLayout> mRegionBoxes =
1125d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            new ArrayMap<TextTrackRegion, RegionLayout>();
1126d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1127d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    /** Map of active cues, used to determine enter/exit. */
1128d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    private final ArrayMap<TextTrackCue, CueLayout> mCueBoxes =
1129d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            new ArrayMap<TextTrackCue, CueLayout>();
1130d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1131d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    /** Captioning manager, used to obtain and track caption properties. */
1132d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    private final CaptioningManager mManager;
1133d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1134d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    /** Callback for rendering changes. */
1135d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    private OnChangedListener mListener;
1136d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1137d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    /** Current caption style. */
1138d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    private CaptionStyle mCaptionStyle;
1139d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1140d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    /** Current font size, computed from font scaling factor and height. */
1141d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    private float mFontSize;
1142d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1143d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    /** Whether a caption style change listener is registered. */
1144d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    private boolean mHasChangeListener;
1145d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1146d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    public WebVttRenderingWidget(Context context) {
1147d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        this(context, null);
1148d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    }
1149d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1150d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    public WebVttRenderingWidget(Context context, AttributeSet attrs) {
1151d6479ec5eec13914f656f6be996d95fe1610fd57Alan Viverette        this(context, attrs, 0);
1152d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    }
1153d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1154617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette    public WebVttRenderingWidget(Context context, AttributeSet attrs, int defStyleAttr) {
1155617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette        this(context, attrs, defStyleAttr, 0);
1156617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette    }
1157617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette
1158e88aee8ad85b01229b12dbc0c3cc2f0b8b490192Alan Viverette    public WebVttRenderingWidget(
1159e88aee8ad85b01229b12dbc0c3cc2f0b8b490192Alan Viverette            Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
1160617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette        super(context, attrs, defStyleAttr, defStyleRes);
1161d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1162d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        // Cannot render text over video when layer type is hardware.
1163d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        setLayerType(View.LAYER_TYPE_SOFTWARE, null);
1164d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1165d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        mManager = (CaptioningManager) context.getSystemService(Context.CAPTIONING_SERVICE);
1166d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        mCaptionStyle = mManager.getUserStyle();
1167d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        mFontSize = mManager.getFontScale() * getHeight() * LINE_HEIGHT_RATIO;
1168d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    }
1169d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1170d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    @Override
1171d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    public void setSize(int width, int height) {
1172d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        final int widthSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY);
1173d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        final int heightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
1174d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1175d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        measure(widthSpec, heightSpec);
1176d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        layout(0, 0, width, height);
1177d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    }
1178d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1179d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    @Override
1180d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    public void onAttachedToWindow() {
1181d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        super.onAttachedToWindow();
1182d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1183d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        manageChangeListener();
1184d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    }
1185d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1186d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    @Override
1187d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    public void onDetachedFromWindow() {
1188d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        super.onDetachedFromWindow();
1189d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1190d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        manageChangeListener();
1191d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    }
1192d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1193d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    @Override
1194d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    public void setOnChangedListener(OnChangedListener listener) {
1195d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        mListener = listener;
1196d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    }
1197d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1198d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    @Override
1199d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    public void setVisible(boolean visible) {
1200d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        if (visible) {
1201d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            setVisibility(View.VISIBLE);
1202d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        } else {
1203d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            setVisibility(View.GONE);
1204d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        }
1205d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1206d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        manageChangeListener();
1207d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    }
1208d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1209d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    /**
1210d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette     * Manages whether this renderer is listening for caption style changes.
1211d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette     */
1212d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    private void manageChangeListener() {
1213d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        final boolean needsListener = isAttachedToWindow() && getVisibility() == View.VISIBLE;
1214d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        if (mHasChangeListener != needsListener) {
1215d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            mHasChangeListener = needsListener;
1216d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1217d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            if (needsListener) {
1218d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                mManager.addCaptioningChangeListener(mCaptioningListener);
1219d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1220d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                final CaptionStyle captionStyle = mManager.getUserStyle();
1221d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                final float fontSize = mManager.getFontScale() * getHeight() * LINE_HEIGHT_RATIO;
1222d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                setCaptionStyle(captionStyle, fontSize);
1223d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            } else {
1224d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                mManager.removeCaptioningChangeListener(mCaptioningListener);
1225d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            }
1226d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        }
1227d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    }
1228d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1229d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    public void setActiveCues(Vector<SubtitleTrack.Cue> activeCues) {
1230d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        final Context context = getContext();
1231d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        final CaptionStyle captionStyle = mCaptionStyle;
1232d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        final float fontSize = mFontSize;
1233d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1234d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        prepForPrune();
1235d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1236d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        // Ensure we have all necessary cue and region boxes.
1237d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        final int count = activeCues.size();
1238d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        for (int i = 0; i < count; i++) {
1239d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            final TextTrackCue cue = (TextTrackCue) activeCues.get(i);
1240d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            final TextTrackRegion region = cue.mRegion;
1241d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            if (region != null) {
1242d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                RegionLayout regionBox = mRegionBoxes.get(region);
1243d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                if (regionBox == null) {
1244d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                    regionBox = new RegionLayout(context, region, captionStyle, fontSize);
1245d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                    mRegionBoxes.put(region, regionBox);
1246d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                    addView(regionBox, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
124776935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                }
1248d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                regionBox.put(cue);
1249d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            } else {
1250d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                CueLayout cueBox = mCueBoxes.get(cue);
1251d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                if (cueBox == null) {
1252d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                    cueBox = new CueLayout(context, cue, captionStyle, fontSize);
1253d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                    mCueBoxes.put(cue, cueBox);
1254d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                    addView(cueBox, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
125576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar                }
1256d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                cueBox.update();
1257d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                cueBox.setOrder(i);
1258d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            }
1259d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        }
1260d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1261d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        prune();
1262d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1263d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        // Force measurement and layout.
1264d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        final int width = getWidth();
1265d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        final int height = getHeight();
1266d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        setSize(width, height);
1267d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1268d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        if (mListener != null) {
1269d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            mListener.onChanged(this);
1270d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        }
1271d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    }
1272d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1273d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    private void setCaptionStyle(CaptionStyle captionStyle, float fontSize) {
1274e88aee8ad85b01229b12dbc0c3cc2f0b8b490192Alan Viverette        captionStyle = DEFAULT_CAPTION_STYLE.applyStyle(captionStyle);
1275d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        mCaptionStyle = captionStyle;
1276d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        mFontSize = fontSize;
1277d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1278d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        final int cueCount = mCueBoxes.size();
1279d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        for (int i = 0; i < cueCount; i++) {
1280d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            final CueLayout cueBox = mCueBoxes.valueAt(i);
1281d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            cueBox.setCaptionStyle(captionStyle, fontSize);
1282d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        }
1283d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1284d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        final int regionCount = mRegionBoxes.size();
1285d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        for (int i = 0; i < regionCount; i++) {
1286d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            final RegionLayout regionBox = mRegionBoxes.valueAt(i);
1287d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            regionBox.setCaptionStyle(captionStyle, fontSize);
1288d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        }
1289d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    }
1290d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1291d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    /**
1292d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette     * Remove inactive cues and regions.
1293d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette     */
1294d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    private void prune() {
1295d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        int regionCount = mRegionBoxes.size();
1296d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        for (int i = 0; i < regionCount; i++) {
1297d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            final RegionLayout regionBox = mRegionBoxes.valueAt(i);
1298d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            if (regionBox.prune()) {
1299d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                removeView(regionBox);
1300d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                mRegionBoxes.removeAt(i);
1301d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                regionCount--;
1302d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                i--;
130376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar            }
130476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        }
130576935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar
1306d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        int cueCount = mCueBoxes.size();
1307d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        for (int i = 0; i < cueCount; i++) {
1308d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            final CueLayout cueBox = mCueBoxes.valueAt(i);
1309d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            if (!cueBox.isActive()) {
1310d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                removeView(cueBox);
1311d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                mCueBoxes.removeAt(i);
1312d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                cueCount--;
1313d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                i--;
1314d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            }
1315d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        }
1316d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    }
1317d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1318d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    /**
1319d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette     * Reset active cues and regions.
1320d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette     */
1321d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    private void prepForPrune() {
1322d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        final int regionCount = mRegionBoxes.size();
1323d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        for (int i = 0; i < regionCount; i++) {
1324d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            final RegionLayout regionBox = mRegionBoxes.valueAt(i);
1325d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            regionBox.prepForPrune();
1326d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        }
1327d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1328d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        final int cueCount = mCueBoxes.size();
1329d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        for (int i = 0; i < cueCount; i++) {
1330d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            final CueLayout cueBox = mCueBoxes.valueAt(i);
1331d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            cueBox.prepForPrune();
1332d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        }
1333d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    }
1334d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1335d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    @Override
1336d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
1337d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
1338d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1339d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        final int regionCount = mRegionBoxes.size();
1340d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        for (int i = 0; i < regionCount; i++) {
1341d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            final RegionLayout regionBox = mRegionBoxes.valueAt(i);
1342d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            regionBox.measureForParent(widthMeasureSpec, heightMeasureSpec);
1343d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        }
1344d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1345d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        final int cueCount = mCueBoxes.size();
1346d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        for (int i = 0; i < cueCount; i++) {
1347d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            final CueLayout cueBox = mCueBoxes.valueAt(i);
1348d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            cueBox.measureForParent(widthMeasureSpec, heightMeasureSpec);
1349d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        }
1350d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    }
1351d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1352d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    @Override
1353d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    protected void onLayout(boolean changed, int l, int t, int r, int b) {
1354d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        final int viewportWidth = r - l;
1355d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        final int viewportHeight = b - t;
1356d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1357d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        setCaptionStyle(mCaptionStyle,
1358d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                mManager.getFontScale() * LINE_HEIGHT_RATIO * viewportHeight);
1359d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1360d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        final int regionCount = mRegionBoxes.size();
1361d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        for (int i = 0; i < regionCount; i++) {
1362d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            final RegionLayout regionBox = mRegionBoxes.valueAt(i);
1363d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            layoutRegion(viewportWidth, viewportHeight, regionBox);
1364d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        }
1365d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1366d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        final int cueCount = mCueBoxes.size();
1367d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        for (int i = 0; i < cueCount; i++) {
1368d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            final CueLayout cueBox = mCueBoxes.valueAt(i);
1369d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            layoutCue(viewportWidth, viewportHeight, cueBox);
1370d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        }
1371d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    }
1372d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1373d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    /**
1374d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette     * Lays out a region within the viewport. The region handles layout for
1375d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette     * contained cues.
1376d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette     */
1377d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    private void layoutRegion(
1378d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            int viewportWidth, int viewportHeight,
1379d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            RegionLayout regionBox) {
1380d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        final TextTrackRegion region = regionBox.getRegion();
1381d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        final int regionHeight = regionBox.getMeasuredHeight();
1382d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        final int regionWidth = regionBox.getMeasuredWidth();
1383d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1384d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        // TODO: Account for region anchor point.
1385d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        final float x = region.mViewportAnchorPointX;
1386d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        final float y = region.mViewportAnchorPointY;
1387d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        final int left = (int) (x * (viewportWidth - regionWidth) / 100);
1388d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        final int top = (int) (y * (viewportHeight - regionHeight) / 100);
1389d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1390d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        regionBox.layout(left, top, left + regionWidth, top + regionHeight);
1391d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    }
1392d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1393d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    /**
1394d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette     * Lays out a cue within the viewport.
1395d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette     */
1396d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    private void layoutCue(
1397d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            int viewportWidth, int viewportHeight, CueLayout cueBox) {
1398d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        final TextTrackCue cue = cueBox.getCue();
1399d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        final int direction = getLayoutDirection();
1400d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        final int absAlignment = resolveCueAlignment(direction, cue.mAlignment);
1401d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        final boolean cueSnapToLines = cue.mSnapToLines;
1402d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1403d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        int size = 100 * cueBox.getMeasuredWidth() / viewportWidth;
1404d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1405d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        // Determine raw x-position.
1406d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        int xPosition;
1407d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        switch (absAlignment) {
1408d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            case TextTrackCue.ALIGNMENT_LEFT:
1409d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                xPosition = cue.mTextPosition;
1410d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                break;
1411d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            case TextTrackCue.ALIGNMENT_RIGHT:
1412d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                xPosition = cue.mTextPosition - size;
1413d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                break;
1414d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            case TextTrackCue.ALIGNMENT_MIDDLE:
1415d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            default:
1416d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                xPosition = cue.mTextPosition - size / 2;
1417d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                break;
1418d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        }
1419d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1420d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        // Adjust x-position for layout.
1421d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        if (direction == LAYOUT_DIRECTION_RTL) {
1422d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            xPosition = 100 - xPosition;
1423d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        }
1424d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1425d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        // If the text track cue snap-to-lines flag is set, adjust
1426d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        // x-position and size for padding. This is equivalent to placing the
1427d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        // cue within the title-safe area.
1428d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        if (cueSnapToLines) {
1429d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            final int paddingLeft = 100 * getPaddingLeft() / viewportWidth;
1430d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            final int paddingRight = 100 * getPaddingRight() / viewportWidth;
1431d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            if (xPosition < paddingLeft && xPosition + size > paddingLeft) {
1432d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                xPosition += paddingLeft;
1433d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                size -= paddingLeft;
1434d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            }
1435d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            final float rightEdge = 100 - paddingRight;
1436d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            if (xPosition < rightEdge && xPosition + size > rightEdge) {
1437d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                size -= paddingRight;
1438d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            }
1439d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        }
1440d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1441d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        // Compute absolute left position and width.
1442d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        final int left = xPosition * viewportWidth / 100;
1443d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        final int width = size * viewportWidth / 100;
1444d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1445d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        // Determine initial y-position.
1446d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        final int yPosition = calculateLinePosition(cueBox);
1447d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1448d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        // Compute absolute final top position and height.
1449d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        final int height = cueBox.getMeasuredHeight();
1450d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        final int top;
1451d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        if (yPosition < 0) {
1452d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            // TODO: This needs to use the actual height of prior boxes.
1453d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            top = viewportHeight + yPosition * height;
1454d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        } else {
1455d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            top = yPosition * (viewportHeight - height) / 100;
1456d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        }
1457d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1458d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        // Layout cue in final position.
1459d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        cueBox.layout(left, top, left + width, top + height);
1460d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    }
1461d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1462d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    /**
1463d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette     * Calculates the line position for a cue.
1464d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette     * <p>
1465d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette     * If the resulting position is negative, it represents a bottom-aligned
1466d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette     * position relative to the number of active cues. Otherwise, it represents
1467d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette     * a percentage [0-100] of the viewport height.
1468d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette     */
1469d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    private int calculateLinePosition(CueLayout cueBox) {
1470d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        final TextTrackCue cue = cueBox.getCue();
1471d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        final Integer linePosition = cue.mLinePosition;
1472d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        final boolean snapToLines = cue.mSnapToLines;
1473d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        final boolean autoPosition = (linePosition == null);
1474d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1475d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        if (!snapToLines && !autoPosition && (linePosition < 0 || linePosition > 100)) {
1476d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            // Invalid line position defaults to 100.
1477d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            return 100;
1478d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        } else if (!autoPosition) {
1479d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            // Use the valid, supplied line position.
1480d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            return linePosition;
1481d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        } else if (!snapToLines) {
1482d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            // Automatic, non-snapped line position defaults to 100.
1483d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            return 100;
1484d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        } else {
1485d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            // Automatic snapped line position uses active cue order.
1486d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            return -(cueBox.mOrder + 1);
1487d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        }
1488d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    }
1489d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1490d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    /**
1491d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette     * Resolves cue alignment according to the specified layout direction.
1492d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette     */
1493d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    private static int resolveCueAlignment(int layoutDirection, int alignment) {
1494d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        switch (alignment) {
1495d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            case TextTrackCue.ALIGNMENT_START:
1496d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                return layoutDirection == View.LAYOUT_DIRECTION_LTR ?
1497d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                        TextTrackCue.ALIGNMENT_LEFT : TextTrackCue.ALIGNMENT_RIGHT;
1498d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            case TextTrackCue.ALIGNMENT_END:
1499d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                return layoutDirection == View.LAYOUT_DIRECTION_LTR ?
1500d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                        TextTrackCue.ALIGNMENT_RIGHT : TextTrackCue.ALIGNMENT_LEFT;
1501d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        }
1502d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        return alignment;
1503d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    }
1504d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1505d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    private final CaptioningChangeListener mCaptioningListener = new CaptioningChangeListener() {
1506d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        @Override
1507d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        public void onFontScaleChanged(float fontScale) {
1508d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            final float fontSize = fontScale * getHeight() * LINE_HEIGHT_RATIO;
1509d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            setCaptionStyle(mCaptionStyle, fontSize);
1510d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        }
1511d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1512d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        @Override
1513d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        public void onUserStyleChanged(CaptionStyle userStyle) {
1514d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            setCaptionStyle(userStyle, mFontSize);
1515d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        }
1516d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    };
1517d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1518d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    /**
1519d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette     * A text track region represents a portion of the video viewport and
1520d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette     * provides a rendering area for text track cues.
1521d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette     */
1522d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    private static class RegionLayout extends LinearLayout {
1523d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        private final ArrayList<CueLayout> mRegionCueBoxes = new ArrayList<CueLayout>();
1524d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        private final TextTrackRegion mRegion;
1525d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1526d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        private CaptionStyle mCaptionStyle;
1527d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        private float mFontSize;
1528d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1529d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        public RegionLayout(Context context, TextTrackRegion region, CaptionStyle captionStyle,
1530d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                float fontSize) {
1531d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            super(context);
1532d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1533d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            mRegion = region;
1534d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            mCaptionStyle = captionStyle;
1535d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            mFontSize = fontSize;
1536d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1537d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            // TODO: Add support for vertical text
1538d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            setOrientation(VERTICAL);
1539d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1540d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            if (DEBUG) {
1541d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                setBackgroundColor(DEBUG_REGION_BACKGROUND);
154255d70620d9fda8afafb2fdec59757a710eec0e89Alan Viverette            } else {
154355d70620d9fda8afafb2fdec59757a710eec0e89Alan Viverette                setBackgroundColor(captionStyle.windowColor);
1544d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            }
1545d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        }
1546d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1547d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        public void setCaptionStyle(CaptionStyle captionStyle, float fontSize) {
1548d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            mCaptionStyle = captionStyle;
1549d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            mFontSize = fontSize;
1550d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1551d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            final int cueCount = mRegionCueBoxes.size();
1552d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            for (int i = 0; i < cueCount; i++) {
1553d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                final CueLayout cueBox = mRegionCueBoxes.get(i);
1554d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                cueBox.setCaptionStyle(captionStyle, fontSize);
1555d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            }
155655d70620d9fda8afafb2fdec59757a710eec0e89Alan Viverette
155755d70620d9fda8afafb2fdec59757a710eec0e89Alan Viverette            setBackgroundColor(captionStyle.windowColor);
1558d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        }
1559d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1560d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        /**
1561d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette         * Performs the parent's measurement responsibilities, then
1562d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette         * automatically performs its own measurement.
1563d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette         */
1564d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        public void measureForParent(int widthMeasureSpec, int heightMeasureSpec) {
1565d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            final TextTrackRegion region = mRegion;
1566d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            final int specWidth = MeasureSpec.getSize(widthMeasureSpec);
1567d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            final int specHeight = MeasureSpec.getSize(heightMeasureSpec);
1568d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            final int width = (int) region.mWidth;
1569d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1570d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            // Determine the absolute maximum region size as the requested size.
1571d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            final int size = width * specWidth / 100;
1572d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1573d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            widthMeasureSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.AT_MOST);
1574d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            heightMeasureSpec = MeasureSpec.makeMeasureSpec(specHeight, MeasureSpec.AT_MOST);
1575d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            measure(widthMeasureSpec, heightMeasureSpec);
1576d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        }
1577d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1578d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        /**
1579d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette         * Prepares this region for pruning by setting all tracks as inactive.
1580d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette         * <p>
1581d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette         * Tracks that are added or updated using {@link #put(TextTrackCue)}
1582d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette         * after this calling this method will be marked as active.
1583d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette         */
1584d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        public void prepForPrune() {
1585d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            final int cueCount = mRegionCueBoxes.size();
1586d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            for (int i = 0; i < cueCount; i++) {
1587d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                final CueLayout cueBox = mRegionCueBoxes.get(i);
1588d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                cueBox.prepForPrune();
1589d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            }
1590d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        }
1591d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1592d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        /**
1593d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette         * Adds a {@link TextTrackCue} to this region. If the track had already
1594d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette         * been added, updates its active state.
1595d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette         *
1596d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette         * @param cue
1597d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette         */
1598d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        public void put(TextTrackCue cue) {
1599d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            final int cueCount = mRegionCueBoxes.size();
1600d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            for (int i = 0; i < cueCount; i++) {
1601d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                final CueLayout cueBox = mRegionCueBoxes.get(i);
1602d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                if (cueBox.getCue() == cue) {
1603d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                    cueBox.update();
1604d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                    return;
1605d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                }
1606d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            }
1607d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1608d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            final CueLayout cueBox = new CueLayout(getContext(), cue, mCaptionStyle, mFontSize);
16097fe420f31b4de2bbd7a89194384d0c9ac6293226Alan Viverette            mRegionCueBoxes.add(cueBox);
1610d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            addView(cueBox, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
1611d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1612d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            if (getChildCount() > mRegion.mLines) {
1613d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                removeViewAt(0);
1614d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            }
1615d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        }
1616d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1617d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        /**
1618d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette         * Remove all inactive tracks from this region.
1619d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette         *
1620d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette         * @return true if this region is empty and should be pruned
1621d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette         */
1622d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        public boolean prune() {
1623d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            int cueCount = mRegionCueBoxes.size();
1624d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            for (int i = 0; i < cueCount; i++) {
1625d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                final CueLayout cueBox = mRegionCueBoxes.get(i);
1626d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                if (!cueBox.isActive()) {
1627d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                    mRegionCueBoxes.remove(i);
1628d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                    removeView(cueBox);
1629d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                    cueCount--;
1630d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                    i--;
1631d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                }
1632d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            }
1633d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1634d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            return mRegionCueBoxes.isEmpty();
1635d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        }
1636d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1637d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        /**
1638d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette         * @return the region data backing this layout
1639d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette         */
1640d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        public TextTrackRegion getRegion() {
1641d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            return mRegion;
1642d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        }
1643d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    }
1644d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1645d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    /**
1646d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette     * A text track cue is the unit of time-sensitive data in a text track,
1647d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette     * corresponding for instance for subtitles and captions to the text that
1648d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette     * appears at a particular time and disappears at another time.
1649d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette     * <p>
1650d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette     * A single cue may contain multiple {@link SpanLayout}s, each representing a
1651d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette     * single line of text.
1652d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette     */
1653d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    private static class CueLayout extends LinearLayout {
1654d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        public final TextTrackCue mCue;
1655d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1656d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        private CaptionStyle mCaptionStyle;
1657d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        private float mFontSize;
1658d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1659d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        private boolean mActive;
1660d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        private int mOrder;
1661d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1662d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        public CueLayout(
1663d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                Context context, TextTrackCue cue, CaptionStyle captionStyle, float fontSize) {
1664d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            super(context);
1665d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1666d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            mCue = cue;
1667d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            mCaptionStyle = captionStyle;
1668d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            mFontSize = fontSize;
1669d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1670d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            // TODO: Add support for vertical text.
1671d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            final boolean horizontal = cue.mWritingDirection
1672d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                    == TextTrackCue.WRITING_DIRECTION_HORIZONTAL;
1673d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            setOrientation(horizontal ? VERTICAL : HORIZONTAL);
1674d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1675d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            switch (cue.mAlignment) {
1676d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                case TextTrackCue.ALIGNMENT_END:
1677d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                    setGravity(Gravity.END);
1678d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                    break;
1679d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                case TextTrackCue.ALIGNMENT_LEFT:
1680d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                    setGravity(Gravity.LEFT);
1681d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                    break;
1682d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                case TextTrackCue.ALIGNMENT_MIDDLE:
1683d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                    setGravity(horizontal
1684d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                            ? Gravity.CENTER_HORIZONTAL : Gravity.CENTER_VERTICAL);
1685d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                    break;
1686d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                case TextTrackCue.ALIGNMENT_RIGHT:
1687d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                    setGravity(Gravity.RIGHT);
1688d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                    break;
1689d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                case TextTrackCue.ALIGNMENT_START:
1690d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                    setGravity(Gravity.START);
1691d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                    break;
1692d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            }
1693d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1694d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            if (DEBUG) {
1695d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                setBackgroundColor(DEBUG_CUE_BACKGROUND);
1696d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            }
1697d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1698d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            update();
1699d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        }
1700d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1701d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        public void setCaptionStyle(CaptionStyle style, float fontSize) {
1702d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            mCaptionStyle = style;
1703d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            mFontSize = fontSize;
1704d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1705d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            final int n = getChildCount();
1706d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            for (int i = 0; i < n; i++) {
1707d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                final View child = getChildAt(i);
1708d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                if (child instanceof SpanLayout) {
1709d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                    ((SpanLayout) child).setCaptionStyle(style, fontSize);
1710d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                }
1711d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            }
1712d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        }
1713d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1714d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        public void prepForPrune() {
1715d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            mActive = false;
1716d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        }
1717d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1718d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        public void update() {
1719d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            mActive = true;
1720d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1721d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            removeAllViews();
1722d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
17237fe420f31b4de2bbd7a89194384d0c9ac6293226Alan Viverette            final int cueAlignment = resolveCueAlignment(getLayoutDirection(), mCue.mAlignment);
17247fe420f31b4de2bbd7a89194384d0c9ac6293226Alan Viverette            final Alignment alignment;
17257fe420f31b4de2bbd7a89194384d0c9ac6293226Alan Viverette            switch (cueAlignment) {
17267fe420f31b4de2bbd7a89194384d0c9ac6293226Alan Viverette                case TextTrackCue.ALIGNMENT_LEFT:
17277fe420f31b4de2bbd7a89194384d0c9ac6293226Alan Viverette                    alignment = Alignment.ALIGN_LEFT;
17287fe420f31b4de2bbd7a89194384d0c9ac6293226Alan Viverette                    break;
17297fe420f31b4de2bbd7a89194384d0c9ac6293226Alan Viverette                case TextTrackCue.ALIGNMENT_RIGHT:
17307fe420f31b4de2bbd7a89194384d0c9ac6293226Alan Viverette                    alignment = Alignment.ALIGN_RIGHT;
17317fe420f31b4de2bbd7a89194384d0c9ac6293226Alan Viverette                    break;
17327fe420f31b4de2bbd7a89194384d0c9ac6293226Alan Viverette                case TextTrackCue.ALIGNMENT_MIDDLE:
17337fe420f31b4de2bbd7a89194384d0c9ac6293226Alan Viverette                default:
17347fe420f31b4de2bbd7a89194384d0c9ac6293226Alan Viverette                    alignment = Alignment.ALIGN_CENTER;
17357fe420f31b4de2bbd7a89194384d0c9ac6293226Alan Viverette            }
17367fe420f31b4de2bbd7a89194384d0c9ac6293226Alan Viverette
1737d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            final CaptionStyle captionStyle = mCaptionStyle;
1738d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            final float fontSize = mFontSize;
1739d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            final TextTrackCueSpan[][] lines = mCue.mLines;
1740d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            final int lineCount = lines.length;
1741d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            for (int i = 0; i < lineCount; i++) {
1742d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                final SpanLayout lineBox = new SpanLayout(getContext(), lines[i]);
17437fe420f31b4de2bbd7a89194384d0c9ac6293226Alan Viverette                lineBox.setAlignment(alignment);
1744d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                lineBox.setCaptionStyle(captionStyle, fontSize);
1745d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1746d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                addView(lineBox, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
1747d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            }
1748d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        }
1749d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1750d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        @Override
1751d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
1752d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
1753d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        }
1754d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1755d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        /**
1756d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette         * Performs the parent's measurement responsibilities, then
1757d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette         * automatically performs its own measurement.
1758d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette         */
1759d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        public void measureForParent(int widthMeasureSpec, int heightMeasureSpec) {
1760d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            final TextTrackCue cue = mCue;
1761d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            final int specWidth = MeasureSpec.getSize(widthMeasureSpec);
1762d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            final int specHeight = MeasureSpec.getSize(heightMeasureSpec);
1763d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            final int direction = getLayoutDirection();
1764d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            final int absAlignment = resolveCueAlignment(direction, cue.mAlignment);
1765d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1766d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            // Determine the maximum size of cue based on its starting position
1767d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            // and the direction in which it grows.
1768d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            final int maximumSize;
1769d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            switch (absAlignment) {
1770d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                case TextTrackCue.ALIGNMENT_LEFT:
1771d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                    maximumSize = 100 - cue.mTextPosition;
1772d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                    break;
1773d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                case TextTrackCue.ALIGNMENT_RIGHT:
1774d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                    maximumSize = cue.mTextPosition;
1775d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                    break;
1776d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                case TextTrackCue.ALIGNMENT_MIDDLE:
1777d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                    if (cue.mTextPosition <= 50) {
1778d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                        maximumSize = cue.mTextPosition * 2;
1779d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                    } else {
1780d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                        maximumSize = (100 - cue.mTextPosition) * 2;
1781d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                    }
1782d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                    break;
1783d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                default:
1784d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                    maximumSize = 0;
1785d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            }
1786d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1787d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            // Determine absolute maximum cue size as the smaller of the
1788d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            // requested size and the maximum theoretical size.
1789d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            final int size = Math.min(cue.mSize, maximumSize) * specWidth / 100;
1790d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            widthMeasureSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.AT_MOST);
1791d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            heightMeasureSpec = MeasureSpec.makeMeasureSpec(specHeight, MeasureSpec.AT_MOST);
1792d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            measure(widthMeasureSpec, heightMeasureSpec);
1793d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        }
1794d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1795d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        /**
1796d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette         * Sets the order of this cue in the list of active cues.
1797d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette         *
1798d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette         * @param order the order of this cue in the list of active cues
1799d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette         */
1800d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        public void setOrder(int order) {
1801d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            mOrder = order;
1802d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        }
1803d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1804d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        /**
1805d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette         * @return whether this cue is marked as active
1806d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette         */
1807d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        public boolean isActive() {
1808d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            return mActive;
1809d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        }
1810d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1811d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        /**
1812d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette         * @return the cue data backing this layout
1813d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette         */
1814d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        public TextTrackCue getCue() {
1815d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            return mCue;
1816d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        }
1817d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    }
1818d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1819d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    /**
1820d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette     * A text track line represents a single line of text within a cue.
1821d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette     * <p>
1822d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette     * A single line may contain multiple spans, each representing a section of
1823d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette     * text that may be enabled or disabled at a particular time.
1824d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette     */
1825d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette    private static class SpanLayout extends SubtitleView {
1826d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        private final SpannableStringBuilder mBuilder = new SpannableStringBuilder();
1827d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        private final TextTrackCueSpan[] mSpans;
1828d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1829d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        public SpanLayout(Context context, TextTrackCueSpan[] spans) {
1830d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            super(context);
1831d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1832d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            mSpans = spans;
1833d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1834d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            update();
1835d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        }
1836d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1837d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        public void update() {
1838d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            final SpannableStringBuilder builder = mBuilder;
1839d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            final TextTrackCueSpan[] spans = mSpans;
1840d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1841d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            builder.clear();
1842d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            builder.clearSpans();
1843d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1844d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            final int spanCount = spans.length;
1845d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            for (int i = 0; i < spanCount; i++) {
1846d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                final TextTrackCueSpan span = spans[i];
1847d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                if (span.mEnabled) {
1848d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                    builder.append(spans[i].mText);
1849d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette                }
1850d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            }
1851d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1852d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            setText(builder);
1853d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        }
1854d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette
1855d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette        public void setCaptionStyle(CaptionStyle captionStyle, float fontSize) {
1856d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            setBackgroundColor(captionStyle.backgroundColor);
1857d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            setForegroundColor(captionStyle.foregroundColor);
1858d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            setEdgeColor(captionStyle.edgeColor);
1859d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            setEdgeType(captionStyle.edgeType);
1860d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            setTypeface(captionStyle.getTypeface());
1861d43daf361e993457e64eeeddab6d1a0ebc828c99Alan Viverette            setTextSize(fontSize);
186276935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar        }
186376935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar    }
186476935a5890ed8dd9d47f76f7593cc1c14c3844dbLajos Molnar}
1865