1978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung/*
2978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung * Copyright (C) 2015 The Android Open Source Project
3978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung *
4978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung * Licensed under the Apache License, Version 2.0 (the "License");
5978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung * you may not use this file except in compliance with the License.
6978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung * You may obtain a copy of the License at
7978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung *
8978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung *      http://www.apache.org/licenses/LICENSE-2.0
9978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung *
10978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung * Unless required by applicable law or agreed to in writing, software
11978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung * distributed under the License is distributed on an "AS IS" BASIS,
12978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung * See the License for the specific language governing permissions and
14978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung * limitations under the License.
15978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung */
16978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
17978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chungpackage android.media;
18978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
19978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chungimport android.content.Context;
20978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chungimport android.graphics.Canvas;
21978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chungimport android.graphics.Color;
22978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chungimport android.graphics.Paint;
23978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chungimport android.graphics.Rect;
24978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chungimport android.graphics.Typeface;
25978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chungimport android.os.Handler;
26978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chungimport android.os.Message;
27978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chungimport android.text.SpannableStringBuilder;
28978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chungimport android.text.Spanned;
29978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chungimport android.text.style.CharacterStyle;
30978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chungimport android.text.style.RelativeSizeSpan;
31978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chungimport android.text.style.StyleSpan;
32978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chungimport android.text.style.SubscriptSpan;
33978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chungimport android.text.style.SuperscriptSpan;
34978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chungimport android.text.style.UnderlineSpan;
35978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chungimport android.util.AttributeSet;
36978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chungimport android.text.Layout.Alignment;
37978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chungimport android.util.Log;
38978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chungimport android.text.TextUtils;
39978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chungimport android.view.Gravity;
40978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chungimport android.view.View;
41978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chungimport android.view.ViewGroup;
42978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chungimport android.view.accessibility.CaptioningManager;
43978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chungimport android.view.accessibility.CaptioningManager.CaptionStyle;
44978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chungimport android.widget.RelativeLayout;
45978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chungimport android.widget.TextView;
46978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
47978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chungimport java.io.UnsupportedEncodingException;
48978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chungimport java.nio.charset.Charset;
49978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chungimport java.nio.charset.StandardCharsets;
50978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chungimport java.util.ArrayList;
51978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chungimport java.util.Arrays;
52978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chungimport java.util.Comparator;
53978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chungimport java.util.List;
54978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chungimport java.util.Vector;
55978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
56978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chungimport com.android.internal.widget.SubtitleView;
57978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
58978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung/** @hide */
59978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chungpublic class Cea708CaptionRenderer extends SubtitleController.Renderer {
60978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    private final Context mContext;
61978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    private Cea708CCWidget mCCWidget;
62978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
63978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    public Cea708CaptionRenderer(Context context) {
64978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        mContext = context;
65978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    }
66978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
67978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    @Override
68978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    public boolean supports(MediaFormat format) {
69978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        if (format.containsKey(MediaFormat.KEY_MIME)) {
70978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            String mimeType = format.getString(MediaFormat.KEY_MIME);
71978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            return MediaPlayer.MEDIA_MIMETYPE_TEXT_CEA_708.equals(mimeType);
72978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
73978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        return false;
74978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    }
75978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
76978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    @Override
77978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    public SubtitleTrack createTrack(MediaFormat format) {
78978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        String mimeType = format.getString(MediaFormat.KEY_MIME);
79978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        if (MediaPlayer.MEDIA_MIMETYPE_TEXT_CEA_708.equals(mimeType)) {
80978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            if (mCCWidget == null) {
81978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                mCCWidget = new Cea708CCWidget(mContext);
82978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
83978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            return new Cea708CaptionTrack(mCCWidget, format);
84978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
85978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        throw new RuntimeException("No matching format: " + format.toString());
86978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    }
87978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung}
88978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
89978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung/** @hide */
90978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chungclass Cea708CaptionTrack extends SubtitleTrack {
91978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    private final Cea708CCParser mCCParser;
92978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    private final Cea708CCWidget mRenderingWidget;
93978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
94978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    Cea708CaptionTrack(Cea708CCWidget renderingWidget, MediaFormat format) {
95978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        super(format);
96978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
97978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        mRenderingWidget = renderingWidget;
98978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        mCCParser = new Cea708CCParser(mRenderingWidget);
99978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    }
100978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
101978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    @Override
102978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    public void onData(byte[] data, boolean eos, long runID) {
103978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        mCCParser.parse(data);
104978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    }
105978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
106978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    @Override
107978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    public RenderingWidget getRenderingWidget() {
108978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        return mRenderingWidget;
109978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    }
110978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
111978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    @Override
112978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    public void updateView(Vector<Cue> activeCues) {
113978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        // Overriding with NO-OP, CC rendering by-passes this
114978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    }
115978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung}
116978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
117978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung/**
118978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung * @hide
119978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung *
120978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung * A class for parsing CEA-708, which is the standard for closed captioning for ATSC DTV.
121978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung *
122978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung * <p>ATSC DTV closed caption data are carried on picture user data of video streams.
123978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung * This class starts to parse from picture user data payload, so extraction process of user_data
124978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung * from video streams is up to outside of this code.
125978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung *
126978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung * <p>There are 4 steps to decode user_data to provide closed caption services. Step 1 and 2 are
127978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung * done in NuPlayer and libstagefright.
128978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung *
129978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung * <h3>Step 1. user_data -&gt; CcPacket</h3>
130978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung *
131978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung * <p>First, user_data consists of cc_data packets, which are 3-byte segments. Here, CcPacket is a
132978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung * collection of cc_data packets in a frame along with same presentation timestamp. Because cc_data
133978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung * packets must be reassembled in the frame display order, CcPackets are reordered.
134978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung *
135978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung * <h3>Step 2. CcPacket -&gt; DTVCC packet</h3>
136978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung *
137978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung * <p>Each cc_data packet has a one byte for declaring a type of itself and data validity, and the
138978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung * subsequent two bytes for input data of a DTVCC packet. There are 4 types for cc_data packet.
139978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung * We're interested in DTVCC_PACKET_START(type 3) and DTVCC_PACKET_DATA(type 2). Each DTVCC packet
140978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung * begins with DTVCC_PACKET_START(type 3) and the following cc_data packets which has
141978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung * DTVCC_PACKET_DATA(type 2) are appended into the DTVCC packet being assembled.
142978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung *
143978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung * <h3>Step 3. DTVCC packet -&gt; Service Blocks</h3>
144978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung *
145978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung * <p>A DTVCC packet consists of multiple service blocks. Each service block represents a caption
146978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung * track and has a service number, which ranges from 1 to 63, that denotes caption track identity.
147978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung * In here, we listen at most one chosen caption track by service number. Otherwise, just skip the
148978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung * other service blocks.
149978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung *
150978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung * <h3>Step 4. Interpreting Service Block Data ({@link #parseServiceBlockData}, {@code parseXX},
151978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung * and {@link #parseExt1} methods)</h3>
152978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung *
153978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung * <p>Service block data is actual caption stream. it looks similar to telnet. It uses most parts of
154978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung * ASCII table and consists of specially defined commands and some ASCII control codes which work
155978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung * in a behavior slightly different from their original purpose. ASCII control codes and caption
156978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung * commands are explicit instructions that control the state of a closed caption service and the
157978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung * other ASCII and text codes are implicit instructions that send their characters to buffer.
158978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung *
159978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung * <p>There are 4 main code groups and 4 extended code groups. Both the range of code groups are the
160978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung * same as the range of a byte.
161978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung *
162978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung * <p>4 main code groups: C0, C1, G0, G1
163978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung * <br>4 extended code groups: C2, C3, G2, G3
164978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung *
165978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung * <p>Each code group has its own handle method. For example, {@link #parseC0} handles C0 code group
166978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung * and so on. And {@link #parseServiceBlockData} method maps a stream on the main code groups while
167978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung * {@link #parseExt1} method maps on the extended code groups.
168978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung *
169978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung * <p>The main code groups:
170978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung * <ul>
171978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung * <li>C0 - contains modified ASCII control codes. It is not intended by CEA-708 but Korea TTA
172978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung *      standard for ATSC CC uses P16 character heavily, which is unclear entity in CEA-708 doc,
173978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung *      even for the alphanumeric characters instead of ASCII characters.</li>
174978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung * <li>C1 - contains the caption commands. There are 3 categories of a caption command.</li>
175978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung * <ul>
176978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung * <li>Window commands: The window commands control a caption window which is addressable area being
177978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung *                  with in the Safe title area. (CWX, CLW, DSW, HDW, TGW, DLW, SWA, DFX)</li>
178978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung * <li>Pen commands: Th pen commands control text style and location. (SPA, SPC, SPL)</li>
179978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung * <li>Job commands: The job commands make a delay and recover from the delay. (DLY, DLC, RST)</li>
180978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung * </ul>
181978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung * <li>G0 - same as printable ASCII character set except music note character.</li>
182978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung * <li>G1 - same as ISO 8859-1 Latin 1 character set.</li>
183978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung * </ul>
184978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung * <p>Most of the extended code groups are being skipped.
185978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung *
186978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung */
187978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chungclass Cea708CCParser {
188978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    private static final String TAG = "Cea708CCParser";
189978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    private static final boolean DEBUG = false;
190978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
191978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    private static final String MUSIC_NOTE_CHAR = new String(
192978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            "\u266B".getBytes(StandardCharsets.UTF_8), StandardCharsets.UTF_8);
193978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
194978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    private final StringBuffer mBuffer = new StringBuffer();
195978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    private int mCommand = 0;
196978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
197978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    // Assign a dummy listener in order to avoid null checks.
198978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    private DisplayListener mListener = new DisplayListener() {
199978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        @Override
200978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public void emitEvent(CaptionEvent event) {
201978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            // do nothing
202978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
203978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    };
204978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
205978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    /**
206978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung     * {@link Cea708Parser} emits caption event of three different types.
207978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung     * {@link DisplayListener#emitEvent} is invoked with the parameter
208978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung     * {@link CaptionEvent} to pass all the results to an observer of the decoding process .
209978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung     *
210978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung     * <p>{@link CaptionEvent#type} determines the type of the result and
211978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung     * {@link CaptionEvent#obj} contains the output value of a caption event.
212978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung     * The observer must do the casting to the corresponding type.
213978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung     *
214978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung     * <ul><li>{@code CAPTION_EMIT_TYPE_BUFFER}: Passes a caption text buffer to a observer.
215978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung     * {@code obj} must be of {@link String}.</li>
216978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung     *
217978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung     * <li>{@code CAPTION_EMIT_TYPE_CONTROL}: Passes a caption character control code to a observer.
218978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung     * {@code obj} must be of {@link Character}.</li>
219978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung     *
220978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung     * <li>{@code CAPTION_EMIT_TYPE_CLEAR_COMMAND}: Passes a clear command to a observer.
221978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung     * {@code obj} must be {@code NULL}.</li></ul>
222978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung     */
223978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    public static final int CAPTION_EMIT_TYPE_BUFFER = 1;
224978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    public static final int CAPTION_EMIT_TYPE_CONTROL = 2;
225978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    public static final int CAPTION_EMIT_TYPE_COMMAND_CWX = 3;
226978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    public static final int CAPTION_EMIT_TYPE_COMMAND_CLW = 4;
227978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    public static final int CAPTION_EMIT_TYPE_COMMAND_DSW = 5;
228978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    public static final int CAPTION_EMIT_TYPE_COMMAND_HDW = 6;
229978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    public static final int CAPTION_EMIT_TYPE_COMMAND_TGW = 7;
230978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    public static final int CAPTION_EMIT_TYPE_COMMAND_DLW = 8;
231978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    public static final int CAPTION_EMIT_TYPE_COMMAND_DLY = 9;
232978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    public static final int CAPTION_EMIT_TYPE_COMMAND_DLC = 10;
233978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    public static final int CAPTION_EMIT_TYPE_COMMAND_RST = 11;
234978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    public static final int CAPTION_EMIT_TYPE_COMMAND_SPA = 12;
235978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    public static final int CAPTION_EMIT_TYPE_COMMAND_SPC = 13;
236978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    public static final int CAPTION_EMIT_TYPE_COMMAND_SPL = 14;
237978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    public static final int CAPTION_EMIT_TYPE_COMMAND_SWA = 15;
238978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    public static final int CAPTION_EMIT_TYPE_COMMAND_DFX = 16;
239978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
240978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    Cea708CCParser(DisplayListener listener) {
241978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        if (listener != null) {
242978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            mListener = listener;
243978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
244978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    }
245978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
246978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    interface DisplayListener {
247978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        void emitEvent(CaptionEvent event);
248978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    }
249978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
250978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    private void emitCaptionEvent(CaptionEvent captionEvent) {
251978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        // Emit the existing string buffer before a new event is arrived.
252978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        emitCaptionBuffer();
253978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        mListener.emitEvent(captionEvent);
254978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    }
255978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
256978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    private void emitCaptionBuffer() {
257978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        if (mBuffer.length() > 0) {
258978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            mListener.emitEvent(new CaptionEvent(CAPTION_EMIT_TYPE_BUFFER, mBuffer.toString()));
259978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            mBuffer.setLength(0);
260978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
261978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    }
262978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
263978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    // Step 3. DTVCC packet -> Service Blocks (parseDtvCcPacket method)
264978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    public void parse(byte[] data) {
265978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        // From this point, starts to read DTVCC coding layer.
266978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        // First, identify code groups, which is defined in CEA-708B Section 7.1.
267978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        int pos = 0;
268978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        while (pos < data.length) {
269978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            pos = parseServiceBlockData(data, pos);
270978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
271978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
272978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        // Emit the buffer after reading codes.
273978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        emitCaptionBuffer();
274978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    }
275978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
276978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    // Step 4. Main code groups
277978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    private int parseServiceBlockData(byte[] data, int pos) {
278978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        // For the details of the ranges of DTVCC code groups, see CEA-708B Table 6.
279978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        mCommand = data[pos] & 0xff;
280978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        ++pos;
281978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        if (mCommand == Const.CODE_C0_EXT1) {
282978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            if (DEBUG) {
283978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                Log.d(TAG, String.format("parseServiceBlockData EXT1 %x", mCommand));
284978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
285978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            pos = parseExt1(data, pos);
286978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        } else if (mCommand >= Const.CODE_C0_RANGE_START
287978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                && mCommand <= Const.CODE_C0_RANGE_END) {
288978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            if (DEBUG) {
289978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                Log.d(TAG, String.format("parseServiceBlockData C0 %x", mCommand));
290978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
291978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            pos = parseC0(data, pos);
292978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        } else if (mCommand >= Const.CODE_C1_RANGE_START
293978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                && mCommand <= Const.CODE_C1_RANGE_END) {
294978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            if (DEBUG) {
295978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                Log.d(TAG, String.format("parseServiceBlockData C1 %x", mCommand));
296978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
297978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            pos = parseC1(data, pos);
298978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        } else if (mCommand >= Const.CODE_G0_RANGE_START
299978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                && mCommand <= Const.CODE_G0_RANGE_END) {
300978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            if (DEBUG) {
301978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                Log.d(TAG, String.format("parseServiceBlockData G0 %x", mCommand));
302978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
303978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            pos = parseG0(data, pos);
304978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        } else if (mCommand >= Const.CODE_G1_RANGE_START
305978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                && mCommand <= Const.CODE_G1_RANGE_END) {
306978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            if (DEBUG) {
307978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                Log.d(TAG, String.format("parseServiceBlockData G1 %x", mCommand));
308978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
309978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            pos = parseG1(data, pos);
310978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
311978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        return pos;
312978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    }
313978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
314978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    private int parseC0(byte[] data, int pos) {
315978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        // For the details of C0 code group, see CEA-708B Section 7.4.1.
316978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        // CL Group: C0 Subset of ASCII Control codes
317978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        if (mCommand >= Const.CODE_C0_SKIP2_RANGE_START
318978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                && mCommand <= Const.CODE_C0_SKIP2_RANGE_END) {
319978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            if (mCommand == Const.CODE_C0_P16) {
320978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                // P16 escapes next two bytes for the large character maps.(no standard rule)
321978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                // For Korea broadcasting, express whole letters by using this.
322978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                try {
323978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    if (data[pos] == 0) {
324978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                        mBuffer.append((char) data[pos + 1]);
325978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    } else {
326978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                        String value = new String(Arrays.copyOfRange(data, pos, pos + 2), "EUC-KR");
327978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                        mBuffer.append(value);
328978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    }
329978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                } catch (UnsupportedEncodingException e) {
330978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    Log.e(TAG, "P16 Code - Could not find supported encoding", e);
331978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                }
332978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
333978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            pos += 2;
334978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        } else if (mCommand >= Const.CODE_C0_SKIP1_RANGE_START
335978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                && mCommand <= Const.CODE_C0_SKIP1_RANGE_END) {
336978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            ++pos;
337978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        } else {
338978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            // NUL, BS, FF, CR interpreted as they are in ASCII control codes.
339978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            // HCR moves the pen location to th beginning of the current line and deletes contents.
340978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            // FF clears the screen and moves the pen location to (0,0).
341978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            // ETX is the NULL command which is used to flush text to the current window when no
342978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            // other command is pending.
343978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            switch (mCommand) {
344978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                case Const.CODE_C0_NUL:
345978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    break;
346978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                case Const.CODE_C0_ETX:
347978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    emitCaptionEvent(new CaptionEvent(CAPTION_EMIT_TYPE_CONTROL, (char) mCommand));
348978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    break;
349978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                case Const.CODE_C0_BS:
350978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    emitCaptionEvent(new CaptionEvent(CAPTION_EMIT_TYPE_CONTROL, (char) mCommand));
351978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    break;
352978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                case Const.CODE_C0_FF:
353978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    emitCaptionEvent(new CaptionEvent(CAPTION_EMIT_TYPE_CONTROL, (char) mCommand));
354978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    break;
355978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                case Const.CODE_C0_CR:
356978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    mBuffer.append('\n');
357978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    break;
358978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                case Const.CODE_C0_HCR:
359978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    emitCaptionEvent(new CaptionEvent(CAPTION_EMIT_TYPE_CONTROL, (char) mCommand));
360978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    break;
361978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                default:
362978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    break;
363978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
364978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
365978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        return pos;
366978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    }
367978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
368978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    private int parseC1(byte[] data, int pos) {
369978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        // For the details of C1 code group, see CEA-708B Section 8.10.
370978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        // CR Group: C1 Caption Control Codes
371978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        switch (mCommand) {
372978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            case Const.CODE_C1_CW0:
373978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            case Const.CODE_C1_CW1:
374978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            case Const.CODE_C1_CW2:
375978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            case Const.CODE_C1_CW3:
376978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            case Const.CODE_C1_CW4:
377978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            case Const.CODE_C1_CW5:
378978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            case Const.CODE_C1_CW6:
379978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            case Const.CODE_C1_CW7: {
380978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                // SetCurrentWindow0-7
381978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                int windowId = mCommand - Const.CODE_C1_CW0;
382978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                emitCaptionEvent(new CaptionEvent(CAPTION_EMIT_TYPE_COMMAND_CWX, windowId));
383978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                if (DEBUG) {
384978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    Log.d(TAG, String.format("CaptionCommand CWX windowId: %d", windowId));
385978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                }
386978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                break;
387978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
388978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
389978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            case Const.CODE_C1_CLW: {
390978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                // ClearWindows
391978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                int windowBitmap = data[pos] & 0xff;
392978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                ++pos;
393978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                emitCaptionEvent(new CaptionEvent(CAPTION_EMIT_TYPE_COMMAND_CLW, windowBitmap));
394978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                if (DEBUG) {
395978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    Log.d(TAG, String.format("CaptionCommand CLW windowBitmap: %d", windowBitmap));
396978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                }
397978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                break;
398978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
399978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
400978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            case Const.CODE_C1_DSW: {
401978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                // DisplayWindows
402978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                int windowBitmap = data[pos] & 0xff;
403978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                ++pos;
404978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                emitCaptionEvent(new CaptionEvent(CAPTION_EMIT_TYPE_COMMAND_DSW, windowBitmap));
405978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                if (DEBUG) {
406978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    Log.d(TAG, String.format("CaptionCommand DSW windowBitmap: %d", windowBitmap));
407978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                }
408978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                break;
409978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
410978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
411978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            case Const.CODE_C1_HDW: {
412978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                // HideWindows
413978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                int windowBitmap = data[pos] & 0xff;
414978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                ++pos;
415978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                emitCaptionEvent(new CaptionEvent(CAPTION_EMIT_TYPE_COMMAND_HDW, windowBitmap));
416978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                if (DEBUG) {
417978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    Log.d(TAG, String.format("CaptionCommand HDW windowBitmap: %d", windowBitmap));
418978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                }
419978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                break;
420978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
421978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
422978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            case Const.CODE_C1_TGW: {
423978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                // ToggleWindows
424978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                int windowBitmap = data[pos] & 0xff;
425978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                ++pos;
426978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                emitCaptionEvent(new CaptionEvent(CAPTION_EMIT_TYPE_COMMAND_TGW, windowBitmap));
427978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                if (DEBUG) {
428978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    Log.d(TAG, String.format("CaptionCommand TGW windowBitmap: %d", windowBitmap));
429978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                }
430978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                break;
431978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
432978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
433978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            case Const.CODE_C1_DLW: {
434978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                // DeleteWindows
435978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                int windowBitmap = data[pos] & 0xff;
436978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                ++pos;
437978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                emitCaptionEvent(new CaptionEvent(CAPTION_EMIT_TYPE_COMMAND_DLW, windowBitmap));
438978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                if (DEBUG) {
439978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    Log.d(TAG, String.format("CaptionCommand DLW windowBitmap: %d", windowBitmap));
440978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                }
441978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                break;
442978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
443978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
444978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            case Const.CODE_C1_DLY: {
445978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                // Delay
446978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                int tenthsOfSeconds = data[pos] & 0xff;
447978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                ++pos;
448978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                emitCaptionEvent(new CaptionEvent(CAPTION_EMIT_TYPE_COMMAND_DLY, tenthsOfSeconds));
449978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                if (DEBUG) {
450978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    Log.d(TAG, String.format("CaptionCommand DLY %d tenths of seconds",
451978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                            tenthsOfSeconds));
452978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                }
453978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                break;
454978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
455978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            case Const.CODE_C1_DLC: {
456978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                // DelayCancel
457978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                emitCaptionEvent(new CaptionEvent(CAPTION_EMIT_TYPE_COMMAND_DLC, null));
458978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                if (DEBUG) {
459978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    Log.d(TAG, "CaptionCommand DLC");
460978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                }
461978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                break;
462978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
463978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
464978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            case Const.CODE_C1_RST: {
465978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                // Reset
466978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                emitCaptionEvent(new CaptionEvent(CAPTION_EMIT_TYPE_COMMAND_RST, null));
467978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                if (DEBUG) {
468978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    Log.d(TAG, "CaptionCommand RST");
469978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                }
470978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                break;
471978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
472978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
473978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            case Const.CODE_C1_SPA: {
474978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                // SetPenAttributes
475978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                int textTag = (data[pos] & 0xf0) >> 4;
476978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                int penSize = data[pos] & 0x03;
477978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                int penOffset = (data[pos] & 0x0c) >> 2;
478978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                boolean italic = (data[pos + 1] & 0x80) != 0;
479978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                boolean underline = (data[pos + 1] & 0x40) != 0;
480978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                int edgeType = (data[pos + 1] & 0x38) >> 3;
481978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                int fontTag = data[pos + 1] & 0x7;
482978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                pos += 2;
483978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                emitCaptionEvent(new CaptionEvent(CAPTION_EMIT_TYPE_COMMAND_SPA,
484978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                        new CaptionPenAttr(penSize, penOffset, textTag, fontTag, edgeType,
485978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                                underline, italic)));
486978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                if (DEBUG) {
487978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    Log.d(TAG, String.format(
488978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                            "CaptionCommand SPA penSize: %d, penOffset: %d, textTag: %d, "
489978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                                    + "fontTag: %d, edgeType: %d, underline: %s, italic: %s",
490978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                            penSize, penOffset, textTag, fontTag, edgeType, underline, italic));
491978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                }
492978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                break;
493978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
494978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
495978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            case Const.CODE_C1_SPC: {
496978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                // SetPenColor
497978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                int opacity = (data[pos] & 0xc0) >> 6;
498978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                int red = (data[pos] & 0x30) >> 4;
499978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                int green = (data[pos] & 0x0c) >> 2;
500978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                int blue = data[pos] & 0x03;
501978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                CaptionColor foregroundColor = new CaptionColor(opacity, red, green, blue);
502978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                ++pos;
503978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                opacity = (data[pos] & 0xc0) >> 6;
504978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                red = (data[pos] & 0x30) >> 4;
505978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                green = (data[pos] & 0x0c) >> 2;
506978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                blue = data[pos] & 0x03;
507978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                CaptionColor backgroundColor = new CaptionColor(opacity, red, green, blue);
508978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                ++pos;
509978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                red = (data[pos] & 0x30) >> 4;
510978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                green = (data[pos] & 0x0c) >> 2;
511978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                blue = data[pos] & 0x03;
512978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                CaptionColor edgeColor = new CaptionColor(
513978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                        CaptionColor.OPACITY_SOLID, red, green, blue);
514978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                ++pos;
515978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                emitCaptionEvent(new CaptionEvent(CAPTION_EMIT_TYPE_COMMAND_SPC,
516978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                        new CaptionPenColor(foregroundColor, backgroundColor, edgeColor)));
517978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                if (DEBUG) {
518978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    Log.d(TAG, String.format(
519978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                            "CaptionCommand SPC foregroundColor %s backgroundColor %s edgeColor %s",
520978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                            foregroundColor, backgroundColor, edgeColor));
521978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                }
522978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                break;
523978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
524978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
525978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            case Const.CODE_C1_SPL: {
526978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                // SetPenLocation
527978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                // column is normally 0-31 for 4:3 formats, and 0-41 for 16:9 formats
528978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                int row = data[pos] & 0x0f;
529978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                int column = data[pos + 1] & 0x3f;
530978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                pos += 2;
531978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                emitCaptionEvent(new CaptionEvent(CAPTION_EMIT_TYPE_COMMAND_SPL,
532978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                        new CaptionPenLocation(row, column)));
533978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                if (DEBUG) {
534978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    Log.d(TAG, String.format("CaptionCommand SPL row: %d, column: %d",
535978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                            row, column));
536978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                }
537978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                break;
538978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
539978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
540978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            case Const.CODE_C1_SWA: {
541978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                // SetWindowAttributes
542978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                int opacity = (data[pos] & 0xc0) >> 6;
543978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                int red = (data[pos] & 0x30) >> 4;
544978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                int green = (data[pos] & 0x0c) >> 2;
545978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                int blue = data[pos] & 0x03;
546978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                CaptionColor fillColor = new CaptionColor(opacity, red, green, blue);
547978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                int borderType = (data[pos + 1] & 0xc0) >> 6 | (data[pos + 2] & 0x80) >> 5;
548978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                red = (data[pos + 1] & 0x30) >> 4;
549978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                green = (data[pos + 1] & 0x0c) >> 2;
550978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                blue = data[pos + 1] & 0x03;
551978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                CaptionColor borderColor = new CaptionColor(
552978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                        CaptionColor.OPACITY_SOLID, red, green, blue);
553978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                boolean wordWrap = (data[pos + 2] & 0x40) != 0;
554978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                int printDirection = (data[pos + 2] & 0x30) >> 4;
555978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                int scrollDirection = (data[pos + 2] & 0x0c) >> 2;
556978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                int justify = (data[pos + 2] & 0x03);
557978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                int effectSpeed = (data[pos + 3] & 0xf0) >> 4;
558978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                int effectDirection = (data[pos + 3] & 0x0c) >> 2;
559978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                int displayEffect = data[pos + 3] & 0x3;
560978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                pos += 4;
561978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                emitCaptionEvent(new CaptionEvent(CAPTION_EMIT_TYPE_COMMAND_SWA,
562978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                        new CaptionWindowAttr(fillColor, borderColor, borderType, wordWrap,
563978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                                printDirection, scrollDirection, justify,
564978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                                effectDirection, effectSpeed, displayEffect)));
565978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                if (DEBUG) {
566978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    Log.d(TAG, String.format(
567978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                            "CaptionCommand SWA fillColor: %s, borderColor: %s, borderType: %d"
568978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                                    + "wordWrap: %s, printDirection: %d, scrollDirection: %d, "
569978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                                    + "justify: %s, effectDirection: %d, effectSpeed: %d, "
570978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                                    + "displayEffect: %d",
571978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                            fillColor, borderColor, borderType, wordWrap, printDirection,
572978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                            scrollDirection, justify, effectDirection, effectSpeed, displayEffect));
573978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                }
574978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                break;
575978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
576978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
577978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            case Const.CODE_C1_DF0:
578978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            case Const.CODE_C1_DF1:
579978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            case Const.CODE_C1_DF2:
580978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            case Const.CODE_C1_DF3:
581978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            case Const.CODE_C1_DF4:
582978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            case Const.CODE_C1_DF5:
583978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            case Const.CODE_C1_DF6:
584978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            case Const.CODE_C1_DF7: {
585978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                // DefineWindow0-7
586978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                int windowId = mCommand - Const.CODE_C1_DF0;
587978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                boolean visible = (data[pos] & 0x20) != 0;
588978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                boolean rowLock = (data[pos] & 0x10) != 0;
589978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                boolean columnLock = (data[pos] & 0x08) != 0;
590978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                int priority = data[pos] & 0x07;
591978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                boolean relativePositioning = (data[pos + 1] & 0x80) != 0;
592978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                int anchorVertical = data[pos + 1] & 0x7f;
593978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                int anchorHorizontal = data[pos + 2] & 0xff;
594978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                int anchorId = (data[pos + 3] & 0xf0) >> 4;
595978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                int rowCount = data[pos + 3] & 0x0f;
596978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                int columnCount = data[pos + 4] & 0x3f;
597978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                int windowStyle = (data[pos + 5] & 0x38) >> 3;
598978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                int penStyle = data[pos + 5] & 0x07;
599978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                pos += 6;
600978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                emitCaptionEvent(new CaptionEvent(CAPTION_EMIT_TYPE_COMMAND_DFX,
601978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                        new CaptionWindow(windowId, visible, rowLock, columnLock, priority,
602978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                                relativePositioning, anchorVertical, anchorHorizontal, anchorId,
603978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                                rowCount, columnCount, penStyle, windowStyle)));
604978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                if (DEBUG) {
605978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    Log.d(TAG, String.format(
606978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                            "CaptionCommand DFx windowId: %d, priority: %d, columnLock: %s, "
607978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                                    + "rowLock: %s, visible: %s, anchorVertical: %d, "
608978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                                    + "relativePositioning: %s, anchorHorizontal: %d, "
609978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                                    + "rowCount: %d, anchorId: %d, columnCount: %d, penStyle: %d, "
610978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                                    + "windowStyle: %d",
611978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                            windowId, priority, columnLock, rowLock, visible, anchorVertical,
612978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                            relativePositioning, anchorHorizontal, rowCount, anchorId, columnCount,
613978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                            penStyle, windowStyle));
614978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                }
615978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                break;
616978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
617978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
618978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            default:
619978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                break;
620978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
621978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        return pos;
622978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    }
623978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
624978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    private int parseG0(byte[] data, int pos) {
625978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        // For the details of G0 code group, see CEA-708B Section 7.4.3.
626978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        // GL Group: G0 Modified version of ANSI X3.4 Printable Character Set (ASCII)
627978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        if (mCommand == Const.CODE_G0_MUSICNOTE) {
628978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            // Music note.
629978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            mBuffer.append(MUSIC_NOTE_CHAR);
630978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        } else {
631978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            // Put ASCII code into buffer.
632978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            mBuffer.append((char) mCommand);
633978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
634978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        return pos;
635978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    }
636978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
637978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    private int parseG1(byte[] data, int pos) {
638978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        // For the details of G0 code group, see CEA-708B Section 7.4.4.
639978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        // GR Group: G1 ISO 8859-1 Latin 1 Characters
640978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        // Put ASCII Extended character set into buffer.
641978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        mBuffer.append((char) mCommand);
642978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        return pos;
643978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    }
644978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
645978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    // Step 4. Extended code groups
646978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    private int parseExt1(byte[] data, int pos) {
647978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        // For the details of EXT1 code group, see CEA-708B Section 7.2.
648978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        mCommand = data[pos] & 0xff;
649978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        ++pos;
650978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        if (mCommand >= Const.CODE_C2_RANGE_START
651978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                && mCommand <= Const.CODE_C2_RANGE_END) {
652978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            pos = parseC2(data, pos);
653978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        } else if (mCommand >= Const.CODE_C3_RANGE_START
654978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                && mCommand <= Const.CODE_C3_RANGE_END) {
655978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            pos = parseC3(data, pos);
656978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        } else if (mCommand >= Const.CODE_G2_RANGE_START
657978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                && mCommand <= Const.CODE_G2_RANGE_END) {
658978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            pos = parseG2(data, pos);
659978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        } else if (mCommand >= Const.CODE_G3_RANGE_START
660978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                && mCommand <= Const.CODE_G3_RANGE_END) {
661978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            pos = parseG3(data ,pos);
662978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
663978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        return pos;
664978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    }
665978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
666978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    private int parseC2(byte[] data, int pos) {
667978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        // For the details of C2 code group, see CEA-708B Section 7.4.7.
668978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        // Extended Miscellaneous Control Codes
669978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        // C2 Table : No commands as of CEA-708B. A decoder must skip.
670978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        if (mCommand >= Const.CODE_C2_SKIP0_RANGE_START
671978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                && mCommand <= Const.CODE_C2_SKIP0_RANGE_END) {
672978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            // Do nothing.
673978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        } else if (mCommand >= Const.CODE_C2_SKIP1_RANGE_START
674978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                && mCommand <= Const.CODE_C2_SKIP1_RANGE_END) {
675978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            ++pos;
676978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        } else if (mCommand >= Const.CODE_C2_SKIP2_RANGE_START
677978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                && mCommand <= Const.CODE_C2_SKIP2_RANGE_END) {
678978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            pos += 2;
679978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        } else if (mCommand >= Const.CODE_C2_SKIP3_RANGE_START
680978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                && mCommand <= Const.CODE_C2_SKIP3_RANGE_END) {
681978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            pos += 3;
682978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
683978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        return pos;
684978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    }
685978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
686978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    private int parseC3(byte[] data, int pos) {
687978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        // For the details of C3 code group, see CEA-708B Section 7.4.8.
688978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        // Extended Control Code Set 2
689978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        // C3 Table : No commands as of CEA-708B. A decoder must skip.
690978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        if (mCommand >= Const.CODE_C3_SKIP4_RANGE_START
691978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                && mCommand <= Const.CODE_C3_SKIP4_RANGE_END) {
692978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            pos += 4;
693978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        } else if (mCommand >= Const.CODE_C3_SKIP5_RANGE_START
694978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                && mCommand <= Const.CODE_C3_SKIP5_RANGE_END) {
695978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            pos += 5;
696978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
697978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        return pos;
698978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    }
699978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
700978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    private int parseG2(byte[] data, int pos) {
701978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        // For the details of C3 code group, see CEA-708B Section 7.4.5.
702978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        // Extended Control Code Set 1(G2 Table)
703978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        switch (mCommand) {
704978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            case Const.CODE_G2_TSP:
705978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                // TODO : TSP is the Transparent space
706978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                break;
707978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            case Const.CODE_G2_NBTSP:
708978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                // TODO : NBTSP is Non-Breaking Transparent Space.
709978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                break;
710978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            case Const.CODE_G2_BLK:
711978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                // TODO : BLK indicates a solid block which fills the entire character block
712978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                // TODO : with a solid foreground color.
713978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                break;
714978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            default:
715978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                break;
716978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
717978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        return pos;
718978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    }
719978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
720978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    private int parseG3(byte[] data, int pos) {
721978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        // For the details of C3 code group, see CEA-708B Section 7.4.6.
722978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        // Future characters and icons(G3 Table)
723978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        if (mCommand == Const.CODE_G3_CC) {
724978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            // TODO : [CC] icon with square corners
725978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
726978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
727978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        // Do nothing
728978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        return pos;
729978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    }
730978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
731978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    /**
732978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung     * @hide
733978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung     *
734978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung     * Collection of CEA-708 structures.
735978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung     */
736978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    private static class Const {
737978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
738978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        private Const() {
739978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
740978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
741978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        // For the details of the ranges of DTVCC code groups, see CEA-708B Table 6.
742978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int CODE_C0_RANGE_START = 0x00;
743978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int CODE_C0_RANGE_END = 0x1f;
744978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int CODE_C1_RANGE_START = 0x80;
745978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int CODE_C1_RANGE_END = 0x9f;
746978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int CODE_G0_RANGE_START = 0x20;
747978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int CODE_G0_RANGE_END = 0x7f;
748978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int CODE_G1_RANGE_START = 0xa0;
749978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int CODE_G1_RANGE_END = 0xff;
750978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int CODE_C2_RANGE_START = 0x00;
751978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int CODE_C2_RANGE_END = 0x1f;
752978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int CODE_C3_RANGE_START = 0x80;
753978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int CODE_C3_RANGE_END = 0x9f;
754978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int CODE_G2_RANGE_START = 0x20;
755978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int CODE_G2_RANGE_END = 0x7f;
756978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int CODE_G3_RANGE_START = 0xa0;
757978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int CODE_G3_RANGE_END = 0xff;
758978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
759978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        // The following ranges are defined in CEA-708B Section 7.4.1.
760978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int CODE_C0_SKIP2_RANGE_START = 0x18;
761978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int CODE_C0_SKIP2_RANGE_END = 0x1f;
762978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int CODE_C0_SKIP1_RANGE_START = 0x10;
763978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int CODE_C0_SKIP1_RANGE_END = 0x17;
764978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
765978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        // The following ranges are defined in CEA-708B Section 7.4.7.
766978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int CODE_C2_SKIP0_RANGE_START = 0x00;
767978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int CODE_C2_SKIP0_RANGE_END = 0x07;
768978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int CODE_C2_SKIP1_RANGE_START = 0x08;
769978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int CODE_C2_SKIP1_RANGE_END = 0x0f;
770978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int CODE_C2_SKIP2_RANGE_START = 0x10;
771978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int CODE_C2_SKIP2_RANGE_END = 0x17;
772978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int CODE_C2_SKIP3_RANGE_START = 0x18;
773978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int CODE_C2_SKIP3_RANGE_END = 0x1f;
774978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
775978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        // The following ranges are defined in CEA-708B Section 7.4.8.
776978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int CODE_C3_SKIP4_RANGE_START = 0x80;
777978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int CODE_C3_SKIP4_RANGE_END = 0x87;
778978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int CODE_C3_SKIP5_RANGE_START = 0x88;
779978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int CODE_C3_SKIP5_RANGE_END = 0x8f;
780978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
781978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        // The following values are the special characters of CEA-708 spec.
782978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int CODE_C0_NUL = 0x00;
783978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int CODE_C0_ETX = 0x03;
784978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int CODE_C0_BS = 0x08;
785978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int CODE_C0_FF = 0x0c;
786978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int CODE_C0_CR = 0x0d;
787978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int CODE_C0_HCR = 0x0e;
788978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int CODE_C0_EXT1 = 0x10;
789978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int CODE_C0_P16 = 0x18;
790978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int CODE_G0_MUSICNOTE = 0x7f;
791978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int CODE_G2_TSP = 0x20;
792978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int CODE_G2_NBTSP = 0x21;
793978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int CODE_G2_BLK = 0x30;
794978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int CODE_G3_CC = 0xa0;
795978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
796978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        // The following values are the command bits of CEA-708 spec.
797978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int CODE_C1_CW0 = 0x80;
798978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int CODE_C1_CW1 = 0x81;
799978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int CODE_C1_CW2 = 0x82;
800978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int CODE_C1_CW3 = 0x83;
801978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int CODE_C1_CW4 = 0x84;
802978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int CODE_C1_CW5 = 0x85;
803978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int CODE_C1_CW6 = 0x86;
804978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int CODE_C1_CW7 = 0x87;
805978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int CODE_C1_CLW = 0x88;
806978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int CODE_C1_DSW = 0x89;
807978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int CODE_C1_HDW = 0x8a;
808978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int CODE_C1_TGW = 0x8b;
809978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int CODE_C1_DLW = 0x8c;
810978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int CODE_C1_DLY = 0x8d;
811978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int CODE_C1_DLC = 0x8e;
812978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int CODE_C1_RST = 0x8f;
813978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int CODE_C1_SPA = 0x90;
814978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int CODE_C1_SPC = 0x91;
815978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int CODE_C1_SPL = 0x92;
816978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int CODE_C1_SWA = 0x97;
817978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int CODE_C1_DF0 = 0x98;
818978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int CODE_C1_DF1 = 0x99;
819978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int CODE_C1_DF2 = 0x9a;
820978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int CODE_C1_DF3 = 0x9b;
821978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int CODE_C1_DF4 = 0x9c;
822978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int CODE_C1_DF5 = 0x9d;
823978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int CODE_C1_DF6 = 0x9e;
824978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int CODE_C1_DF7 = 0x9f;
825978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    }
826978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
827978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    /**
828978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung     * @hide
829978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung     *
830978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung     * CEA-708B-specific color.
831978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung     */
832978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    public static class CaptionColor {
833978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int OPACITY_SOLID = 0;
834978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int OPACITY_FLASH = 1;
835978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int OPACITY_TRANSLUCENT = 2;
836978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int OPACITY_TRANSPARENT = 3;
837978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
838978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        private static final int[] COLOR_MAP = new int[] { 0x00, 0x0f, 0xf0, 0xff };
839978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        private static final int[] OPACITY_MAP = new int[] { 0xff, 0xfe, 0x80, 0x00 };
840978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
841978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public final int opacity;
842978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public final int red;
843978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public final int green;
844978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public final int blue;
845978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
846978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public CaptionColor(int opacity, int red, int green, int blue) {
847978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            this.opacity = opacity;
848978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            this.red = red;
849978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            this.green = green;
850978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            this.blue = blue;
851978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
852978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
853978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public int getArgbValue() {
854978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            return Color.argb(
855978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    OPACITY_MAP[opacity], COLOR_MAP[red], COLOR_MAP[green], COLOR_MAP[blue]);
856978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
857978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    }
858978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
859978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    /**
860978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung     * @hide
861978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung     *
862978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung     * Caption event generated by {@link Cea708CCParser}.
863978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung     */
864978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    public static class CaptionEvent {
865978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public final int type;
866978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public final Object obj;
867978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
868978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public CaptionEvent(int type, Object obj) {
869978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            this.type = type;
870978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            this.obj = obj;
871978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
872978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    }
873978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
874978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    /**
875978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung     * @hide
876978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung     *
877978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung     * Pen style information.
878978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung     */
879978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    public static class CaptionPenAttr {
880978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        // Pen sizes
881978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int PEN_SIZE_SMALL = 0;
882978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int PEN_SIZE_STANDARD = 1;
883978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int PEN_SIZE_LARGE = 2;
884978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
885978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        // Offsets
886978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int OFFSET_SUBSCRIPT = 0;
887978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int OFFSET_NORMAL = 1;
888978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public static final int OFFSET_SUPERSCRIPT = 2;
889978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
890978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public final int penSize;
891978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public final int penOffset;
892978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public final int textTag;
893978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public final int fontTag;
894978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public final int edgeType;
895978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public final boolean underline;
896978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public final boolean italic;
897978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
898978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public CaptionPenAttr(int penSize, int penOffset, int textTag, int fontTag, int edgeType,
899978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                boolean underline, boolean italic) {
900978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            this.penSize = penSize;
901978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            this.penOffset = penOffset;
902978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            this.textTag = textTag;
903978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            this.fontTag = fontTag;
904978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            this.edgeType = edgeType;
905978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            this.underline = underline;
906978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            this.italic = italic;
907978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
908978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    }
909978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
910978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    /**
911978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung     * @hide
912978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung     *
913978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung     * {@link CaptionColor} objects that indicate the foreground, background, and edge color of a
914978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung     * pen.
915978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung     */
916978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    public static class CaptionPenColor {
917978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public final CaptionColor foregroundColor;
918978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public final CaptionColor backgroundColor;
919978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public final CaptionColor edgeColor;
920978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
921978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public CaptionPenColor(CaptionColor foregroundColor, CaptionColor backgroundColor,
922978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                CaptionColor edgeColor) {
923978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            this.foregroundColor = foregroundColor;
924978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            this.backgroundColor = backgroundColor;
925978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            this.edgeColor = edgeColor;
926978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
927978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    }
928978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
929978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    /**
930978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung     * @hide
931978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung     *
932978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung     * Location information of a pen.
933978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung     */
934978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    public static class CaptionPenLocation {
935978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public final int row;
936978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public final int column;
937978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
938978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public CaptionPenLocation(int row, int column) {
939978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            this.row = row;
940978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            this.column = column;
941978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
942978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    }
943978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
944978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    /**
945978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung     * @hide
946978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung     *
947978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung     * Attributes of a caption window, which is defined in CEA-708B.
948978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung     */
949978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    public static class CaptionWindowAttr {
950978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public final CaptionColor fillColor;
951978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public final CaptionColor borderColor;
952978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public final int borderType;
953978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public final boolean wordWrap;
954978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public final int printDirection;
955978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public final int scrollDirection;
956978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public final int justify;
957978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public final int effectDirection;
958978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public final int effectSpeed;
959978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public final int displayEffect;
960978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
961978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public CaptionWindowAttr(CaptionColor fillColor, CaptionColor borderColor, int borderType,
962978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                boolean wordWrap, int printDirection, int scrollDirection, int justify,
963978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                int effectDirection,
964978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                int effectSpeed, int displayEffect) {
965978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            this.fillColor = fillColor;
966978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            this.borderColor = borderColor;
967978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            this.borderType = borderType;
968978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            this.wordWrap = wordWrap;
969978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            this.printDirection = printDirection;
970978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            this.scrollDirection = scrollDirection;
971978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            this.justify = justify;
972978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            this.effectDirection = effectDirection;
973978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            this.effectSpeed = effectSpeed;
974978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            this.displayEffect = displayEffect;
975978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
976978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    }
977978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
978978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    /**
979978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung     * @hide
980978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung     *
981978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung     * Construction information of the caption window of CEA-708B.
982978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung     */
983978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    public static class CaptionWindow {
984978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public final int id;
985978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public final boolean visible;
986978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public final boolean rowLock;
987978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public final boolean columnLock;
988978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public final int priority;
989978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public final boolean relativePositioning;
990978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public final int anchorVertical;
991978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public final int anchorHorizontal;
992978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public final int anchorId;
993978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public final int rowCount;
994978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public final int columnCount;
995978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public final int penStyle;
996978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public final int windowStyle;
997978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
998978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public CaptionWindow(int id, boolean visible,
999978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                boolean rowLock, boolean columnLock, int priority, boolean relativePositioning,
1000978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                int anchorVertical, int anchorHorizontal, int anchorId,
1001978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                int rowCount, int columnCount, int penStyle, int windowStyle) {
1002978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            this.id = id;
1003978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            this.visible = visible;
1004978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            this.rowLock = rowLock;
1005978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            this.columnLock = columnLock;
1006978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            this.priority = priority;
1007978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            this.relativePositioning = relativePositioning;
1008978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            this.anchorVertical = anchorVertical;
1009978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            this.anchorHorizontal = anchorHorizontal;
1010978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            this.anchorId = anchorId;
1011978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            this.rowCount = rowCount;
1012978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            this.columnCount = columnCount;
1013978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            this.penStyle = penStyle;
1014978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            this.windowStyle = windowStyle;
1015978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
1016978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    }
1017978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung}
1018978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1019978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung/**
1020978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung * Widget capable of rendering CEA-708 closed captions.
1021978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung *
1022978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung * @hide
1023978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung */
1024978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chungclass Cea708CCWidget extends ClosedCaptionWidget implements Cea708CCParser.DisplayListener {
1025978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    private final CCHandler mCCHandler;
1026978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1027978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    public Cea708CCWidget(Context context) {
1028978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        this(context, null);
1029978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    }
1030978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1031978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    public Cea708CCWidget(Context context, AttributeSet attrs) {
1032978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        this(context, attrs, 0);
1033978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    }
1034978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1035978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    public Cea708CCWidget(Context context, AttributeSet attrs, int defStyleAttr) {
1036978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        this(context, attrs, defStyleAttr, 0);
1037978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    }
1038978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1039978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    public Cea708CCWidget(Context context, AttributeSet attrs, int defStyleAttr,
1040978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            int defStyleRes) {
1041978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        super(context, attrs, defStyleAttr, defStyleRes);
1042978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1043978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        mCCHandler = new CCHandler((CCLayout) mClosedCaptionLayout);
1044978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    }
1045978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1046978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    @Override
1047978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    public ClosedCaptionLayout createCaptionLayout(Context context) {
1048978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        return new CCLayout(context);
1049978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    }
1050978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1051978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    @Override
1052978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    public void emitEvent(Cea708CCParser.CaptionEvent event) {
1053978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        mCCHandler.processCaptionEvent(event);
1054978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1055978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        setSize(getWidth(), getHeight());
1056978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1057978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        if (mListener != null) {
1058978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            mListener.onChanged(this);
1059978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
1060978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    }
1061978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1062978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    @Override
1063978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    public void onDraw(Canvas canvas) {
1064978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        super.onDraw(canvas);
1065978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        ((ViewGroup) mClosedCaptionLayout).draw(canvas);
1066978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    }
1067978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1068978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    /**
1069978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung     * @hide
1070978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung     *
1071978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung     * A layout that scales its children using the given percentage value.
1072978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung     */
1073978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    static class ScaledLayout extends ViewGroup {
1074978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        private static final String TAG = "ScaledLayout";
1075978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        private static final boolean DEBUG = false;
1076978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        private static final Comparator<Rect> mRectTopLeftSorter = new Comparator<Rect>() {
1077978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            @Override
1078978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            public int compare(Rect lhs, Rect rhs) {
1079978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                if (lhs.top != rhs.top) {
1080978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    return lhs.top - rhs.top;
1081978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                } else {
1082978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    return lhs.left - rhs.left;
1083978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                }
1084978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
1085978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        };
1086978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1087978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        private Rect[] mRectArray;
1088978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1089978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public ScaledLayout(Context context) {
1090978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            super(context);
1091978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
1092978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1093978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        /**
1094978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung         * @hide
1095978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung         *
1096978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung         * ScaledLayoutParams stores the four scale factors.
1097978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung         * <br>
1098978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung         * Vertical coordinate system:   (scaleStartRow * 100) % ~ (scaleEndRow * 100) %
1099978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung         * Horizontal coordinate system: (scaleStartCol * 100) % ~ (scaleEndCol * 100) %
1100978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung         * <br>
1101978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung         * In XML, for example,
1102978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung         * <pre>
1103978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung         * {@code
1104978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung         * <View
1105978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung         *     app:layout_scaleStartRow="0.1"
1106978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung         *     app:layout_scaleEndRow="0.5"
1107978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung         *     app:layout_scaleStartCol="0.4"
1108978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung         *     app:layout_scaleEndCol="1" />
1109978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung         * }
1110978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung         * </pre>
1111978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung         */
1112978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        static class ScaledLayoutParams extends ViewGroup.LayoutParams {
1113978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            public static final float SCALE_UNSPECIFIED = -1;
1114978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            public float scaleStartRow;
1115978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            public float scaleEndRow;
1116978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            public float scaleStartCol;
1117978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            public float scaleEndCol;
1118978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1119978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            public ScaledLayoutParams(float scaleStartRow, float scaleEndRow,
1120978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    float scaleStartCol, float scaleEndCol) {
1121978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                super(MATCH_PARENT, MATCH_PARENT);
1122978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                this.scaleStartRow = scaleStartRow;
1123978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                this.scaleEndRow = scaleEndRow;
1124978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                this.scaleStartCol = scaleStartCol;
1125978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                this.scaleEndCol = scaleEndCol;
1126978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
1127978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1128978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            public ScaledLayoutParams(Context context, AttributeSet attrs) {
1129978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                super(MATCH_PARENT, MATCH_PARENT);
1130978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
1131978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
1132978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1133978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        @Override
1134978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public LayoutParams generateLayoutParams(AttributeSet attrs) {
1135978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            return new ScaledLayoutParams(getContext(), attrs);
1136978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
1137978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1138978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        @Override
1139978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        protected boolean checkLayoutParams(LayoutParams p) {
1140978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            return (p instanceof ScaledLayoutParams);
1141978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
1142978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1143978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        @Override
1144978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
1145978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
1146978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
1147978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            int width = widthSpecSize - getPaddingLeft() - getPaddingRight();
1148978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            int height = heightSpecSize - getPaddingTop() - getPaddingBottom();
1149978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            if (DEBUG) {
1150978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                Log.d(TAG, String.format("onMeasure width: %d, height: %d", width, height));
1151978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
1152978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            int count = getChildCount();
1153978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            mRectArray = new Rect[count];
1154978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            for (int i = 0; i < count; ++i) {
1155978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                View child = getChildAt(i);
1156978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                ViewGroup.LayoutParams params = child.getLayoutParams();
1157978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                float scaleStartRow, scaleEndRow, scaleStartCol, scaleEndCol;
1158978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                if (!(params instanceof ScaledLayoutParams)) {
1159978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    throw new RuntimeException(
1160978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                            "A child of ScaledLayout cannot have the UNSPECIFIED scale factors");
1161978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                }
1162978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                scaleStartRow = ((ScaledLayoutParams) params).scaleStartRow;
1163978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                scaleEndRow = ((ScaledLayoutParams) params).scaleEndRow;
1164978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                scaleStartCol = ((ScaledLayoutParams) params).scaleStartCol;
1165978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                scaleEndCol = ((ScaledLayoutParams) params).scaleEndCol;
1166978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                if (scaleStartRow < 0 || scaleStartRow > 1) {
1167978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    throw new RuntimeException("A child of ScaledLayout should have a range of "
1168978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                            + "scaleStartRow between 0 and 1");
1169978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                }
1170978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                if (scaleEndRow < scaleStartRow || scaleStartRow > 1) {
1171978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    throw new RuntimeException("A child of ScaledLayout should have a range of "
1172978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                            + "scaleEndRow between scaleStartRow and 1");
1173978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                }
1174978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                if (scaleEndCol < 0 || scaleEndCol > 1) {
1175978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    throw new RuntimeException("A child of ScaledLayout should have a range of "
1176978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                            + "scaleStartCol between 0 and 1");
1177978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                }
1178978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                if (scaleEndCol < scaleStartCol || scaleEndCol > 1) {
1179978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    throw new RuntimeException("A child of ScaledLayout should have a range of "
1180978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                            + "scaleEndCol between scaleStartCol and 1");
1181978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                }
1182978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                if (DEBUG) {
1183978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    Log.d(TAG, String.format("onMeasure child scaleStartRow: %f scaleEndRow: %f "
1184978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                                    + "scaleStartCol: %f scaleEndCol: %f",
1185978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                            scaleStartRow, scaleEndRow, scaleStartCol, scaleEndCol));
1186978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                }
1187978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                mRectArray[i] = new Rect((int) (scaleStartCol * width), (int) (scaleStartRow
1188978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                        * height), (int) (scaleEndCol * width), (int) (scaleEndRow * height));
1189978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                int childWidthSpec = MeasureSpec.makeMeasureSpec(
1190978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                        (int) (width * (scaleEndCol - scaleStartCol)), MeasureSpec.EXACTLY);
1191978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                int childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
1192978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                child.measure(childWidthSpec, childHeightSpec);
1193978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1194978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                // If the height of the measured child view is bigger than the height of the
1195978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                // calculated region by the given ScaleLayoutParams, the height of the region should
1196978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                // be increased to fit the size of the child view.
1197978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                if (child.getMeasuredHeight() > mRectArray[i].height()) {
1198978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    int overflowedHeight = child.getMeasuredHeight() - mRectArray[i].height();
1199978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    overflowedHeight = (overflowedHeight + 1) / 2;
1200978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    mRectArray[i].bottom += overflowedHeight;
1201978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    mRectArray[i].top -= overflowedHeight;
1202978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    if (mRectArray[i].top < 0) {
1203978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                        mRectArray[i].bottom -= mRectArray[i].top;
1204978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                        mRectArray[i].top = 0;
1205978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    }
1206978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    if (mRectArray[i].bottom > height) {
1207978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                        mRectArray[i].top -= mRectArray[i].bottom - height;
1208978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                        mRectArray[i].bottom = height;
1209978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    }
1210978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                }
1211978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                childHeightSpec = MeasureSpec.makeMeasureSpec(
1212978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                        (int) (height * (scaleEndRow - scaleStartRow)), MeasureSpec.EXACTLY);
1213978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                child.measure(childWidthSpec, childHeightSpec);
1214978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
1215978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1216978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            // Avoid overlapping rectangles.
1217978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            // Step 1. Sort rectangles by position (top-left).
1218978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            int visibleRectCount = 0;
1219978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            int[] visibleRectGroup = new int[count];
1220978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            Rect[] visibleRectArray = new Rect[count];
1221978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            for (int i = 0; i < count; ++i) {
1222978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                if (getChildAt(i).getVisibility() == View.VISIBLE) {
1223978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    visibleRectGroup[visibleRectCount] = visibleRectCount;
1224978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    visibleRectArray[visibleRectCount] = mRectArray[i];
1225978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    ++visibleRectCount;
1226978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                }
1227978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
1228978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            Arrays.sort(visibleRectArray, 0, visibleRectCount, mRectTopLeftSorter);
1229978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1230978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            // Step 2. Move down if there are overlapping rectangles.
1231978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            for (int i = 0; i < visibleRectCount - 1; ++i) {
1232978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                for (int j = i + 1; j < visibleRectCount; ++j) {
1233978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    if (Rect.intersects(visibleRectArray[i], visibleRectArray[j])) {
1234978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                        visibleRectGroup[j] = visibleRectGroup[i];
1235978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                        visibleRectArray[j].set(visibleRectArray[j].left,
1236978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                                visibleRectArray[i].bottom,
1237978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                                visibleRectArray[j].right,
1238978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                                visibleRectArray[i].bottom + visibleRectArray[j].height());
1239978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    }
1240978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                }
1241978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
1242978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1243978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            // Step 3. Move up if there is any overflowed rectangle.
1244978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            for (int i = visibleRectCount - 1; i >= 0; --i) {
1245978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                if (visibleRectArray[i].bottom > height) {
1246978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    int overflowedHeight = visibleRectArray[i].bottom - height;
1247978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    for (int j = 0; j <= i; ++j) {
1248978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                        if (visibleRectGroup[i] == visibleRectGroup[j]) {
1249978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                            visibleRectArray[j].set(visibleRectArray[j].left,
1250978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                                    visibleRectArray[j].top - overflowedHeight,
1251978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                                    visibleRectArray[j].right,
1252978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                                    visibleRectArray[j].bottom - overflowedHeight);
1253978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                        }
1254978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    }
1255978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                }
1256978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
1257978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            setMeasuredDimension(widthSpecSize, heightSpecSize);
1258978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
1259978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1260978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        @Override
1261978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        protected void onLayout(boolean changed, int l, int t, int r, int b) {
1262978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            int paddingLeft = getPaddingLeft();
1263978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            int paddingTop = getPaddingTop();
1264978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            int count = getChildCount();
1265978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            for (int i = 0; i < count; ++i) {
1266978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                View child = getChildAt(i);
1267978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                if (child.getVisibility() != GONE) {
1268978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    int childLeft = paddingLeft + mRectArray[i].left;
1269978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    int childTop = paddingTop + mRectArray[i].top;
1270978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    int childBottom = paddingLeft + mRectArray[i].bottom;
1271978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    int childRight = paddingTop + mRectArray[i].right;
1272978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    if (DEBUG) {
1273978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                        Log.d(TAG, String.format(
1274978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                                "child layout bottom: %d left: %d right: %d top: %d",
1275978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                                childBottom, childLeft, childRight, childTop));
1276978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    }
1277978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    child.layout(childLeft, childTop, childRight, childBottom);
1278978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                }
1279978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
1280978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
1281978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1282978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        @Override
1283978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public void dispatchDraw(Canvas canvas) {
1284978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            int paddingLeft = getPaddingLeft();
1285978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            int paddingTop = getPaddingTop();
1286978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            int count = getChildCount();
1287978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            for (int i = 0; i < count; ++i) {
1288978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                View child = getChildAt(i);
1289978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                if (child.getVisibility() != GONE) {
1290978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    if (i >= mRectArray.length) {
1291978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                        break;
1292978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    }
1293978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    int childLeft = paddingLeft + mRectArray[i].left;
1294978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    int childTop = paddingTop + mRectArray[i].top;
1295978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    final int saveCount = canvas.save();
1296978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    canvas.translate(childLeft, childTop);
1297978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    child.draw(canvas);
1298978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    canvas.restoreToCount(saveCount);
1299978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                }
1300978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
1301978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
1302978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    }
1303978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1304978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    /**
1305978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung     * @hide
1306978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung     *
1307978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung     * Layout containing the safe title area that helps the closed captions look more prominent.
1308978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung     *
1309978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung     * <p>This is required by CEA-708B.
1310978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung     */
1311978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    static class CCLayout extends ScaledLayout implements ClosedCaptionLayout {
1312978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        private static final float SAFE_TITLE_AREA_SCALE_START_X = 0.1f;
1313978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        private static final float SAFE_TITLE_AREA_SCALE_END_X = 0.9f;
1314978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        private static final float SAFE_TITLE_AREA_SCALE_START_Y = 0.1f;
1315978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        private static final float SAFE_TITLE_AREA_SCALE_END_Y = 0.9f;
1316978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1317978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        private final ScaledLayout mSafeTitleAreaLayout;
1318978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1319978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public CCLayout(Context context) {
1320978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            super(context);
1321978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1322978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            mSafeTitleAreaLayout = new ScaledLayout(context);
1323978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            addView(mSafeTitleAreaLayout, new ScaledLayout.ScaledLayoutParams(
1324978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    SAFE_TITLE_AREA_SCALE_START_X, SAFE_TITLE_AREA_SCALE_END_X,
1325978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    SAFE_TITLE_AREA_SCALE_START_Y, SAFE_TITLE_AREA_SCALE_END_Y));
1326978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
1327978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1328978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public void addOrUpdateViewToSafeTitleArea(CCWindowLayout captionWindowLayout,
1329978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                ScaledLayoutParams scaledLayoutParams) {
1330978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            int index = mSafeTitleAreaLayout.indexOfChild(captionWindowLayout);
1331978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            if (index < 0) {
1332978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                mSafeTitleAreaLayout.addView(captionWindowLayout, scaledLayoutParams);
1333978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                return;
1334978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
1335978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            mSafeTitleAreaLayout.updateViewLayout(captionWindowLayout, scaledLayoutParams);
1336978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
1337978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1338978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public void removeViewFromSafeTitleArea(CCWindowLayout captionWindowLayout) {
1339978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            mSafeTitleAreaLayout.removeView(captionWindowLayout);
1340978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
1341978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1342978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public void setCaptionStyle(CaptionStyle style) {
1343978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            final int count = mSafeTitleAreaLayout.getChildCount();
1344978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            for (int i = 0; i < count; ++i) {
1345978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                final CCWindowLayout windowLayout =
1346978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                        (CCWindowLayout) mSafeTitleAreaLayout.getChildAt(i);
1347978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                windowLayout.setCaptionStyle(style);
1348978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
1349978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
1350978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1351978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public void setFontScale(float fontScale) {
1352978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            final int count = mSafeTitleAreaLayout.getChildCount();
1353978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            for (int i = 0; i < count; ++i) {
1354978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                final CCWindowLayout windowLayout =
1355978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                        (CCWindowLayout) mSafeTitleAreaLayout.getChildAt(i);
1356978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                windowLayout.setFontScale(fontScale);
1357978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
1358978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
1359978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    }
1360978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1361978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    /**
1362978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung     * @hide
1363978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung     *
1364978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung     * Renders the selected CC track.
1365978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung     */
1366978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    static class CCHandler implements Handler.Callback {
1367978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        // TODO: Remaining works
1368978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        // CaptionTrackRenderer does not support the full spec of CEA-708. The remaining works are
1369978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        // described in the follows.
1370978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        // C0 Table: Backspace, FF, and HCR are not supported. The rule for P16 is not standardized
1371978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        //           but it is handled as EUC-KR charset for Korea broadcasting.
1372978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        // C1 Table: All the styles of windows and pens except underline, italic, pen size, and pen
1373978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        //           offset specified in CEA-708 are ignored and this follows system wide CC
1374978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        //           preferences for look and feel. SetPenLocation is not implemented.
1375978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        // G2 Table: TSP, NBTSP and BLK are not supported.
1376978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        // Text/commands: Word wrapping, fonts, row and column locking are not supported.
1377978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1378978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        private static final String TAG = "CCHandler";
1379978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        private static final boolean DEBUG = false;
1380978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1381978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        private static final int TENTHS_OF_SECOND_IN_MILLIS = 100;
1382978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1383978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        // According to CEA-708B, there can exist up to 8 caption windows.
1384978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        private static final int CAPTION_WINDOWS_MAX = 8;
1385978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        private static final int CAPTION_ALL_WINDOWS_BITMAP = 255;
1386978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1387978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        private static final int MSG_DELAY_CANCEL = 1;
1388978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        private static final int MSG_CAPTION_CLEAR = 2;
1389978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1390978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        private static final long CAPTION_CLEAR_INTERVAL_MS = 60000;
1391978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1392978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        private final CCLayout mCCLayout;
1393978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        private boolean mIsDelayed = false;
1394978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        private CCWindowLayout mCurrentWindowLayout;
1395978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        private final CCWindowLayout[] mCaptionWindowLayouts =
1396978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                new CCWindowLayout[CAPTION_WINDOWS_MAX];
1397978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        private final ArrayList<Cea708CCParser.CaptionEvent> mPendingCaptionEvents
1398978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                = new ArrayList<>();
1399978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        private final Handler mHandler;
1400978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1401978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public CCHandler(CCLayout ccLayout) {
1402978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            mCCLayout = ccLayout;
1403978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            mHandler = new Handler(this);
1404978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
1405978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1406978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        @Override
1407978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public boolean handleMessage(Message msg) {
1408978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            switch (msg.what) {
1409978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                case MSG_DELAY_CANCEL:
1410978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    delayCancel();
1411978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    return true;
1412978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                case MSG_CAPTION_CLEAR:
1413978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    clearWindows(CAPTION_ALL_WINDOWS_BITMAP);
1414978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    return true;
1415978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
1416978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            return false;
1417978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
1418978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1419978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public void processCaptionEvent(Cea708CCParser.CaptionEvent event) {
1420978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            if (mIsDelayed) {
1421978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                mPendingCaptionEvents.add(event);
1422978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                return;
1423978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
1424978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            switch (event.type) {
1425978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                case Cea708CCParser.CAPTION_EMIT_TYPE_BUFFER:
1426978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    sendBufferToCurrentWindow((String) event.obj);
1427978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    break;
1428978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                case Cea708CCParser.CAPTION_EMIT_TYPE_CONTROL:
1429978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    sendControlToCurrentWindow((char) event.obj);
1430978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    break;
1431978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                case Cea708CCParser.CAPTION_EMIT_TYPE_COMMAND_CWX:
1432978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    setCurrentWindowLayout((int) event.obj);
1433978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    break;
1434978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                case Cea708CCParser.CAPTION_EMIT_TYPE_COMMAND_CLW:
1435978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    clearWindows((int) event.obj);
1436978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    break;
1437978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                case Cea708CCParser.CAPTION_EMIT_TYPE_COMMAND_DSW:
1438978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    displayWindows((int) event.obj);
1439978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    break;
1440978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                case Cea708CCParser.CAPTION_EMIT_TYPE_COMMAND_HDW:
1441978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    hideWindows((int) event.obj);
1442978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    break;
1443978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                case Cea708CCParser.CAPTION_EMIT_TYPE_COMMAND_TGW:
1444978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    toggleWindows((int) event.obj);
1445978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    break;
1446978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                case Cea708CCParser.CAPTION_EMIT_TYPE_COMMAND_DLW:
1447978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    deleteWindows((int) event.obj);
1448978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    break;
1449978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                case Cea708CCParser.CAPTION_EMIT_TYPE_COMMAND_DLY:
1450978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    delay((int) event.obj);
1451978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    break;
1452978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                case Cea708CCParser.CAPTION_EMIT_TYPE_COMMAND_DLC:
1453978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    delayCancel();
1454978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    break;
1455978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                case Cea708CCParser.CAPTION_EMIT_TYPE_COMMAND_RST:
1456978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    reset();
1457978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    break;
1458978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                case Cea708CCParser.CAPTION_EMIT_TYPE_COMMAND_SPA:
1459978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    setPenAttr((Cea708CCParser.CaptionPenAttr) event.obj);
1460978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    break;
1461978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                case Cea708CCParser.CAPTION_EMIT_TYPE_COMMAND_SPC:
1462978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    setPenColor((Cea708CCParser.CaptionPenColor) event.obj);
1463978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    break;
1464978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                case Cea708CCParser.CAPTION_EMIT_TYPE_COMMAND_SPL:
1465978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    setPenLocation((Cea708CCParser.CaptionPenLocation) event.obj);
1466978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    break;
1467978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                case Cea708CCParser.CAPTION_EMIT_TYPE_COMMAND_SWA:
1468978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    setWindowAttr((Cea708CCParser.CaptionWindowAttr) event.obj);
1469978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    break;
1470978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                case Cea708CCParser.CAPTION_EMIT_TYPE_COMMAND_DFX:
1471978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    defineWindow((Cea708CCParser.CaptionWindow) event.obj);
1472978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    break;
1473978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
1474978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
1475978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1476978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        // The window related caption commands
1477978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        private void setCurrentWindowLayout(int windowId) {
1478978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            if (windowId < 0 || windowId >= mCaptionWindowLayouts.length) {
1479978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                return;
1480978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
1481978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            CCWindowLayout windowLayout = mCaptionWindowLayouts[windowId];
1482978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            if (windowLayout == null) {
1483978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                return;
1484978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
1485978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            if (DEBUG) {
1486978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                Log.d(TAG, "setCurrentWindowLayout to " + windowId);
1487978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
1488978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            mCurrentWindowLayout = windowLayout;
1489978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
1490978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1491978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        // Each bit of windowBitmap indicates a window.
1492978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        // If a bit is set, the window id is the same as the number of the trailing zeros of the
1493978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        // bit.
1494978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        private ArrayList<CCWindowLayout> getWindowsFromBitmap(int windowBitmap) {
1495978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            ArrayList<CCWindowLayout> windows = new ArrayList<>();
1496978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            for (int i = 0; i < CAPTION_WINDOWS_MAX; ++i) {
1497978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                if ((windowBitmap & (1 << i)) != 0) {
1498978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    CCWindowLayout windowLayout = mCaptionWindowLayouts[i];
1499978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    if (windowLayout != null) {
1500978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                        windows.add(windowLayout);
1501978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    }
1502978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                }
1503978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
1504978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            return windows;
1505978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
1506978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1507978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        private void clearWindows(int windowBitmap) {
1508978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            if (windowBitmap == 0) {
1509978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                return;
1510978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
1511978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            for (CCWindowLayout windowLayout : getWindowsFromBitmap(windowBitmap)) {
1512978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                windowLayout.clear();
1513978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
1514978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
1515978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1516978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        private void displayWindows(int windowBitmap) {
1517978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            if (windowBitmap == 0) {
1518978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                return;
1519978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
1520978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            for (CCWindowLayout windowLayout : getWindowsFromBitmap(windowBitmap)) {
1521978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                windowLayout.show();
1522978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
1523978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
1524978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1525978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        private void hideWindows(int windowBitmap) {
1526978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            if (windowBitmap == 0) {
1527978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                return;
1528978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
1529978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            for (CCWindowLayout windowLayout : getWindowsFromBitmap(windowBitmap)) {
1530978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                windowLayout.hide();
1531978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
1532978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
1533978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1534978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        private void toggleWindows(int windowBitmap) {
1535978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            if (windowBitmap == 0) {
1536978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                return;
1537978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
1538978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            for (CCWindowLayout windowLayout : getWindowsFromBitmap(windowBitmap)) {
1539978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                if (windowLayout.isShown()) {
1540978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    windowLayout.hide();
1541978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                } else {
1542978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    windowLayout.show();
1543978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                }
1544978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
1545978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
1546978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1547978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        private void deleteWindows(int windowBitmap) {
1548978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            if (windowBitmap == 0) {
1549978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                return;
1550978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
1551978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            for (CCWindowLayout windowLayout : getWindowsFromBitmap(windowBitmap)) {
1552978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                windowLayout.removeFromCaptionView();
1553978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                mCaptionWindowLayouts[windowLayout.getCaptionWindowId()] = null;
1554978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
1555978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
1556978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1557978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public void reset() {
1558978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            mCurrentWindowLayout = null;
1559978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            mIsDelayed = false;
1560978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            mPendingCaptionEvents.clear();
1561978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            for (int i = 0; i < CAPTION_WINDOWS_MAX; ++i) {
1562978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                if (mCaptionWindowLayouts[i] != null) {
1563978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    mCaptionWindowLayouts[i].removeFromCaptionView();
1564978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                }
1565978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                mCaptionWindowLayouts[i] = null;
1566978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
1567978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            mCCLayout.setVisibility(View.INVISIBLE);
1568978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            mHandler.removeMessages(MSG_CAPTION_CLEAR);
1569978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
1570978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1571978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        private void setWindowAttr(Cea708CCParser.CaptionWindowAttr windowAttr) {
1572978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            if (mCurrentWindowLayout != null) {
1573978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                mCurrentWindowLayout.setWindowAttr(windowAttr);
1574978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
1575978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
1576978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1577978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        private void defineWindow(Cea708CCParser.CaptionWindow window) {
1578978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            if (window == null) {
1579978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                return;
1580978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
1581978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            int windowId = window.id;
1582978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            if (windowId < 0 || windowId >= mCaptionWindowLayouts.length) {
1583978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                return;
1584978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
1585978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            CCWindowLayout windowLayout = mCaptionWindowLayouts[windowId];
1586978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            if (windowLayout == null) {
1587978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                windowLayout = new CCWindowLayout(mCCLayout.getContext());
1588978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
1589978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            windowLayout.initWindow(mCCLayout, window);
1590978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            mCurrentWindowLayout = mCaptionWindowLayouts[windowId] = windowLayout;
1591978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
1592978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1593978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        // The job related caption commands
1594978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        private void delay(int tenthsOfSeconds) {
1595978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            if (tenthsOfSeconds < 0 || tenthsOfSeconds > 255) {
1596978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                return;
1597978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
1598978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            mIsDelayed = true;
1599978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_DELAY_CANCEL),
1600978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    tenthsOfSeconds * TENTHS_OF_SECOND_IN_MILLIS);
1601978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
1602978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1603978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        private void delayCancel() {
1604978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            mIsDelayed = false;
1605978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            processPendingBuffer();
1606978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
1607978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1608978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        private void processPendingBuffer() {
1609978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            for (Cea708CCParser.CaptionEvent event : mPendingCaptionEvents) {
1610978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                processCaptionEvent(event);
1611978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
1612978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            mPendingCaptionEvents.clear();
1613978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
1614978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1615978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        // The implicit write caption commands
1616978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        private void sendControlToCurrentWindow(char control) {
1617978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            if (mCurrentWindowLayout != null) {
1618978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                mCurrentWindowLayout.sendControl(control);
1619978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
1620978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
1621978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1622978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        private void sendBufferToCurrentWindow(String buffer) {
1623978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            if (mCurrentWindowLayout != null) {
1624978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                mCurrentWindowLayout.sendBuffer(buffer);
1625978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                mHandler.removeMessages(MSG_CAPTION_CLEAR);
1626978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_CAPTION_CLEAR),
1627978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                        CAPTION_CLEAR_INTERVAL_MS);
1628978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
1629978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
1630978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1631978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        // The pen related caption commands
1632978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        private void setPenAttr(Cea708CCParser.CaptionPenAttr attr) {
1633978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            if (mCurrentWindowLayout != null) {
1634978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                mCurrentWindowLayout.setPenAttr(attr);
1635978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
1636978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
1637978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1638978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        private void setPenColor(Cea708CCParser.CaptionPenColor color) {
1639978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            if (mCurrentWindowLayout != null) {
1640978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                mCurrentWindowLayout.setPenColor(color);
1641978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
1642978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
1643978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1644978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        private void setPenLocation(Cea708CCParser.CaptionPenLocation location) {
1645978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            if (mCurrentWindowLayout != null) {
1646978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                mCurrentWindowLayout.setPenLocation(location.row, location.column);
1647978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
1648978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
1649978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    }
1650978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1651978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    /**
1652978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung     * @hide
1653978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung     *
1654978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung     * Layout which renders a caption window of CEA-708B. It contains a {@link TextView} that takes
1655978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung     * care of displaying the actual CC text.
1656978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung     */
1657978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    static class CCWindowLayout extends RelativeLayout implements View.OnLayoutChangeListener {
1658978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        private static final String TAG = "CCWindowLayout";
1659978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1660978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        private static final float PROPORTION_PEN_SIZE_SMALL = .75f;
1661978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        private static final float PROPORTION_PEN_SIZE_LARGE = 1.25f;
1662978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1663978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        // The following values indicates the maximum cell number of a window.
1664978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        private static final int ANCHOR_RELATIVE_POSITIONING_MAX = 99;
1665978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        private static final int ANCHOR_VERTICAL_MAX = 74;
1666978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        private static final int ANCHOR_HORIZONTAL_16_9_MAX = 209;
1667978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        private static final int MAX_COLUMN_COUNT_16_9 = 42;
1668978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1669978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        // The following values indicates a gravity of a window.
1670978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        private static final int ANCHOR_MODE_DIVIDER = 3;
1671978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        private static final int ANCHOR_HORIZONTAL_MODE_LEFT = 0;
1672978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        private static final int ANCHOR_HORIZONTAL_MODE_CENTER = 1;
1673978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        private static final int ANCHOR_HORIZONTAL_MODE_RIGHT = 2;
1674978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        private static final int ANCHOR_VERTICAL_MODE_TOP = 0;
1675978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        private static final int ANCHOR_VERTICAL_MODE_CENTER = 1;
1676978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        private static final int ANCHOR_VERTICAL_MODE_BOTTOM = 2;
1677978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1678978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        private CCLayout mCCLayout;
1679978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1680978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        private CCView mCCView;
1681978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        private CaptionStyle mCaptionStyle;
1682978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        private int mRowLimit = 0;
1683978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        private final SpannableStringBuilder mBuilder = new SpannableStringBuilder();
1684978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        private final List<CharacterStyle> mCharacterStyles = new ArrayList<>();
1685978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        private int mCaptionWindowId;
1686978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        private int mRow = -1;
1687978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        private float mFontScale;
1688978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        private float mTextSize;
1689978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        private String mWidestChar;
1690978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        private int mLastCaptionLayoutWidth;
1691978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        private int mLastCaptionLayoutHeight;
1692978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1693978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public CCWindowLayout(Context context) {
1694978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            this(context, null);
1695978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
1696978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1697978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public CCWindowLayout(Context context, AttributeSet attrs) {
1698978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            this(context, attrs, 0);
1699978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
1700978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1701978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public CCWindowLayout(Context context, AttributeSet attrs, int defStyleAttr) {
1702978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            this(context, attrs, defStyleAttr, 0);
1703978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
1704978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1705978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public CCWindowLayout(Context context, AttributeSet attrs, int defStyleAttr,
1706978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                int defStyleRes) {
1707978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            super(context, attrs, defStyleAttr, defStyleRes);
1708978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1709978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            // Add a subtitle view to the layout.
1710978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            mCCView = new CCView(context);
1711978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            LayoutParams params = new RelativeLayout.LayoutParams(
1712978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
1713978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            addView(mCCView, params);
1714978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1715978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            // Set the system wide CC preferences to the subtitle view.
1716978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            CaptioningManager captioningManager =
1717978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    (CaptioningManager) context.getSystemService(Context.CAPTIONING_SERVICE);
1718978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            mFontScale = captioningManager.getFontScale();
1719978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            setCaptionStyle(captioningManager.getUserStyle());
1720978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            mCCView.setText("");
1721978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            updateWidestChar();
1722978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
1723978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1724978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public void setCaptionStyle(CaptionStyle style) {
1725978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            mCaptionStyle = style;
1726978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            mCCView.setCaptionStyle(style);
1727978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
1728978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1729978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public void setFontScale(float fontScale) {
1730978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            mFontScale = fontScale;
1731978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            updateTextSize();
1732978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
1733978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1734978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public int getCaptionWindowId() {
1735978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            return mCaptionWindowId;
1736978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
1737978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1738978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public void setCaptionWindowId(int captionWindowId) {
1739978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            mCaptionWindowId = captionWindowId;
1740978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
1741978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1742978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public void clear() {
1743978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            clearText();
1744978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            hide();
1745978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
1746978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1747978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public void show() {
1748978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            setVisibility(View.VISIBLE);
1749978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            requestLayout();
1750978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
1751978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1752978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public void hide() {
1753978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            setVisibility(View.INVISIBLE);
1754978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            requestLayout();
1755978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
1756978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1757978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public void setPenAttr(Cea708CCParser.CaptionPenAttr penAttr) {
1758978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            mCharacterStyles.clear();
1759978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            if (penAttr.italic) {
1760978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                mCharacterStyles.add(new StyleSpan(Typeface.ITALIC));
1761978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
1762978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            if (penAttr.underline) {
1763978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                mCharacterStyles.add(new UnderlineSpan());
1764978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
1765978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            switch (penAttr.penSize) {
1766978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                case Cea708CCParser.CaptionPenAttr.PEN_SIZE_SMALL:
1767978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    mCharacterStyles.add(new RelativeSizeSpan(PROPORTION_PEN_SIZE_SMALL));
1768978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    break;
1769978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                case Cea708CCParser.CaptionPenAttr.PEN_SIZE_LARGE:
1770978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    mCharacterStyles.add(new RelativeSizeSpan(PROPORTION_PEN_SIZE_LARGE));
1771978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    break;
1772978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
1773978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            switch (penAttr.penOffset) {
1774978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                case Cea708CCParser.CaptionPenAttr.OFFSET_SUBSCRIPT:
1775978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    mCharacterStyles.add(new SubscriptSpan());
1776978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    break;
1777978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                case Cea708CCParser.CaptionPenAttr.OFFSET_SUPERSCRIPT:
1778978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    mCharacterStyles.add(new SuperscriptSpan());
1779978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    break;
1780978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
1781978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
1782978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1783978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public void setPenColor(Cea708CCParser.CaptionPenColor penColor) {
1784978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            // TODO: apply pen colors or skip this and use the style of system wide CC style as is.
1785978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
1786978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1787978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public void setPenLocation(int row, int column) {
1788978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            // TODO: change the location of pen based on row and column both.
1789978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            if (mRow >= 0) {
1790978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                for (int r = mRow; r < row; ++r) {
1791978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    appendText("\n");
1792978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                }
1793978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
1794978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            mRow = row;
1795978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
1796978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1797978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public void setWindowAttr(Cea708CCParser.CaptionWindowAttr windowAttr) {
1798978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            // TODO: apply window attrs or skip this and use the style of system wide CC style as
1799978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            // is.
1800978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
1801978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1802978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public void sendBuffer(String buffer) {
1803978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            appendText(buffer);
1804978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
1805978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1806978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public void sendControl(char control) {
1807978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            // TODO: there are a bunch of ASCII-style control codes.
1808978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
1809978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1810978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        /**
1811978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung         * This method places the window on a given CaptionLayout along with the anchor of the
1812978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung         * window.
1813978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung         * <p>
1814978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung         * According to CEA-708B, the anchor id indicates the gravity of the window as the follows.
1815978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung         * For example, A value 7 of a anchor id says that a window is align with its parent bottom
1816978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung         * and is located at the center horizontally of its parent.
1817978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung         * </p>
1818978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung         * <h4>Anchor id and the gravity of a window</h4>
1819978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung         * <table>
1820978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung         *     <tr>
1821978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung         *         <th>GRAVITY</th>
1822978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung         *         <th>LEFT</th>
1823978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung         *         <th>CENTER_HORIZONTAL</th>
1824978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung         *         <th>RIGHT</th>
1825978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung         *     </tr>
1826978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung         *     <tr>
1827978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung         *         <th>TOP</th>
1828978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung         *         <td>0</td>
1829978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung         *         <td>1</td>
1830978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung         *         <td>2</td>
1831978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung         *     </tr>
1832978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung         *     <tr>
1833978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung         *         <th>CENTER_VERTICAL</th>
1834978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung         *         <td>3</td>
1835978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung         *         <td>4</td>
1836978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung         *         <td>5</td>
1837978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung         *     </tr>
1838978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung         *     <tr>
1839978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung         *         <th>BOTTOM</th>
1840978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung         *         <td>6</td>
1841978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung         *         <td>7</td>
1842978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung         *         <td>8</td>
1843978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung         *     </tr>
1844978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung         * </table>
1845978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung         * <p>
1846978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung         * In order to handle the gravity of a window, there are two steps. First, set the size of
1847978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung         * the window. Since the window will be positioned at ScaledLayout, the size factors are
1848978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung         * determined in a ratio. Second, set the gravity of the window. CaptionWindowLayout is
1849978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung         * inherited from RelativeLayout. Hence, we could set the gravity of its child view,
1850978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung         * SubtitleView.
1851978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung         * </p>
1852978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung         * <p>
1853978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung         * The gravity of the window is also related to its size. When it should be pushed to a one
1854978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung         * of the end of the window, like LEFT, RIGHT, TOP or BOTTOM, the anchor point should be a
1855978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung         * boundary of the window. When it should be pushed in the horizontal/vertical center of its
1856978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung         * container, the horizontal/vertical center point of the window should be the same as the
1857978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung         * anchor point.
1858978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung         * </p>
1859978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung         *
1860978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung         * @param ccLayout a given CaptionLayout, which contains a safe title area.
1861978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung         * @param captionWindow a given CaptionWindow, which stores the construction info of the
1862978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung         *                      window.
1863978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung         */
1864978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public void initWindow(CCLayout ccLayout, Cea708CCParser.CaptionWindow captionWindow) {
1865978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            if (mCCLayout != ccLayout) {
1866978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                if (mCCLayout != null) {
1867978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    mCCLayout.removeOnLayoutChangeListener(this);
1868978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                }
1869978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                mCCLayout = ccLayout;
1870978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                mCCLayout.addOnLayoutChangeListener(this);
1871978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                updateWidestChar();
1872978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
1873978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1874978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            // Both anchor vertical and horizontal indicates the position cell number of the window.
1875978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            float scaleRow = (float) captionWindow.anchorVertical /
1876978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    (captionWindow.relativePositioning
1877978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                            ? ANCHOR_RELATIVE_POSITIONING_MAX : ANCHOR_VERTICAL_MAX);
1878978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1879978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            // Assumes it has a wide aspect ratio track.
1880978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            float scaleCol = (float) captionWindow.anchorHorizontal /
1881978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    (captionWindow.relativePositioning ? ANCHOR_RELATIVE_POSITIONING_MAX
1882978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                            : ANCHOR_HORIZONTAL_16_9_MAX);
1883978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1884978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            // The range of scaleRow/Col need to be verified to be in [0, 1].
1885978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            // Otherwise a RuntimeException will be raised in ScaledLayout.
1886978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            if (scaleRow < 0 || scaleRow > 1) {
1887978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                Log.i(TAG, "The vertical position of the anchor point should be at the range of 0 "
1888978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                        + "and 1 but " + scaleRow);
1889978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                scaleRow = Math.max(0, Math.min(scaleRow, 1));
1890978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
1891978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            if (scaleCol < 0 || scaleCol > 1) {
1892978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                Log.i(TAG, "The horizontal position of the anchor point should be at the range of 0"
1893978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                        + " and 1 but " + scaleCol);
1894978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                scaleCol = Math.max(0, Math.min(scaleCol, 1));
1895978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
1896978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            int gravity = Gravity.CENTER;
1897978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            int horizontalMode = captionWindow.anchorId % ANCHOR_MODE_DIVIDER;
1898978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            int verticalMode = captionWindow.anchorId / ANCHOR_MODE_DIVIDER;
1899978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            float scaleStartRow = 0;
1900978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            float scaleEndRow = 1;
1901978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            float scaleStartCol = 0;
1902978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            float scaleEndCol = 1;
1903978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            switch (horizontalMode) {
1904978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                case ANCHOR_HORIZONTAL_MODE_LEFT:
1905978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    gravity = Gravity.LEFT;
1906978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    mCCView.setAlignment(Alignment.ALIGN_NORMAL);
1907978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    scaleStartCol = scaleCol;
1908978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    break;
1909978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                case ANCHOR_HORIZONTAL_MODE_CENTER:
1910978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    float gap = Math.min(1 - scaleCol, scaleCol);
1911978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1912978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    // Since all TV sets use left text alignment instead of center text alignment
1913978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    // for this case, we follow the industry convention if possible.
1914978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    int columnCount = captionWindow.columnCount + 1;
1915978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    columnCount = Math.min(getScreenColumnCount(), columnCount);
1916978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    StringBuilder widestTextBuilder = new StringBuilder();
1917978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    for (int i = 0; i < columnCount; ++i) {
1918978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                        widestTextBuilder.append(mWidestChar);
1919978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    }
1920978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    Paint paint = new Paint();
1921978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    paint.setTypeface(mCaptionStyle.getTypeface());
1922978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    paint.setTextSize(mTextSize);
1923978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    float maxWindowWidth = paint.measureText(widestTextBuilder.toString());
1924978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    float halfMaxWidthScale = mCCLayout.getWidth() > 0
1925978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                            ? maxWindowWidth / 2.0f / (mCCLayout.getWidth() * 0.8f) : 0.0f;
1926978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    if (halfMaxWidthScale > 0f && halfMaxWidthScale < scaleCol) {
1927978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                        // Calculate the expected max window size based on the column count of the
1928978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                        // caption window multiplied by average alphabets char width, then align the
1929978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                        // left side of the window with the left side of the expected max window.
1930978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                        gravity = Gravity.LEFT;
1931978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                        mCCView.setAlignment(Alignment.ALIGN_NORMAL);
1932978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                        scaleStartCol = scaleCol - halfMaxWidthScale;
1933978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                        scaleEndCol = 1.0f;
1934978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    } else {
1935978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                        // The gap will be the minimum distance value of the distances from both
1936978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                        // horizontal end points to the anchor point.
1937978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                        // If scaleCol <= 0.5, the range of scaleCol is [0, the anchor point * 2].
1938978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                        // If scaleCol > 0.5, the range of scaleCol is
1939978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                        // [(1 - the anchor point) * 2, 1].
1940978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                        // The anchor point is located at the horizontal center of the window in
1941978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                        // both cases.
1942978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                        gravity = Gravity.CENTER_HORIZONTAL;
1943978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                        mCCView.setAlignment(Alignment.ALIGN_CENTER);
1944978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                        scaleStartCol = scaleCol - gap;
1945978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                        scaleEndCol = scaleCol + gap;
1946978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    }
1947978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    break;
1948978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                case ANCHOR_HORIZONTAL_MODE_RIGHT:
1949978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    gravity = Gravity.RIGHT;
1950978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    mCCView.setAlignment(Alignment.ALIGN_RIGHT);
1951978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    scaleEndCol = scaleCol;
1952978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    break;
1953978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
1954978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            switch (verticalMode) {
1955978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                case ANCHOR_VERTICAL_MODE_TOP:
1956978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    gravity |= Gravity.TOP;
1957978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    scaleStartRow = scaleRow;
1958978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    break;
1959978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                case ANCHOR_VERTICAL_MODE_CENTER:
1960978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    gravity |= Gravity.CENTER_VERTICAL;
1961978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1962978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    // See the above comment.
1963978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    float gap = Math.min(1 - scaleRow, scaleRow);
1964978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    scaleStartRow = scaleRow - gap;
1965978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    scaleEndRow = scaleRow + gap;
1966978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    break;
1967978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                case ANCHOR_VERTICAL_MODE_BOTTOM:
1968978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    gravity |= Gravity.BOTTOM;
1969978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    scaleEndRow = scaleRow;
1970978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    break;
1971978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
1972978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            mCCLayout.addOrUpdateViewToSafeTitleArea(this, new ScaledLayout
1973978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    .ScaledLayoutParams(scaleStartRow, scaleEndRow, scaleStartCol, scaleEndCol));
1974978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            setCaptionWindowId(captionWindow.id);
1975978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            setRowLimit(captionWindow.rowCount);
1976978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            setGravity(gravity);
1977978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            if (captionWindow.visible) {
1978978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                show();
1979978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            } else {
1980978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                hide();
1981978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
1982978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
1983978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1984978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        @Override
1985978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
1986978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                int oldTop, int oldRight, int oldBottom) {
1987978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            int width = right - left;
1988978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            int height = bottom - top;
1989978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            if (width != mLastCaptionLayoutWidth || height != mLastCaptionLayoutHeight) {
1990978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                mLastCaptionLayoutWidth = width;
1991978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                mLastCaptionLayoutHeight = height;
1992978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                updateTextSize();
1993978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
1994978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
1995978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
1996978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        private void updateWidestChar() {
1997978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            Paint paint = new Paint();
1998978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            paint.setTypeface(mCaptionStyle.getTypeface());
1999978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            Charset latin1 = Charset.forName("ISO-8859-1");
2000978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            float widestCharWidth = 0f;
2001978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            for (int i = 0; i < 256; ++i) {
2002978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                String ch = new String(new byte[]{(byte) i}, latin1);
2003978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                float charWidth = paint.measureText(ch);
2004978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                if (widestCharWidth < charWidth) {
2005978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    widestCharWidth = charWidth;
2006978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    mWidestChar = ch;
2007978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                }
2008978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
2009978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            updateTextSize();
2010978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
2011978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
2012978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        private void updateTextSize() {
2013978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            if (mCCLayout == null) return;
2014978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
2015978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            // Calculate text size based on the max window size.
2016978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            StringBuilder widestTextBuilder = new StringBuilder();
2017978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            int screenColumnCount = getScreenColumnCount();
2018978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            for (int i = 0; i < screenColumnCount; ++i) {
2019978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                widestTextBuilder.append(mWidestChar);
2020978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
2021978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            String widestText = widestTextBuilder.toString();
2022978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            Paint paint = new Paint();
2023978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            paint.setTypeface(mCaptionStyle.getTypeface());
2024978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            float startFontSize = 0f;
2025978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            float endFontSize = 255f;
2026978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            while (startFontSize < endFontSize) {
2027978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                float testTextSize = (startFontSize + endFontSize) / 2f;
2028978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                paint.setTextSize(testTextSize);
2029978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                float width = paint.measureText(widestText);
2030978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                if (mCCLayout.getWidth() * 0.8f > width) {
2031978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    startFontSize = testTextSize + 0.01f;
2032978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                } else {
2033978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    endFontSize = testTextSize - 0.01f;
2034978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                }
2035978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
2036978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            mTextSize = endFontSize * mFontScale;
2037978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            mCCView.setTextSize(mTextSize);
2038978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
2039978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
2040978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        private int getScreenColumnCount() {
2041978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            // Assume it has a wide aspect ratio track.
2042978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            return MAX_COLUMN_COUNT_16_9;
2043978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
2044978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
2045978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public void removeFromCaptionView() {
2046978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            if (mCCLayout != null) {
2047978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                mCCLayout.removeViewFromSafeTitleArea(this);
2048978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                mCCLayout.removeOnLayoutChangeListener(this);
2049978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                mCCLayout = null;
2050978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
2051978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
2052978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
2053978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public void setText(String text) {
2054978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            updateText(text, false);
2055978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
2056978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
2057978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public void appendText(String text) {
2058978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            updateText(text, true);
2059978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
2060978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
2061978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public void clearText() {
2062978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            mBuilder.clear();
2063978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            mCCView.setText("");
2064978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
2065978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
2066978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        private void updateText(String text, boolean appended) {
2067978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            if (!appended) {
2068978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                mBuilder.clear();
2069978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
2070978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            if (text != null && text.length() > 0) {
2071978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                int length = mBuilder.length();
2072978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                mBuilder.append(text);
2073978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                for (CharacterStyle characterStyle : mCharacterStyles) {
2074978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    mBuilder.setSpan(characterStyle, length, mBuilder.length(),
2075978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                            Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
2076978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                }
2077978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
2078978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            String[] lines = TextUtils.split(mBuilder.toString(), "\n");
2079978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
2080978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            // Truncate text not to exceed the row limit.
2081978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            // Plus one here since the range of the rows is [0, mRowLimit].
2082978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            String truncatedText = TextUtils.join("\n", Arrays.copyOfRange(
2083978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    lines, Math.max(0, lines.length - (mRowLimit + 1)), lines.length));
2084978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            mBuilder.delete(0, mBuilder.length() - truncatedText.length());
2085978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
2086978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            // Trim the buffer first then set text to CCView.
2087978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            int start = 0, last = mBuilder.length() - 1;
2088978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            int end = last;
2089978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            while ((start <= end) && (mBuilder.charAt(start) <= ' ')) {
2090978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                ++start;
2091978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
2092978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            while ((end >= start) && (mBuilder.charAt(end) <= ' ')) {
2093978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                --end;
2094978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
2095978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            if (start == 0 && end == last) {
2096978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                mCCView.setText(mBuilder);
2097978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            } else {
2098978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                SpannableStringBuilder trim = new SpannableStringBuilder();
2099978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                trim.append(mBuilder);
2100978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                if (end < last) {
2101978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    trim.delete(end + 1, last + 1);
2102978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                }
2103978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                if (start > 0) {
2104978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    trim.delete(0, start);
2105978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                }
2106978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                mCCView.setText(trim);
2107978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
2108978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
2109978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
2110978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public void setRowLimit(int rowLimit) {
2111978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            if (rowLimit < 0) {
2112978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                throw new IllegalArgumentException("A rowLimit should have a positive number");
2113978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            }
2114978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            mRowLimit = rowLimit;
2115978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
2116978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    }
2117978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
2118978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    /** @hide */
2119978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    static class CCView extends SubtitleView {
2120978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        private static final CaptionStyle DEFAULT_CAPTION_STYLE = CaptionStyle.DEFAULT;
2121978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
2122978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public CCView(Context context) {
2123978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            this(context, null);
2124978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
2125978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
2126978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public CCView(Context context, AttributeSet attrs) {
2127978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            this(context, attrs, 0);
2128978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
2129978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
2130978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public CCView(Context context, AttributeSet attrs, int defStyleAttr) {
2131978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            this(context, attrs, defStyleAttr, 0);
2132978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
2133978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
2134978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public CCView(Context context, AttributeSet attrs, int defStyleAttr,
2135978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                int defStyleRes) {
2136978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            super(context, attrs, defStyleAttr, defStyleRes);
2137978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
2138978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung
2139978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        public void setCaptionStyle(CaptionStyle style) {
2140978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            setForegroundColor(style.hasForegroundColor()
2141978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    ? style.foregroundColor : DEFAULT_CAPTION_STYLE.foregroundColor);
2142978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            setBackgroundColor(style.hasBackgroundColor()
2143978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    ? style.backgroundColor : DEFAULT_CAPTION_STYLE.backgroundColor);
2144978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            setEdgeType(style.hasEdgeType()
2145978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    ? style.edgeType : DEFAULT_CAPTION_STYLE.edgeType);
2146978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            setEdgeColor(style.hasEdgeColor()
2147978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung                    ? style.edgeColor : DEFAULT_CAPTION_STYLE.edgeColor);
2148978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung            setTypeface(style.getTypeface());
2149978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung        }
2150978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung    }
2151978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung}
2152