1bdfd91024767893829017918ab565f224ccd35ecChong Zhang/* 2bdfd91024767893829017918ab565f224ccd35ecChong Zhang * Copyright (C) 2014 The Android Open Source Project 3bdfd91024767893829017918ab565f224ccd35ecChong Zhang * 4bdfd91024767893829017918ab565f224ccd35ecChong Zhang * Licensed under the Apache License, Version 2.0 (the "License"); 5bdfd91024767893829017918ab565f224ccd35ecChong Zhang * you may not use this file except in compliance with the License. 6bdfd91024767893829017918ab565f224ccd35ecChong Zhang * You may obtain a copy of the License at 7bdfd91024767893829017918ab565f224ccd35ecChong Zhang * 8bdfd91024767893829017918ab565f224ccd35ecChong Zhang * http://www.apache.org/licenses/LICENSE-2.0 9bdfd91024767893829017918ab565f224ccd35ecChong Zhang * 10bdfd91024767893829017918ab565f224ccd35ecChong Zhang * Unless required by applicable law or agreed to in writing, software 11bdfd91024767893829017918ab565f224ccd35ecChong Zhang * distributed under the License is distributed on an "AS IS" BASIS, 12bdfd91024767893829017918ab565f224ccd35ecChong Zhang * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13bdfd91024767893829017918ab565f224ccd35ecChong Zhang * See the License for the specific language governing permissions and 14bdfd91024767893829017918ab565f224ccd35ecChong Zhang * limitations under the License. 15bdfd91024767893829017918ab565f224ccd35ecChong Zhang */ 16bdfd91024767893829017918ab565f224ccd35ecChong Zhang 17bdfd91024767893829017918ab565f224ccd35ecChong Zhangpackage android.media; 18bdfd91024767893829017918ab565f224ccd35ecChong Zhang 19bdfd91024767893829017918ab565f224ccd35ecChong Zhangimport android.content.Context; 2019ed4a179ccea6915f1852139d520dddba779490Chong Zhangimport android.content.res.Resources; 2119ed4a179ccea6915f1852139d520dddba779490Chong Zhangimport android.graphics.Canvas; 22bdfd91024767893829017918ab565f224ccd35ecChong Zhangimport android.graphics.Color; 2319ed4a179ccea6915f1852139d520dddba779490Chong Zhangimport android.graphics.Paint; 24bdfd91024767893829017918ab565f224ccd35ecChong Zhangimport android.graphics.Rect; 25bdfd91024767893829017918ab565f224ccd35ecChong Zhangimport android.graphics.Typeface; 2619ed4a179ccea6915f1852139d520dddba779490Chong Zhangimport android.text.Spannable; 2719ed4a179ccea6915f1852139d520dddba779490Chong Zhangimport android.text.SpannableStringBuilder; 2819ed4a179ccea6915f1852139d520dddba779490Chong Zhangimport android.text.TextPaint; 2919ed4a179ccea6915f1852139d520dddba779490Chong Zhangimport android.text.style.CharacterStyle; 3019ed4a179ccea6915f1852139d520dddba779490Chong Zhangimport android.text.style.StyleSpan; 3119ed4a179ccea6915f1852139d520dddba779490Chong Zhangimport android.text.style.UnderlineSpan; 3219ed4a179ccea6915f1852139d520dddba779490Chong Zhangimport android.text.style.UpdateAppearance; 33bdfd91024767893829017918ab565f224ccd35ecChong Zhangimport android.util.AttributeSet; 34bdfd91024767893829017918ab565f224ccd35ecChong Zhangimport android.util.Log; 35bdfd91024767893829017918ab565f224ccd35ecChong Zhangimport android.util.TypedValue; 36bdfd91024767893829017918ab565f224ccd35ecChong Zhangimport android.view.Gravity; 37bdfd91024767893829017918ab565f224ccd35ecChong Zhangimport android.view.View; 38bdfd91024767893829017918ab565f224ccd35ecChong Zhangimport android.view.ViewGroup; 39bdfd91024767893829017918ab565f224ccd35ecChong Zhangimport android.view.accessibility.CaptioningManager; 40bdfd91024767893829017918ab565f224ccd35ecChong Zhangimport android.view.accessibility.CaptioningManager.CaptionStyle; 41bdfd91024767893829017918ab565f224ccd35ecChong Zhangimport android.view.accessibility.CaptioningManager.CaptioningChangeListener; 42bdfd91024767893829017918ab565f224ccd35ecChong Zhangimport android.widget.LinearLayout; 43bdfd91024767893829017918ab565f224ccd35ecChong Zhangimport android.widget.TextView; 44bdfd91024767893829017918ab565f224ccd35ecChong Zhang 45bdfd91024767893829017918ab565f224ccd35ecChong Zhangimport java.util.ArrayList; 46bdfd91024767893829017918ab565f224ccd35ecChong Zhangimport java.util.Arrays; 47bdfd91024767893829017918ab565f224ccd35ecChong Zhangimport java.util.Vector; 48bdfd91024767893829017918ab565f224ccd35ecChong Zhang 49bdfd91024767893829017918ab565f224ccd35ecChong Zhang/** @hide */ 50bdfd91024767893829017918ab565f224ccd35ecChong Zhangpublic class ClosedCaptionRenderer extends SubtitleController.Renderer { 51bdfd91024767893829017918ab565f224ccd35ecChong Zhang private final Context mContext; 52978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung private Cea608CCWidget mCCWidget; 53bdfd91024767893829017918ab565f224ccd35ecChong Zhang 54bdfd91024767893829017918ab565f224ccd35ecChong Zhang public ClosedCaptionRenderer(Context context) { 55bdfd91024767893829017918ab565f224ccd35ecChong Zhang mContext = context; 56bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 57bdfd91024767893829017918ab565f224ccd35ecChong Zhang 58bdfd91024767893829017918ab565f224ccd35ecChong Zhang @Override 59bdfd91024767893829017918ab565f224ccd35ecChong Zhang public boolean supports(MediaFormat format) { 60bdfd91024767893829017918ab565f224ccd35ecChong Zhang if (format.containsKey(MediaFormat.KEY_MIME)) { 61978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung String mimeType = format.getString(MediaFormat.KEY_MIME); 62978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung return MediaPlayer.MEDIA_MIMETYPE_TEXT_CEA_608.equals(mimeType); 63bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 64bdfd91024767893829017918ab565f224ccd35ecChong Zhang return false; 65bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 66bdfd91024767893829017918ab565f224ccd35ecChong Zhang 67bdfd91024767893829017918ab565f224ccd35ecChong Zhang @Override 68bdfd91024767893829017918ab565f224ccd35ecChong Zhang public SubtitleTrack createTrack(MediaFormat format) { 69978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung String mimeType = format.getString(MediaFormat.KEY_MIME); 70978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung if (MediaPlayer.MEDIA_MIMETYPE_TEXT_CEA_608.equals(mimeType)) { 71978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung if (mCCWidget == null) { 72978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung mCCWidget = new Cea608CCWidget(mContext); 73978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung } 74978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung return new Cea608CaptionTrack(mCCWidget, format); 75bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 76978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung throw new RuntimeException("No matching format: " + format.toString()); 77bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 78bdfd91024767893829017918ab565f224ccd35ecChong Zhang} 79bdfd91024767893829017918ab565f224ccd35ecChong Zhang 80bdfd91024767893829017918ab565f224ccd35ecChong Zhang/** @hide */ 81978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chungclass Cea608CaptionTrack extends SubtitleTrack { 82978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung private final Cea608CCParser mCCParser; 83978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung private final Cea608CCWidget mRenderingWidget; 84bdfd91024767893829017918ab565f224ccd35ecChong Zhang 85978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung Cea608CaptionTrack(Cea608CCWidget renderingWidget, MediaFormat format) { 86bdfd91024767893829017918ab565f224ccd35ecChong Zhang super(format); 87bdfd91024767893829017918ab565f224ccd35ecChong Zhang 88bdfd91024767893829017918ab565f224ccd35ecChong Zhang mRenderingWidget = renderingWidget; 89978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung mCCParser = new Cea608CCParser(mRenderingWidget); 90bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 91bdfd91024767893829017918ab565f224ccd35ecChong Zhang 92bdfd91024767893829017918ab565f224ccd35ecChong Zhang @Override 93bdfd91024767893829017918ab565f224ccd35ecChong Zhang public void onData(byte[] data, boolean eos, long runID) { 94bdfd91024767893829017918ab565f224ccd35ecChong Zhang mCCParser.parse(data); 95bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 96bdfd91024767893829017918ab565f224ccd35ecChong Zhang 97bdfd91024767893829017918ab565f224ccd35ecChong Zhang @Override 98bdfd91024767893829017918ab565f224ccd35ecChong Zhang public RenderingWidget getRenderingWidget() { 99bdfd91024767893829017918ab565f224ccd35ecChong Zhang return mRenderingWidget; 100bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 101bdfd91024767893829017918ab565f224ccd35ecChong Zhang 102bdfd91024767893829017918ab565f224ccd35ecChong Zhang @Override 103bdfd91024767893829017918ab565f224ccd35ecChong Zhang public void updateView(Vector<Cue> activeCues) { 104bdfd91024767893829017918ab565f224ccd35ecChong Zhang // Overriding with NO-OP, CC rendering by-passes this 105bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 106bdfd91024767893829017918ab565f224ccd35ecChong Zhang} 107bdfd91024767893829017918ab565f224ccd35ecChong Zhang 108bdfd91024767893829017918ab565f224ccd35ecChong Zhang/** 109978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung * Abstract widget class to render a closed caption track. 110978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung * 111978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung * @hide 112978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung */ 113978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chungabstract class ClosedCaptionWidget extends ViewGroup implements SubtitleTrack.RenderingWidget { 114978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung 115978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung /** @hide */ 116978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung interface ClosedCaptionLayout { 117978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung void setCaptionStyle(CaptionStyle captionStyle); 118978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung void setFontScale(float scale); 119978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung } 120978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung 121978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung private static final CaptionStyle DEFAULT_CAPTION_STYLE = CaptionStyle.DEFAULT; 122978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung 123978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung /** Captioning manager, used to obtain and track caption properties. */ 124978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung private final CaptioningManager mManager; 125978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung 126978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung /** Current caption style. */ 127978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung protected CaptionStyle mCaptionStyle; 128978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung 129978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung /** Callback for rendering changes. */ 130978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung protected OnChangedListener mListener; 131978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung 132978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung /** Concrete layout of CC. */ 133978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung protected ClosedCaptionLayout mClosedCaptionLayout; 134978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung 135978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung /** Whether a caption style change listener is registered. */ 136978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung private boolean mHasChangeListener; 137978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung 138978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung public ClosedCaptionWidget(Context context) { 139978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung this(context, null); 140978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung } 141978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung 142978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung public ClosedCaptionWidget(Context context, AttributeSet attrs) { 143978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung this(context, attrs, 0); 144978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung } 145978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung 146978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung public ClosedCaptionWidget(Context context, AttributeSet attrs, int defStyle) { 147978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung this(context, attrs, defStyle, 0); 148978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung } 149978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung 150978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung public ClosedCaptionWidget(Context context, AttributeSet attrs, int defStyleAttr, 151978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung int defStyleRes) { 152978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung super(context, attrs, defStyleAttr, defStyleRes); 153978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung 154978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung // Cannot render text over video when layer type is hardware. 155978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung setLayerType(View.LAYER_TYPE_SOFTWARE, null); 156978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung 157978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung mManager = (CaptioningManager) context.getSystemService(Context.CAPTIONING_SERVICE); 158978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung mCaptionStyle = DEFAULT_CAPTION_STYLE.applyStyle(mManager.getUserStyle()); 159978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung 160978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung mClosedCaptionLayout = createCaptionLayout(context); 161978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung mClosedCaptionLayout.setCaptionStyle(mCaptionStyle); 162978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung mClosedCaptionLayout.setFontScale(mManager.getFontScale()); 163978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung addView((ViewGroup) mClosedCaptionLayout, LayoutParams.MATCH_PARENT, 164978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung LayoutParams.MATCH_PARENT); 165978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung 166978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung requestLayout(); 167978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung } 168978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung 169978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung public abstract ClosedCaptionLayout createCaptionLayout(Context context); 170978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung 171978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung @Override 172978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung public void setOnChangedListener(OnChangedListener listener) { 173978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung mListener = listener; 174978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung } 175978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung 176978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung @Override 177978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung public void setSize(int width, int height) { 178978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung final int widthSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY); 179978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung final int heightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY); 180978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung 181978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung measure(widthSpec, heightSpec); 182978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung layout(0, 0, width, height); 183978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung } 184978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung 185978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung @Override 186978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung public void setVisible(boolean visible) { 187978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung if (visible) { 188978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung setVisibility(View.VISIBLE); 189978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung } else { 190978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung setVisibility(View.GONE); 191978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung } 192978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung 193978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung manageChangeListener(); 194978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung } 195978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung 196978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung @Override 197978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung public void onAttachedToWindow() { 198978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung super.onAttachedToWindow(); 199978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung 200978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung manageChangeListener(); 201978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung } 202978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung 203978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung @Override 204978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung public void onDetachedFromWindow() { 205978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung super.onDetachedFromWindow(); 206978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung 207978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung manageChangeListener(); 208978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung } 209978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung 210978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung @Override 211978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 212978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung super.onMeasure(widthMeasureSpec, heightMeasureSpec); 213978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung ((ViewGroup) mClosedCaptionLayout).measure(widthMeasureSpec, heightMeasureSpec); 214978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung } 215978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung 216978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung @Override 217978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung protected void onLayout(boolean changed, int l, int t, int r, int b) { 218978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung ((ViewGroup) mClosedCaptionLayout).layout(l, t, r, b); 219978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung } 220978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung 221978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung /** 222978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung * Manages whether this renderer is listening for caption style changes. 223978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung */ 224978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung private final CaptioningChangeListener mCaptioningListener = new CaptioningChangeListener() { 225978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung @Override 226978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung public void onUserStyleChanged(CaptionStyle userStyle) { 227978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung mCaptionStyle = DEFAULT_CAPTION_STYLE.applyStyle(userStyle); 228978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung mClosedCaptionLayout.setCaptionStyle(mCaptionStyle); 229978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung } 230978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung 231978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung @Override 232978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung public void onFontScaleChanged(float fontScale) { 233978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung mClosedCaptionLayout.setFontScale(fontScale); 234978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung } 235978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung }; 236978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung 237978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung private void manageChangeListener() { 238978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung final boolean needsListener = isAttachedToWindow() && getVisibility() == View.VISIBLE; 239978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung if (mHasChangeListener != needsListener) { 240978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung mHasChangeListener = needsListener; 241978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung 242978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung if (needsListener) { 243978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung mManager.addCaptioningChangeListener(mCaptioningListener); 244978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung } else { 245978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung mManager.removeCaptioningChangeListener(mCaptioningListener); 246978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung } 247978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung } 248978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung } 249978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung} 250978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung 251978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung/** 252bdfd91024767893829017918ab565f224ccd35ecChong Zhang * @hide 253bdfd91024767893829017918ab565f224ccd35ecChong Zhang * 254bdfd91024767893829017918ab565f224ccd35ecChong Zhang * CCParser processes CEA-608 closed caption data. 255bdfd91024767893829017918ab565f224ccd35ecChong Zhang * 256bdfd91024767893829017918ab565f224ccd35ecChong Zhang * It calls back into OnDisplayChangedListener upon 257bdfd91024767893829017918ab565f224ccd35ecChong Zhang * display change with styled text for rendering. 258bdfd91024767893829017918ab565f224ccd35ecChong Zhang * 259bdfd91024767893829017918ab565f224ccd35ecChong Zhang */ 260978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chungclass Cea608CCParser { 261bdfd91024767893829017918ab565f224ccd35ecChong Zhang public static final int MAX_ROWS = 15; 262bdfd91024767893829017918ab565f224ccd35ecChong Zhang public static final int MAX_COLS = 32; 263bdfd91024767893829017918ab565f224ccd35ecChong Zhang 264978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung private static final String TAG = "Cea608CCParser"; 265bdfd91024767893829017918ab565f224ccd35ecChong Zhang private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 266bdfd91024767893829017918ab565f224ccd35ecChong Zhang 267bdfd91024767893829017918ab565f224ccd35ecChong Zhang private static final int INVALID = -1; 268bdfd91024767893829017918ab565f224ccd35ecChong Zhang 269bdfd91024767893829017918ab565f224ccd35ecChong Zhang // EIA-CEA-608: Table 70 - Control Codes 270bdfd91024767893829017918ab565f224ccd35ecChong Zhang private static final int RCL = 0x20; 271bdfd91024767893829017918ab565f224ccd35ecChong Zhang private static final int BS = 0x21; 272bdfd91024767893829017918ab565f224ccd35ecChong Zhang private static final int AOF = 0x22; 273bdfd91024767893829017918ab565f224ccd35ecChong Zhang private static final int AON = 0x23; 274bdfd91024767893829017918ab565f224ccd35ecChong Zhang private static final int DER = 0x24; 275bdfd91024767893829017918ab565f224ccd35ecChong Zhang private static final int RU2 = 0x25; 276bdfd91024767893829017918ab565f224ccd35ecChong Zhang private static final int RU3 = 0x26; 277bdfd91024767893829017918ab565f224ccd35ecChong Zhang private static final int RU4 = 0x27; 278bdfd91024767893829017918ab565f224ccd35ecChong Zhang private static final int FON = 0x28; 279bdfd91024767893829017918ab565f224ccd35ecChong Zhang private static final int RDC = 0x29; 280bdfd91024767893829017918ab565f224ccd35ecChong Zhang private static final int TR = 0x2a; 281bdfd91024767893829017918ab565f224ccd35ecChong Zhang private static final int RTD = 0x2b; 282bdfd91024767893829017918ab565f224ccd35ecChong Zhang private static final int EDM = 0x2c; 283bdfd91024767893829017918ab565f224ccd35ecChong Zhang private static final int CR = 0x2d; 284bdfd91024767893829017918ab565f224ccd35ecChong Zhang private static final int ENM = 0x2e; 285bdfd91024767893829017918ab565f224ccd35ecChong Zhang private static final int EOC = 0x2f; 286bdfd91024767893829017918ab565f224ccd35ecChong Zhang 287bdfd91024767893829017918ab565f224ccd35ecChong Zhang // Transparent Space 288bdfd91024767893829017918ab565f224ccd35ecChong Zhang private static final char TS = '\u00A0'; 289bdfd91024767893829017918ab565f224ccd35ecChong Zhang 290bdfd91024767893829017918ab565f224ccd35ecChong Zhang // Captioning Modes 291bdfd91024767893829017918ab565f224ccd35ecChong Zhang private static final int MODE_UNKNOWN = 0; 292bdfd91024767893829017918ab565f224ccd35ecChong Zhang private static final int MODE_PAINT_ON = 1; 293bdfd91024767893829017918ab565f224ccd35ecChong Zhang private static final int MODE_ROLL_UP = 2; 294bdfd91024767893829017918ab565f224ccd35ecChong Zhang private static final int MODE_POP_ON = 3; 295bdfd91024767893829017918ab565f224ccd35ecChong Zhang private static final int MODE_TEXT = 4; 296bdfd91024767893829017918ab565f224ccd35ecChong Zhang 297bdfd91024767893829017918ab565f224ccd35ecChong Zhang private final DisplayListener mListener; 298bdfd91024767893829017918ab565f224ccd35ecChong Zhang 299bdfd91024767893829017918ab565f224ccd35ecChong Zhang private int mMode = MODE_PAINT_ON; 300bdfd91024767893829017918ab565f224ccd35ecChong Zhang private int mRollUpSize = 4; 30156a7ed6982ce5100ed6fa5442931b2f39851a08cChong Zhang private int mPrevCtrlCode = INVALID; 302bdfd91024767893829017918ab565f224ccd35ecChong Zhang 303bdfd91024767893829017918ab565f224ccd35ecChong Zhang private CCMemory mDisplay = new CCMemory(); 304bdfd91024767893829017918ab565f224ccd35ecChong Zhang private CCMemory mNonDisplay = new CCMemory(); 305bdfd91024767893829017918ab565f224ccd35ecChong Zhang private CCMemory mTextMem = new CCMemory(); 306bdfd91024767893829017918ab565f224ccd35ecChong Zhang 307978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung Cea608CCParser(DisplayListener listener) { 308bdfd91024767893829017918ab565f224ccd35ecChong Zhang mListener = listener; 309bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 310bdfd91024767893829017918ab565f224ccd35ecChong Zhang 311978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung public void parse(byte[] data) { 312bdfd91024767893829017918ab565f224ccd35ecChong Zhang CCData[] ccData = CCData.fromByteArray(data); 313bdfd91024767893829017918ab565f224ccd35ecChong Zhang 314bdfd91024767893829017918ab565f224ccd35ecChong Zhang for (int i = 0; i < ccData.length; i++) { 315bdfd91024767893829017918ab565f224ccd35ecChong Zhang if (DEBUG) { 316bdfd91024767893829017918ab565f224ccd35ecChong Zhang Log.d(TAG, ccData[i].toString()); 317bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 318bdfd91024767893829017918ab565f224ccd35ecChong Zhang 319bdfd91024767893829017918ab565f224ccd35ecChong Zhang if (handleCtrlCode(ccData[i]) 320bdfd91024767893829017918ab565f224ccd35ecChong Zhang || handleTabOffsets(ccData[i]) 321bdfd91024767893829017918ab565f224ccd35ecChong Zhang || handlePACCode(ccData[i]) 322bdfd91024767893829017918ab565f224ccd35ecChong Zhang || handleMidRowCode(ccData[i])) { 323bdfd91024767893829017918ab565f224ccd35ecChong Zhang continue; 324bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 325bdfd91024767893829017918ab565f224ccd35ecChong Zhang 326bdfd91024767893829017918ab565f224ccd35ecChong Zhang handleDisplayableChars(ccData[i]); 327bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 328bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 329bdfd91024767893829017918ab565f224ccd35ecChong Zhang 330bdfd91024767893829017918ab565f224ccd35ecChong Zhang interface DisplayListener { 331978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung void onDisplayChanged(SpannableStringBuilder[] styledTexts); 332978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung CaptionStyle getCaptionStyle(); 333bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 334bdfd91024767893829017918ab565f224ccd35ecChong Zhang 335bdfd91024767893829017918ab565f224ccd35ecChong Zhang private CCMemory getMemory() { 336bdfd91024767893829017918ab565f224ccd35ecChong Zhang // get the CC memory to operate on for current mode 337bdfd91024767893829017918ab565f224ccd35ecChong Zhang switch (mMode) { 338bdfd91024767893829017918ab565f224ccd35ecChong Zhang case MODE_POP_ON: 339bdfd91024767893829017918ab565f224ccd35ecChong Zhang return mNonDisplay; 340bdfd91024767893829017918ab565f224ccd35ecChong Zhang case MODE_TEXT: 341bdfd91024767893829017918ab565f224ccd35ecChong Zhang // TODO(chz): support only caption mode for now, 342bdfd91024767893829017918ab565f224ccd35ecChong Zhang // in text mode, dump everything to text mem. 343bdfd91024767893829017918ab565f224ccd35ecChong Zhang return mTextMem; 344bdfd91024767893829017918ab565f224ccd35ecChong Zhang case MODE_PAINT_ON: 345bdfd91024767893829017918ab565f224ccd35ecChong Zhang case MODE_ROLL_UP: 346bdfd91024767893829017918ab565f224ccd35ecChong Zhang return mDisplay; 347bdfd91024767893829017918ab565f224ccd35ecChong Zhang default: 348bdfd91024767893829017918ab565f224ccd35ecChong Zhang Log.w(TAG, "unrecoginized mode: " + mMode); 349bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 350bdfd91024767893829017918ab565f224ccd35ecChong Zhang return mDisplay; 351bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 352bdfd91024767893829017918ab565f224ccd35ecChong Zhang 353bdfd91024767893829017918ab565f224ccd35ecChong Zhang private boolean handleDisplayableChars(CCData ccData) { 354bdfd91024767893829017918ab565f224ccd35ecChong Zhang if (!ccData.isDisplayableChar()) { 355bdfd91024767893829017918ab565f224ccd35ecChong Zhang return false; 356bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 357bdfd91024767893829017918ab565f224ccd35ecChong Zhang 358bdfd91024767893829017918ab565f224ccd35ecChong Zhang // Extended char includes 1 automatic backspace 359bdfd91024767893829017918ab565f224ccd35ecChong Zhang if (ccData.isExtendedChar()) { 360bdfd91024767893829017918ab565f224ccd35ecChong Zhang getMemory().bs(); 361bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 362bdfd91024767893829017918ab565f224ccd35ecChong Zhang 363bdfd91024767893829017918ab565f224ccd35ecChong Zhang getMemory().writeText(ccData.getDisplayText()); 364bdfd91024767893829017918ab565f224ccd35ecChong Zhang 365bdfd91024767893829017918ab565f224ccd35ecChong Zhang if (mMode == MODE_PAINT_ON || mMode == MODE_ROLL_UP) { 366bdfd91024767893829017918ab565f224ccd35ecChong Zhang updateDisplay(); 367bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 368bdfd91024767893829017918ab565f224ccd35ecChong Zhang 369bdfd91024767893829017918ab565f224ccd35ecChong Zhang return true; 370bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 371bdfd91024767893829017918ab565f224ccd35ecChong Zhang 372bdfd91024767893829017918ab565f224ccd35ecChong Zhang private boolean handleMidRowCode(CCData ccData) { 373bdfd91024767893829017918ab565f224ccd35ecChong Zhang StyleCode m = ccData.getMidRow(); 374bdfd91024767893829017918ab565f224ccd35ecChong Zhang if (m != null) { 375bdfd91024767893829017918ab565f224ccd35ecChong Zhang getMemory().writeMidRowCode(m); 376bdfd91024767893829017918ab565f224ccd35ecChong Zhang return true; 377bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 378bdfd91024767893829017918ab565f224ccd35ecChong Zhang return false; 379bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 380bdfd91024767893829017918ab565f224ccd35ecChong Zhang 381bdfd91024767893829017918ab565f224ccd35ecChong Zhang private boolean handlePACCode(CCData ccData) { 382bdfd91024767893829017918ab565f224ccd35ecChong Zhang PAC pac = ccData.getPAC(); 383bdfd91024767893829017918ab565f224ccd35ecChong Zhang 384bdfd91024767893829017918ab565f224ccd35ecChong Zhang if (pac != null) { 385bdfd91024767893829017918ab565f224ccd35ecChong Zhang if (mMode == MODE_ROLL_UP) { 386bdfd91024767893829017918ab565f224ccd35ecChong Zhang getMemory().moveBaselineTo(pac.getRow(), mRollUpSize); 387bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 388bdfd91024767893829017918ab565f224ccd35ecChong Zhang getMemory().writePAC(pac); 389bdfd91024767893829017918ab565f224ccd35ecChong Zhang return true; 390bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 391bdfd91024767893829017918ab565f224ccd35ecChong Zhang 392bdfd91024767893829017918ab565f224ccd35ecChong Zhang return false; 393bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 394bdfd91024767893829017918ab565f224ccd35ecChong Zhang 395bdfd91024767893829017918ab565f224ccd35ecChong Zhang private boolean handleTabOffsets(CCData ccData) { 396bdfd91024767893829017918ab565f224ccd35ecChong Zhang int tabs = ccData.getTabOffset(); 397bdfd91024767893829017918ab565f224ccd35ecChong Zhang 398bdfd91024767893829017918ab565f224ccd35ecChong Zhang if (tabs > 0) { 399bdfd91024767893829017918ab565f224ccd35ecChong Zhang getMemory().tab(tabs); 400bdfd91024767893829017918ab565f224ccd35ecChong Zhang return true; 401bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 402bdfd91024767893829017918ab565f224ccd35ecChong Zhang 403bdfd91024767893829017918ab565f224ccd35ecChong Zhang return false; 404bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 405bdfd91024767893829017918ab565f224ccd35ecChong Zhang 406bdfd91024767893829017918ab565f224ccd35ecChong Zhang private boolean handleCtrlCode(CCData ccData) { 407bdfd91024767893829017918ab565f224ccd35ecChong Zhang int ctrlCode = ccData.getCtrlCode(); 40856a7ed6982ce5100ed6fa5442931b2f39851a08cChong Zhang 40956a7ed6982ce5100ed6fa5442931b2f39851a08cChong Zhang if (mPrevCtrlCode != INVALID && mPrevCtrlCode == ctrlCode) { 41056a7ed6982ce5100ed6fa5442931b2f39851a08cChong Zhang // discard double ctrl codes (but if there's a 3rd one, we still take that) 41156a7ed6982ce5100ed6fa5442931b2f39851a08cChong Zhang mPrevCtrlCode = INVALID; 41256a7ed6982ce5100ed6fa5442931b2f39851a08cChong Zhang return true; 41356a7ed6982ce5100ed6fa5442931b2f39851a08cChong Zhang } 41456a7ed6982ce5100ed6fa5442931b2f39851a08cChong Zhang 415bdfd91024767893829017918ab565f224ccd35ecChong Zhang switch(ctrlCode) { 416bdfd91024767893829017918ab565f224ccd35ecChong Zhang case RCL: 417bdfd91024767893829017918ab565f224ccd35ecChong Zhang // select pop-on style 418bdfd91024767893829017918ab565f224ccd35ecChong Zhang mMode = MODE_POP_ON; 419bdfd91024767893829017918ab565f224ccd35ecChong Zhang break; 420bdfd91024767893829017918ab565f224ccd35ecChong Zhang case BS: 421bdfd91024767893829017918ab565f224ccd35ecChong Zhang getMemory().bs(); 422bdfd91024767893829017918ab565f224ccd35ecChong Zhang break; 423bdfd91024767893829017918ab565f224ccd35ecChong Zhang case DER: 424bdfd91024767893829017918ab565f224ccd35ecChong Zhang getMemory().der(); 425bdfd91024767893829017918ab565f224ccd35ecChong Zhang break; 426bdfd91024767893829017918ab565f224ccd35ecChong Zhang case RU2: 427bdfd91024767893829017918ab565f224ccd35ecChong Zhang case RU3: 428bdfd91024767893829017918ab565f224ccd35ecChong Zhang case RU4: 429bdfd91024767893829017918ab565f224ccd35ecChong Zhang mRollUpSize = (ctrlCode - 0x23); 430bdfd91024767893829017918ab565f224ccd35ecChong Zhang // erase memory if currently in other style 431bdfd91024767893829017918ab565f224ccd35ecChong Zhang if (mMode != MODE_ROLL_UP) { 432bdfd91024767893829017918ab565f224ccd35ecChong Zhang mDisplay.erase(); 433bdfd91024767893829017918ab565f224ccd35ecChong Zhang mNonDisplay.erase(); 434bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 435bdfd91024767893829017918ab565f224ccd35ecChong Zhang // select roll-up style 436bdfd91024767893829017918ab565f224ccd35ecChong Zhang mMode = MODE_ROLL_UP; 437bdfd91024767893829017918ab565f224ccd35ecChong Zhang break; 438bdfd91024767893829017918ab565f224ccd35ecChong Zhang case FON: 439bdfd91024767893829017918ab565f224ccd35ecChong Zhang Log.i(TAG, "Flash On"); 440bdfd91024767893829017918ab565f224ccd35ecChong Zhang break; 441bdfd91024767893829017918ab565f224ccd35ecChong Zhang case RDC: 442bdfd91024767893829017918ab565f224ccd35ecChong Zhang // select paint-on style 443bdfd91024767893829017918ab565f224ccd35ecChong Zhang mMode = MODE_PAINT_ON; 444bdfd91024767893829017918ab565f224ccd35ecChong Zhang break; 445bdfd91024767893829017918ab565f224ccd35ecChong Zhang case TR: 446bdfd91024767893829017918ab565f224ccd35ecChong Zhang mMode = MODE_TEXT; 447bdfd91024767893829017918ab565f224ccd35ecChong Zhang mTextMem.erase(); 448bdfd91024767893829017918ab565f224ccd35ecChong Zhang break; 449bdfd91024767893829017918ab565f224ccd35ecChong Zhang case RTD: 450bdfd91024767893829017918ab565f224ccd35ecChong Zhang mMode = MODE_TEXT; 451bdfd91024767893829017918ab565f224ccd35ecChong Zhang break; 452bdfd91024767893829017918ab565f224ccd35ecChong Zhang case EDM: 453bdfd91024767893829017918ab565f224ccd35ecChong Zhang // erase display memory 454bdfd91024767893829017918ab565f224ccd35ecChong Zhang mDisplay.erase(); 455bdfd91024767893829017918ab565f224ccd35ecChong Zhang updateDisplay(); 456bdfd91024767893829017918ab565f224ccd35ecChong Zhang break; 457bdfd91024767893829017918ab565f224ccd35ecChong Zhang case CR: 458bdfd91024767893829017918ab565f224ccd35ecChong Zhang if (mMode == MODE_ROLL_UP) { 459bdfd91024767893829017918ab565f224ccd35ecChong Zhang getMemory().rollUp(mRollUpSize); 460bdfd91024767893829017918ab565f224ccd35ecChong Zhang } else { 461bdfd91024767893829017918ab565f224ccd35ecChong Zhang getMemory().cr(); 462bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 463bdfd91024767893829017918ab565f224ccd35ecChong Zhang if (mMode == MODE_ROLL_UP) { 464bdfd91024767893829017918ab565f224ccd35ecChong Zhang updateDisplay(); 465bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 466bdfd91024767893829017918ab565f224ccd35ecChong Zhang break; 467bdfd91024767893829017918ab565f224ccd35ecChong Zhang case ENM: 468bdfd91024767893829017918ab565f224ccd35ecChong Zhang // erase non-display memory 469bdfd91024767893829017918ab565f224ccd35ecChong Zhang mNonDisplay.erase(); 470bdfd91024767893829017918ab565f224ccd35ecChong Zhang break; 471bdfd91024767893829017918ab565f224ccd35ecChong Zhang case EOC: 472bdfd91024767893829017918ab565f224ccd35ecChong Zhang // swap display/non-display memory 473bdfd91024767893829017918ab565f224ccd35ecChong Zhang swapMemory(); 474bdfd91024767893829017918ab565f224ccd35ecChong Zhang // switch to pop-on style 475bdfd91024767893829017918ab565f224ccd35ecChong Zhang mMode = MODE_POP_ON; 476bdfd91024767893829017918ab565f224ccd35ecChong Zhang updateDisplay(); 477bdfd91024767893829017918ab565f224ccd35ecChong Zhang break; 478bdfd91024767893829017918ab565f224ccd35ecChong Zhang case INVALID: 479bdfd91024767893829017918ab565f224ccd35ecChong Zhang default: 48056a7ed6982ce5100ed6fa5442931b2f39851a08cChong Zhang mPrevCtrlCode = INVALID; 481bdfd91024767893829017918ab565f224ccd35ecChong Zhang return false; 482bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 483bdfd91024767893829017918ab565f224ccd35ecChong Zhang 48456a7ed6982ce5100ed6fa5442931b2f39851a08cChong Zhang mPrevCtrlCode = ctrlCode; 48556a7ed6982ce5100ed6fa5442931b2f39851a08cChong Zhang 486bdfd91024767893829017918ab565f224ccd35ecChong Zhang // handled 487bdfd91024767893829017918ab565f224ccd35ecChong Zhang return true; 488bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 489bdfd91024767893829017918ab565f224ccd35ecChong Zhang 490bdfd91024767893829017918ab565f224ccd35ecChong Zhang private void updateDisplay() { 491bdfd91024767893829017918ab565f224ccd35ecChong Zhang if (mListener != null) { 492bdfd91024767893829017918ab565f224ccd35ecChong Zhang CaptionStyle captionStyle = mListener.getCaptionStyle(); 493bdfd91024767893829017918ab565f224ccd35ecChong Zhang mListener.onDisplayChanged(mDisplay.getStyledText(captionStyle)); 494bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 495bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 496bdfd91024767893829017918ab565f224ccd35ecChong Zhang 497bdfd91024767893829017918ab565f224ccd35ecChong Zhang private void swapMemory() { 498bdfd91024767893829017918ab565f224ccd35ecChong Zhang CCMemory temp = mDisplay; 499bdfd91024767893829017918ab565f224ccd35ecChong Zhang mDisplay = mNonDisplay; 500bdfd91024767893829017918ab565f224ccd35ecChong Zhang mNonDisplay = temp; 501bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 502bdfd91024767893829017918ab565f224ccd35ecChong Zhang 503bdfd91024767893829017918ab565f224ccd35ecChong Zhang private static class StyleCode { 504bdfd91024767893829017918ab565f224ccd35ecChong Zhang static final int COLOR_WHITE = 0; 505bdfd91024767893829017918ab565f224ccd35ecChong Zhang static final int COLOR_GREEN = 1; 506bdfd91024767893829017918ab565f224ccd35ecChong Zhang static final int COLOR_BLUE = 2; 507bdfd91024767893829017918ab565f224ccd35ecChong Zhang static final int COLOR_CYAN = 3; 508bdfd91024767893829017918ab565f224ccd35ecChong Zhang static final int COLOR_RED = 4; 509bdfd91024767893829017918ab565f224ccd35ecChong Zhang static final int COLOR_YELLOW = 5; 510bdfd91024767893829017918ab565f224ccd35ecChong Zhang static final int COLOR_MAGENTA = 6; 511bdfd91024767893829017918ab565f224ccd35ecChong Zhang static final int COLOR_INVALID = 7; 512bdfd91024767893829017918ab565f224ccd35ecChong Zhang 513bdfd91024767893829017918ab565f224ccd35ecChong Zhang static final int STYLE_ITALICS = 0x00000001; 514bdfd91024767893829017918ab565f224ccd35ecChong Zhang static final int STYLE_UNDERLINE = 0x00000002; 515bdfd91024767893829017918ab565f224ccd35ecChong Zhang 516bdfd91024767893829017918ab565f224ccd35ecChong Zhang static final String[] mColorMap = { 517bdfd91024767893829017918ab565f224ccd35ecChong Zhang "WHITE", "GREEN", "BLUE", "CYAN", "RED", "YELLOW", "MAGENTA", "INVALID" 518bdfd91024767893829017918ab565f224ccd35ecChong Zhang }; 519bdfd91024767893829017918ab565f224ccd35ecChong Zhang 520bdfd91024767893829017918ab565f224ccd35ecChong Zhang final int mStyle; 521bdfd91024767893829017918ab565f224ccd35ecChong Zhang final int mColor; 522bdfd91024767893829017918ab565f224ccd35ecChong Zhang 523bdfd91024767893829017918ab565f224ccd35ecChong Zhang static StyleCode fromByte(byte data2) { 524bdfd91024767893829017918ab565f224ccd35ecChong Zhang int style = 0; 525bdfd91024767893829017918ab565f224ccd35ecChong Zhang int color = (data2 >> 1) & 0x7; 526bdfd91024767893829017918ab565f224ccd35ecChong Zhang 527bdfd91024767893829017918ab565f224ccd35ecChong Zhang if ((data2 & 0x1) != 0) { 528bdfd91024767893829017918ab565f224ccd35ecChong Zhang style |= STYLE_UNDERLINE; 529bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 530bdfd91024767893829017918ab565f224ccd35ecChong Zhang 531bdfd91024767893829017918ab565f224ccd35ecChong Zhang if (color == COLOR_INVALID) { 532bdfd91024767893829017918ab565f224ccd35ecChong Zhang // WHITE ITALICS 533bdfd91024767893829017918ab565f224ccd35ecChong Zhang color = COLOR_WHITE; 534bdfd91024767893829017918ab565f224ccd35ecChong Zhang style |= STYLE_ITALICS; 535bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 536bdfd91024767893829017918ab565f224ccd35ecChong Zhang 537bdfd91024767893829017918ab565f224ccd35ecChong Zhang return new StyleCode(style, color); 538bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 539bdfd91024767893829017918ab565f224ccd35ecChong Zhang 540bdfd91024767893829017918ab565f224ccd35ecChong Zhang StyleCode(int style, int color) { 541bdfd91024767893829017918ab565f224ccd35ecChong Zhang mStyle = style; 542bdfd91024767893829017918ab565f224ccd35ecChong Zhang mColor = color; 543bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 544bdfd91024767893829017918ab565f224ccd35ecChong Zhang 545bdfd91024767893829017918ab565f224ccd35ecChong Zhang boolean isItalics() { 546bdfd91024767893829017918ab565f224ccd35ecChong Zhang return (mStyle & STYLE_ITALICS) != 0; 547bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 548bdfd91024767893829017918ab565f224ccd35ecChong Zhang 54919ed4a179ccea6915f1852139d520dddba779490Chong Zhang boolean isUnderline() { 55019ed4a179ccea6915f1852139d520dddba779490Chong Zhang return (mStyle & STYLE_UNDERLINE) != 0; 55119ed4a179ccea6915f1852139d520dddba779490Chong Zhang } 55219ed4a179ccea6915f1852139d520dddba779490Chong Zhang 553bdfd91024767893829017918ab565f224ccd35ecChong Zhang int getColor() { 554bdfd91024767893829017918ab565f224ccd35ecChong Zhang return mColor; 555bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 556bdfd91024767893829017918ab565f224ccd35ecChong Zhang 557bdfd91024767893829017918ab565f224ccd35ecChong Zhang @Override 558bdfd91024767893829017918ab565f224ccd35ecChong Zhang public String toString() { 559bdfd91024767893829017918ab565f224ccd35ecChong Zhang StringBuilder str = new StringBuilder(); 560bdfd91024767893829017918ab565f224ccd35ecChong Zhang str.append("{"); 561bdfd91024767893829017918ab565f224ccd35ecChong Zhang str.append(mColorMap[mColor]); 562bdfd91024767893829017918ab565f224ccd35ecChong Zhang if ((mStyle & STYLE_ITALICS) != 0) { 563bdfd91024767893829017918ab565f224ccd35ecChong Zhang str.append(", ITALICS"); 564bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 565bdfd91024767893829017918ab565f224ccd35ecChong Zhang if ((mStyle & STYLE_UNDERLINE) != 0) { 566bdfd91024767893829017918ab565f224ccd35ecChong Zhang str.append(", UNDERLINE"); 567bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 568bdfd91024767893829017918ab565f224ccd35ecChong Zhang str.append("}"); 569bdfd91024767893829017918ab565f224ccd35ecChong Zhang 570bdfd91024767893829017918ab565f224ccd35ecChong Zhang return str.toString(); 571bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 572bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 573bdfd91024767893829017918ab565f224ccd35ecChong Zhang 574bdfd91024767893829017918ab565f224ccd35ecChong Zhang private static class PAC extends StyleCode { 575bdfd91024767893829017918ab565f224ccd35ecChong Zhang final int mRow; 576bdfd91024767893829017918ab565f224ccd35ecChong Zhang final int mCol; 577bdfd91024767893829017918ab565f224ccd35ecChong Zhang 578bdfd91024767893829017918ab565f224ccd35ecChong Zhang static PAC fromBytes(byte data1, byte data2) { 579bdfd91024767893829017918ab565f224ccd35ecChong Zhang int[] rowTable = {11, 1, 3, 12, 14, 5, 7, 9}; 580bdfd91024767893829017918ab565f224ccd35ecChong Zhang int row = rowTable[data1 & 0x07] + ((data2 & 0x20) >> 5); 581bdfd91024767893829017918ab565f224ccd35ecChong Zhang int style = 0; 582bdfd91024767893829017918ab565f224ccd35ecChong Zhang if ((data2 & 1) != 0) { 583bdfd91024767893829017918ab565f224ccd35ecChong Zhang style |= STYLE_UNDERLINE; 584bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 585bdfd91024767893829017918ab565f224ccd35ecChong Zhang if ((data2 & 0x10) != 0) { 586bdfd91024767893829017918ab565f224ccd35ecChong Zhang // indent code 587bdfd91024767893829017918ab565f224ccd35ecChong Zhang int indent = (data2 >> 1) & 0x7; 588bdfd91024767893829017918ab565f224ccd35ecChong Zhang return new PAC(row, indent * 4, style, COLOR_WHITE); 589bdfd91024767893829017918ab565f224ccd35ecChong Zhang } else { 590bdfd91024767893829017918ab565f224ccd35ecChong Zhang // style code 591bdfd91024767893829017918ab565f224ccd35ecChong Zhang int color = (data2 >> 1) & 0x7; 592bdfd91024767893829017918ab565f224ccd35ecChong Zhang 593bdfd91024767893829017918ab565f224ccd35ecChong Zhang if (color == COLOR_INVALID) { 594bdfd91024767893829017918ab565f224ccd35ecChong Zhang // WHITE ITALICS 595bdfd91024767893829017918ab565f224ccd35ecChong Zhang color = COLOR_WHITE; 596bdfd91024767893829017918ab565f224ccd35ecChong Zhang style |= STYLE_ITALICS; 597bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 598bdfd91024767893829017918ab565f224ccd35ecChong Zhang return new PAC(row, -1, style, color); 599bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 600bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 601bdfd91024767893829017918ab565f224ccd35ecChong Zhang 602bdfd91024767893829017918ab565f224ccd35ecChong Zhang PAC(int row, int col, int style, int color) { 603bdfd91024767893829017918ab565f224ccd35ecChong Zhang super(style, color); 604bdfd91024767893829017918ab565f224ccd35ecChong Zhang mRow = row; 605bdfd91024767893829017918ab565f224ccd35ecChong Zhang mCol = col; 606bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 607bdfd91024767893829017918ab565f224ccd35ecChong Zhang 608bdfd91024767893829017918ab565f224ccd35ecChong Zhang boolean isIndentPAC() { 609bdfd91024767893829017918ab565f224ccd35ecChong Zhang return (mCol >= 0); 610bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 611bdfd91024767893829017918ab565f224ccd35ecChong Zhang 612bdfd91024767893829017918ab565f224ccd35ecChong Zhang int getRow() { 613bdfd91024767893829017918ab565f224ccd35ecChong Zhang return mRow; 614bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 615bdfd91024767893829017918ab565f224ccd35ecChong Zhang 616bdfd91024767893829017918ab565f224ccd35ecChong Zhang int getCol() { 617bdfd91024767893829017918ab565f224ccd35ecChong Zhang return mCol; 618bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 619bdfd91024767893829017918ab565f224ccd35ecChong Zhang 620bdfd91024767893829017918ab565f224ccd35ecChong Zhang @Override 621bdfd91024767893829017918ab565f224ccd35ecChong Zhang public String toString() { 622bdfd91024767893829017918ab565f224ccd35ecChong Zhang return String.format("{%d, %d}, %s", 623bdfd91024767893829017918ab565f224ccd35ecChong Zhang mRow, mCol, super.toString()); 624bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 625bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 626bdfd91024767893829017918ab565f224ccd35ecChong Zhang 627978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung /** 628978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung * Mutable version of BackgroundSpan to facilitate text rendering with edge styles. 629978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung * 630978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung * @hide 631978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung */ 632978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung public static class MutableBackgroundColorSpan extends CharacterStyle 633978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung implements UpdateAppearance { 634978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung private int mColor; 635978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung 636978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung public MutableBackgroundColorSpan(int color) { 637978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung mColor = color; 638978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung } 639978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung 640978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung public void setBackgroundColor(int color) { 641978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung mColor = color; 642978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung } 643978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung 644978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung public int getBackgroundColor() { 645978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung return mColor; 646978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung } 647978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung 648978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung @Override 649978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung public void updateDrawState(TextPaint ds) { 650978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung ds.bgColor = mColor; 651978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung } 652978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung } 653978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung 654bdfd91024767893829017918ab565f224ccd35ecChong Zhang /* CCLineBuilder keeps track of displayable chars, as well as 655bdfd91024767893829017918ab565f224ccd35ecChong Zhang * MidRow styles and PACs, for a single line of CC memory. 656bdfd91024767893829017918ab565f224ccd35ecChong Zhang * 657bdfd91024767893829017918ab565f224ccd35ecChong Zhang * It generates styled text via getStyledText() method. 658bdfd91024767893829017918ab565f224ccd35ecChong Zhang */ 659bdfd91024767893829017918ab565f224ccd35ecChong Zhang private static class CCLineBuilder { 660bdfd91024767893829017918ab565f224ccd35ecChong Zhang private final StringBuilder mDisplayChars; 661bdfd91024767893829017918ab565f224ccd35ecChong Zhang private final StyleCode[] mMidRowStyles; 662bdfd91024767893829017918ab565f224ccd35ecChong Zhang private final StyleCode[] mPACStyles; 663bdfd91024767893829017918ab565f224ccd35ecChong Zhang 664bdfd91024767893829017918ab565f224ccd35ecChong Zhang CCLineBuilder(String str) { 665bdfd91024767893829017918ab565f224ccd35ecChong Zhang mDisplayChars = new StringBuilder(str); 666bdfd91024767893829017918ab565f224ccd35ecChong Zhang mMidRowStyles = new StyleCode[mDisplayChars.length()]; 667bdfd91024767893829017918ab565f224ccd35ecChong Zhang mPACStyles = new StyleCode[mDisplayChars.length()]; 668bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 669bdfd91024767893829017918ab565f224ccd35ecChong Zhang 670bdfd91024767893829017918ab565f224ccd35ecChong Zhang void setCharAt(int index, char ch) { 671bdfd91024767893829017918ab565f224ccd35ecChong Zhang mDisplayChars.setCharAt(index, ch); 672bdfd91024767893829017918ab565f224ccd35ecChong Zhang mMidRowStyles[index] = null; 673bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 674bdfd91024767893829017918ab565f224ccd35ecChong Zhang 675bdfd91024767893829017918ab565f224ccd35ecChong Zhang void setMidRowAt(int index, StyleCode m) { 676bdfd91024767893829017918ab565f224ccd35ecChong Zhang mDisplayChars.setCharAt(index, ' '); 677bdfd91024767893829017918ab565f224ccd35ecChong Zhang mMidRowStyles[index] = m; 678bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 679bdfd91024767893829017918ab565f224ccd35ecChong Zhang 680bdfd91024767893829017918ab565f224ccd35ecChong Zhang void setPACAt(int index, PAC pac) { 681bdfd91024767893829017918ab565f224ccd35ecChong Zhang mPACStyles[index] = pac; 682bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 683bdfd91024767893829017918ab565f224ccd35ecChong Zhang 684bdfd91024767893829017918ab565f224ccd35ecChong Zhang char charAt(int index) { 685bdfd91024767893829017918ab565f224ccd35ecChong Zhang return mDisplayChars.charAt(index); 686bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 687bdfd91024767893829017918ab565f224ccd35ecChong Zhang 688bdfd91024767893829017918ab565f224ccd35ecChong Zhang int length() { 689bdfd91024767893829017918ab565f224ccd35ecChong Zhang return mDisplayChars.length(); 690bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 691bdfd91024767893829017918ab565f224ccd35ecChong Zhang 692bdfd91024767893829017918ab565f224ccd35ecChong Zhang void applyStyleSpan( 693bdfd91024767893829017918ab565f224ccd35ecChong Zhang SpannableStringBuilder styledText, 694bdfd91024767893829017918ab565f224ccd35ecChong Zhang StyleCode s, int start, int end) { 695bdfd91024767893829017918ab565f224ccd35ecChong Zhang if (s.isItalics()) { 696bdfd91024767893829017918ab565f224ccd35ecChong Zhang styledText.setSpan( 697bdfd91024767893829017918ab565f224ccd35ecChong Zhang new StyleSpan(android.graphics.Typeface.ITALIC), 698bdfd91024767893829017918ab565f224ccd35ecChong Zhang start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 699bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 70019ed4a179ccea6915f1852139d520dddba779490Chong Zhang if (s.isUnderline()) { 70119ed4a179ccea6915f1852139d520dddba779490Chong Zhang styledText.setSpan( 70219ed4a179ccea6915f1852139d520dddba779490Chong Zhang new UnderlineSpan(), 70319ed4a179ccea6915f1852139d520dddba779490Chong Zhang start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 70419ed4a179ccea6915f1852139d520dddba779490Chong Zhang } 705bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 706bdfd91024767893829017918ab565f224ccd35ecChong Zhang 707bdfd91024767893829017918ab565f224ccd35ecChong Zhang SpannableStringBuilder getStyledText(CaptionStyle captionStyle) { 708bdfd91024767893829017918ab565f224ccd35ecChong Zhang SpannableStringBuilder styledText = new SpannableStringBuilder(mDisplayChars); 709bdfd91024767893829017918ab565f224ccd35ecChong Zhang int start = -1, next = 0; 710bdfd91024767893829017918ab565f224ccd35ecChong Zhang int styleStart = -1; 711bdfd91024767893829017918ab565f224ccd35ecChong Zhang StyleCode curStyle = null; 712bdfd91024767893829017918ab565f224ccd35ecChong Zhang while (next < mDisplayChars.length()) { 713bdfd91024767893829017918ab565f224ccd35ecChong Zhang StyleCode newStyle = null; 714bdfd91024767893829017918ab565f224ccd35ecChong Zhang if (mMidRowStyles[next] != null) { 715bdfd91024767893829017918ab565f224ccd35ecChong Zhang // apply mid-row style change 716bdfd91024767893829017918ab565f224ccd35ecChong Zhang newStyle = mMidRowStyles[next]; 717bdfd91024767893829017918ab565f224ccd35ecChong Zhang } else if (mPACStyles[next] != null 718bdfd91024767893829017918ab565f224ccd35ecChong Zhang && (styleStart < 0 || start < 0)) { 719bdfd91024767893829017918ab565f224ccd35ecChong Zhang // apply PAC style change, only if: 720bdfd91024767893829017918ab565f224ccd35ecChong Zhang // 1. no style set, or 721bdfd91024767893829017918ab565f224ccd35ecChong Zhang // 2. style set, but prev char is none-displayable 722bdfd91024767893829017918ab565f224ccd35ecChong Zhang newStyle = mPACStyles[next]; 723bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 724bdfd91024767893829017918ab565f224ccd35ecChong Zhang if (newStyle != null) { 725bdfd91024767893829017918ab565f224ccd35ecChong Zhang curStyle = newStyle; 726bdfd91024767893829017918ab565f224ccd35ecChong Zhang if (styleStart >= 0 && start >= 0) { 727bdfd91024767893829017918ab565f224ccd35ecChong Zhang applyStyleSpan(styledText, newStyle, styleStart, next); 728bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 729bdfd91024767893829017918ab565f224ccd35ecChong Zhang styleStart = next; 730bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 731bdfd91024767893829017918ab565f224ccd35ecChong Zhang 732bdfd91024767893829017918ab565f224ccd35ecChong Zhang if (mDisplayChars.charAt(next) != TS) { 733bdfd91024767893829017918ab565f224ccd35ecChong Zhang if (start < 0) { 734bdfd91024767893829017918ab565f224ccd35ecChong Zhang start = next; 735bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 736bdfd91024767893829017918ab565f224ccd35ecChong Zhang } else if (start >= 0) { 737bdfd91024767893829017918ab565f224ccd35ecChong Zhang int expandedStart = mDisplayChars.charAt(start) == ' ' ? start : start - 1; 738bdfd91024767893829017918ab565f224ccd35ecChong Zhang int expandedEnd = mDisplayChars.charAt(next - 1) == ' ' ? next : next + 1; 739bdfd91024767893829017918ab565f224ccd35ecChong Zhang styledText.setSpan( 74019ed4a179ccea6915f1852139d520dddba779490Chong Zhang new MutableBackgroundColorSpan(captionStyle.backgroundColor), 741bdfd91024767893829017918ab565f224ccd35ecChong Zhang expandedStart, expandedEnd, 742bdfd91024767893829017918ab565f224ccd35ecChong Zhang Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 743bdfd91024767893829017918ab565f224ccd35ecChong Zhang if (styleStart >= 0) { 744bdfd91024767893829017918ab565f224ccd35ecChong Zhang applyStyleSpan(styledText, curStyle, styleStart, expandedEnd); 745bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 746bdfd91024767893829017918ab565f224ccd35ecChong Zhang start = -1; 747bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 748bdfd91024767893829017918ab565f224ccd35ecChong Zhang next++; 749bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 750bdfd91024767893829017918ab565f224ccd35ecChong Zhang 751bdfd91024767893829017918ab565f224ccd35ecChong Zhang return styledText; 752bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 753bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 754bdfd91024767893829017918ab565f224ccd35ecChong Zhang 755bdfd91024767893829017918ab565f224ccd35ecChong Zhang /* 756bdfd91024767893829017918ab565f224ccd35ecChong Zhang * CCMemory models a console-style display. 757bdfd91024767893829017918ab565f224ccd35ecChong Zhang */ 758bdfd91024767893829017918ab565f224ccd35ecChong Zhang private static class CCMemory { 759bdfd91024767893829017918ab565f224ccd35ecChong Zhang private final String mBlankLine; 760bdfd91024767893829017918ab565f224ccd35ecChong Zhang private final CCLineBuilder[] mLines = new CCLineBuilder[MAX_ROWS + 2]; 761bdfd91024767893829017918ab565f224ccd35ecChong Zhang private int mRow; 762bdfd91024767893829017918ab565f224ccd35ecChong Zhang private int mCol; 763bdfd91024767893829017918ab565f224ccd35ecChong Zhang 764bdfd91024767893829017918ab565f224ccd35ecChong Zhang CCMemory() { 765bdfd91024767893829017918ab565f224ccd35ecChong Zhang char[] blank = new char[MAX_COLS + 2]; 766bdfd91024767893829017918ab565f224ccd35ecChong Zhang Arrays.fill(blank, TS); 767bdfd91024767893829017918ab565f224ccd35ecChong Zhang mBlankLine = new String(blank); 768bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 769bdfd91024767893829017918ab565f224ccd35ecChong Zhang 770bdfd91024767893829017918ab565f224ccd35ecChong Zhang void erase() { 771bdfd91024767893829017918ab565f224ccd35ecChong Zhang // erase all lines 772bdfd91024767893829017918ab565f224ccd35ecChong Zhang for (int i = 0; i < mLines.length; i++) { 773bdfd91024767893829017918ab565f224ccd35ecChong Zhang mLines[i] = null; 774bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 775bdfd91024767893829017918ab565f224ccd35ecChong Zhang mRow = MAX_ROWS; 776bdfd91024767893829017918ab565f224ccd35ecChong Zhang mCol = 1; 777bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 778bdfd91024767893829017918ab565f224ccd35ecChong Zhang 779bdfd91024767893829017918ab565f224ccd35ecChong Zhang void der() { 780bdfd91024767893829017918ab565f224ccd35ecChong Zhang if (mLines[mRow] != null) { 781bdfd91024767893829017918ab565f224ccd35ecChong Zhang for (int i = 0; i < mCol; i++) { 782bdfd91024767893829017918ab565f224ccd35ecChong Zhang if (mLines[mRow].charAt(i) != TS) { 783bdfd91024767893829017918ab565f224ccd35ecChong Zhang for (int j = mCol; j < mLines[mRow].length(); j++) { 784bdfd91024767893829017918ab565f224ccd35ecChong Zhang mLines[j].setCharAt(j, TS); 785bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 786bdfd91024767893829017918ab565f224ccd35ecChong Zhang return; 787bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 788bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 789bdfd91024767893829017918ab565f224ccd35ecChong Zhang mLines[mRow] = null; 790bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 791bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 792bdfd91024767893829017918ab565f224ccd35ecChong Zhang 793bdfd91024767893829017918ab565f224ccd35ecChong Zhang void tab(int tabs) { 794bdfd91024767893829017918ab565f224ccd35ecChong Zhang moveCursorByCol(tabs); 795bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 796bdfd91024767893829017918ab565f224ccd35ecChong Zhang 797bdfd91024767893829017918ab565f224ccd35ecChong Zhang void bs() { 798bdfd91024767893829017918ab565f224ccd35ecChong Zhang moveCursorByCol(-1); 799bdfd91024767893829017918ab565f224ccd35ecChong Zhang if (mLines[mRow] != null) { 800bdfd91024767893829017918ab565f224ccd35ecChong Zhang mLines[mRow].setCharAt(mCol, TS); 801bdfd91024767893829017918ab565f224ccd35ecChong Zhang if (mCol == MAX_COLS - 1) { 802bdfd91024767893829017918ab565f224ccd35ecChong Zhang // Spec recommendation: 803bdfd91024767893829017918ab565f224ccd35ecChong Zhang // if cursor was at col 32, move cursor 804bdfd91024767893829017918ab565f224ccd35ecChong Zhang // back to col 31 and erase both col 31&32 805bdfd91024767893829017918ab565f224ccd35ecChong Zhang mLines[mRow].setCharAt(MAX_COLS, TS); 806bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 807bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 808bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 809bdfd91024767893829017918ab565f224ccd35ecChong Zhang 810bdfd91024767893829017918ab565f224ccd35ecChong Zhang void cr() { 811bdfd91024767893829017918ab565f224ccd35ecChong Zhang moveCursorTo(mRow + 1, 1); 812bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 813bdfd91024767893829017918ab565f224ccd35ecChong Zhang 814bdfd91024767893829017918ab565f224ccd35ecChong Zhang void rollUp(int windowSize) { 815bdfd91024767893829017918ab565f224ccd35ecChong Zhang int i; 816bdfd91024767893829017918ab565f224ccd35ecChong Zhang for (i = 0; i <= mRow - windowSize; i++) { 817bdfd91024767893829017918ab565f224ccd35ecChong Zhang mLines[i] = null; 818bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 819bdfd91024767893829017918ab565f224ccd35ecChong Zhang int startRow = mRow - windowSize + 1; 820bdfd91024767893829017918ab565f224ccd35ecChong Zhang if (startRow < 1) { 821bdfd91024767893829017918ab565f224ccd35ecChong Zhang startRow = 1; 822bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 823bdfd91024767893829017918ab565f224ccd35ecChong Zhang for (i = startRow; i < mRow; i++) { 824bdfd91024767893829017918ab565f224ccd35ecChong Zhang mLines[i] = mLines[i + 1]; 825bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 826bdfd91024767893829017918ab565f224ccd35ecChong Zhang for (i = mRow; i < mLines.length; i++) { 827bdfd91024767893829017918ab565f224ccd35ecChong Zhang // clear base row 828bdfd91024767893829017918ab565f224ccd35ecChong Zhang mLines[i] = null; 829bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 830bdfd91024767893829017918ab565f224ccd35ecChong Zhang // default to col 1, in case PAC is not sent 831bdfd91024767893829017918ab565f224ccd35ecChong Zhang mCol = 1; 832bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 833bdfd91024767893829017918ab565f224ccd35ecChong Zhang 834bdfd91024767893829017918ab565f224ccd35ecChong Zhang void writeText(String text) { 835bdfd91024767893829017918ab565f224ccd35ecChong Zhang for (int i = 0; i < text.length(); i++) { 836bdfd91024767893829017918ab565f224ccd35ecChong Zhang getLineBuffer(mRow).setCharAt(mCol, text.charAt(i)); 837bdfd91024767893829017918ab565f224ccd35ecChong Zhang moveCursorByCol(1); 838bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 839bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 840bdfd91024767893829017918ab565f224ccd35ecChong Zhang 841bdfd91024767893829017918ab565f224ccd35ecChong Zhang void writeMidRowCode(StyleCode m) { 842bdfd91024767893829017918ab565f224ccd35ecChong Zhang getLineBuffer(mRow).setMidRowAt(mCol, m); 843bdfd91024767893829017918ab565f224ccd35ecChong Zhang moveCursorByCol(1); 844bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 845bdfd91024767893829017918ab565f224ccd35ecChong Zhang 846bdfd91024767893829017918ab565f224ccd35ecChong Zhang void writePAC(PAC pac) { 847bdfd91024767893829017918ab565f224ccd35ecChong Zhang if (pac.isIndentPAC()) { 848bdfd91024767893829017918ab565f224ccd35ecChong Zhang moveCursorTo(pac.getRow(), pac.getCol()); 849bdfd91024767893829017918ab565f224ccd35ecChong Zhang } else { 8506286f8ec05ff213f52169a6828aaae3ffd1f20caChong Zhang moveCursorTo(pac.getRow(), 1); 851bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 852bdfd91024767893829017918ab565f224ccd35ecChong Zhang getLineBuffer(mRow).setPACAt(mCol, pac); 853bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 854bdfd91024767893829017918ab565f224ccd35ecChong Zhang 855bdfd91024767893829017918ab565f224ccd35ecChong Zhang SpannableStringBuilder[] getStyledText(CaptionStyle captionStyle) { 856978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung ArrayList<SpannableStringBuilder> rows = new ArrayList<>(MAX_ROWS); 857bdfd91024767893829017918ab565f224ccd35ecChong Zhang for (int i = 1; i <= MAX_ROWS; i++) { 858bdfd91024767893829017918ab565f224ccd35ecChong Zhang rows.add(mLines[i] != null ? 859bdfd91024767893829017918ab565f224ccd35ecChong Zhang mLines[i].getStyledText(captionStyle) : null); 860bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 861bdfd91024767893829017918ab565f224ccd35ecChong Zhang return rows.toArray(new SpannableStringBuilder[MAX_ROWS]); 862bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 863bdfd91024767893829017918ab565f224ccd35ecChong Zhang 864bdfd91024767893829017918ab565f224ccd35ecChong Zhang private static int clamp(int x, int min, int max) { 865bdfd91024767893829017918ab565f224ccd35ecChong Zhang return x < min ? min : (x > max ? max : x); 866bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 867bdfd91024767893829017918ab565f224ccd35ecChong Zhang 868bdfd91024767893829017918ab565f224ccd35ecChong Zhang private void moveCursorTo(int row, int col) { 869bdfd91024767893829017918ab565f224ccd35ecChong Zhang mRow = clamp(row, 1, MAX_ROWS); 870bdfd91024767893829017918ab565f224ccd35ecChong Zhang mCol = clamp(col, 1, MAX_COLS); 871bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 872bdfd91024767893829017918ab565f224ccd35ecChong Zhang 873bdfd91024767893829017918ab565f224ccd35ecChong Zhang private void moveCursorToRow(int row) { 874bdfd91024767893829017918ab565f224ccd35ecChong Zhang mRow = clamp(row, 1, MAX_ROWS); 875bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 876bdfd91024767893829017918ab565f224ccd35ecChong Zhang 877bdfd91024767893829017918ab565f224ccd35ecChong Zhang private void moveCursorByCol(int col) { 878bdfd91024767893829017918ab565f224ccd35ecChong Zhang mCol = clamp(mCol + col, 1, MAX_COLS); 879bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 880bdfd91024767893829017918ab565f224ccd35ecChong Zhang 881bdfd91024767893829017918ab565f224ccd35ecChong Zhang private void moveBaselineTo(int baseRow, int windowSize) { 882bdfd91024767893829017918ab565f224ccd35ecChong Zhang if (mRow == baseRow) { 883bdfd91024767893829017918ab565f224ccd35ecChong Zhang return; 884bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 885bdfd91024767893829017918ab565f224ccd35ecChong Zhang int actualWindowSize = windowSize; 886bdfd91024767893829017918ab565f224ccd35ecChong Zhang if (baseRow < actualWindowSize) { 887bdfd91024767893829017918ab565f224ccd35ecChong Zhang actualWindowSize = baseRow; 888bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 889bdfd91024767893829017918ab565f224ccd35ecChong Zhang if (mRow < actualWindowSize) { 890bdfd91024767893829017918ab565f224ccd35ecChong Zhang actualWindowSize = mRow; 891bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 892bdfd91024767893829017918ab565f224ccd35ecChong Zhang 893bdfd91024767893829017918ab565f224ccd35ecChong Zhang int i; 894bdfd91024767893829017918ab565f224ccd35ecChong Zhang if (baseRow < mRow) { 895bdfd91024767893829017918ab565f224ccd35ecChong Zhang // copy from bottom to top row 896bdfd91024767893829017918ab565f224ccd35ecChong Zhang for (i = actualWindowSize - 1; i >= 0; i--) { 897bdfd91024767893829017918ab565f224ccd35ecChong Zhang mLines[baseRow - i] = mLines[mRow - i]; 898bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 899bdfd91024767893829017918ab565f224ccd35ecChong Zhang } else { 900bdfd91024767893829017918ab565f224ccd35ecChong Zhang // copy from top to bottom row 901bdfd91024767893829017918ab565f224ccd35ecChong Zhang for (i = 0; i < actualWindowSize; i++) { 902bdfd91024767893829017918ab565f224ccd35ecChong Zhang mLines[baseRow - i] = mLines[mRow - i]; 903bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 904bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 905bdfd91024767893829017918ab565f224ccd35ecChong Zhang // clear rest of the rows 906bdfd91024767893829017918ab565f224ccd35ecChong Zhang for (i = 0; i <= baseRow - windowSize; i++) { 907bdfd91024767893829017918ab565f224ccd35ecChong Zhang mLines[i] = null; 908bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 909bdfd91024767893829017918ab565f224ccd35ecChong Zhang for (i = baseRow + 1; i < mLines.length; i++) { 910bdfd91024767893829017918ab565f224ccd35ecChong Zhang mLines[i] = null; 911bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 912bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 913bdfd91024767893829017918ab565f224ccd35ecChong Zhang 914bdfd91024767893829017918ab565f224ccd35ecChong Zhang private CCLineBuilder getLineBuffer(int row) { 915bdfd91024767893829017918ab565f224ccd35ecChong Zhang if (mLines[row] == null) { 916bdfd91024767893829017918ab565f224ccd35ecChong Zhang mLines[row] = new CCLineBuilder(mBlankLine); 917bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 918bdfd91024767893829017918ab565f224ccd35ecChong Zhang return mLines[row]; 919bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 920bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 921bdfd91024767893829017918ab565f224ccd35ecChong Zhang 922bdfd91024767893829017918ab565f224ccd35ecChong Zhang /* 923bdfd91024767893829017918ab565f224ccd35ecChong Zhang * CCData parses the raw CC byte pair into displayable chars, 924bdfd91024767893829017918ab565f224ccd35ecChong Zhang * misc control codes, Mid-Row or Preamble Address Codes. 925bdfd91024767893829017918ab565f224ccd35ecChong Zhang */ 926bdfd91024767893829017918ab565f224ccd35ecChong Zhang private static class CCData { 927bdfd91024767893829017918ab565f224ccd35ecChong Zhang private final byte mType; 928bdfd91024767893829017918ab565f224ccd35ecChong Zhang private final byte mData1; 929bdfd91024767893829017918ab565f224ccd35ecChong Zhang private final byte mData2; 930bdfd91024767893829017918ab565f224ccd35ecChong Zhang 931bdfd91024767893829017918ab565f224ccd35ecChong Zhang private static final String[] mCtrlCodeMap = { 932bdfd91024767893829017918ab565f224ccd35ecChong Zhang "RCL", "BS" , "AOF", "AON", 933bdfd91024767893829017918ab565f224ccd35ecChong Zhang "DER", "RU2", "RU3", "RU4", 934bdfd91024767893829017918ab565f224ccd35ecChong Zhang "FON", "RDC", "TR" , "RTD", 935bdfd91024767893829017918ab565f224ccd35ecChong Zhang "EDM", "CR" , "ENM", "EOC", 936bdfd91024767893829017918ab565f224ccd35ecChong Zhang }; 937bdfd91024767893829017918ab565f224ccd35ecChong Zhang 938bdfd91024767893829017918ab565f224ccd35ecChong Zhang private static final String[] mSpecialCharMap = { 939bdfd91024767893829017918ab565f224ccd35ecChong Zhang "\u00AE", 940bdfd91024767893829017918ab565f224ccd35ecChong Zhang "\u00B0", 941bdfd91024767893829017918ab565f224ccd35ecChong Zhang "\u00BD", 942bdfd91024767893829017918ab565f224ccd35ecChong Zhang "\u00BF", 943bdfd91024767893829017918ab565f224ccd35ecChong Zhang "\u2122", 944bdfd91024767893829017918ab565f224ccd35ecChong Zhang "\u00A2", 945bdfd91024767893829017918ab565f224ccd35ecChong Zhang "\u00A3", 946bdfd91024767893829017918ab565f224ccd35ecChong Zhang "\u266A", // Eighth note 947bdfd91024767893829017918ab565f224ccd35ecChong Zhang "\u00E0", 948bdfd91024767893829017918ab565f224ccd35ecChong Zhang "\u00A0", // Transparent space 949bdfd91024767893829017918ab565f224ccd35ecChong Zhang "\u00E8", 950bdfd91024767893829017918ab565f224ccd35ecChong Zhang "\u00E2", 951bdfd91024767893829017918ab565f224ccd35ecChong Zhang "\u00EA", 952bdfd91024767893829017918ab565f224ccd35ecChong Zhang "\u00EE", 953bdfd91024767893829017918ab565f224ccd35ecChong Zhang "\u00F4", 954bdfd91024767893829017918ab565f224ccd35ecChong Zhang "\u00FB", 955bdfd91024767893829017918ab565f224ccd35ecChong Zhang }; 956bdfd91024767893829017918ab565f224ccd35ecChong Zhang 957bdfd91024767893829017918ab565f224ccd35ecChong Zhang private static final String[] mSpanishCharMap = { 958bdfd91024767893829017918ab565f224ccd35ecChong Zhang // Spanish and misc chars 959bdfd91024767893829017918ab565f224ccd35ecChong Zhang "\u00C1", // A 960bdfd91024767893829017918ab565f224ccd35ecChong Zhang "\u00C9", // E 961bdfd91024767893829017918ab565f224ccd35ecChong Zhang "\u00D3", // I 962bdfd91024767893829017918ab565f224ccd35ecChong Zhang "\u00DA", // O 963bdfd91024767893829017918ab565f224ccd35ecChong Zhang "\u00DC", // U 964bdfd91024767893829017918ab565f224ccd35ecChong Zhang "\u00FC", // u 965bdfd91024767893829017918ab565f224ccd35ecChong Zhang "\u2018", // opening single quote 966bdfd91024767893829017918ab565f224ccd35ecChong Zhang "\u00A1", // inverted exclamation mark 967bdfd91024767893829017918ab565f224ccd35ecChong Zhang "*", 968bdfd91024767893829017918ab565f224ccd35ecChong Zhang "'", 969bdfd91024767893829017918ab565f224ccd35ecChong Zhang "\u2014", // em dash 970bdfd91024767893829017918ab565f224ccd35ecChong Zhang "\u00A9", // Copyright 971bdfd91024767893829017918ab565f224ccd35ecChong Zhang "\u2120", // Servicemark 972bdfd91024767893829017918ab565f224ccd35ecChong Zhang "\u2022", // round bullet 973bdfd91024767893829017918ab565f224ccd35ecChong Zhang "\u201C", // opening double quote 974bdfd91024767893829017918ab565f224ccd35ecChong Zhang "\u201D", // closing double quote 975bdfd91024767893829017918ab565f224ccd35ecChong Zhang // French 976bdfd91024767893829017918ab565f224ccd35ecChong Zhang "\u00C0", 977bdfd91024767893829017918ab565f224ccd35ecChong Zhang "\u00C2", 978bdfd91024767893829017918ab565f224ccd35ecChong Zhang "\u00C7", 979bdfd91024767893829017918ab565f224ccd35ecChong Zhang "\u00C8", 980bdfd91024767893829017918ab565f224ccd35ecChong Zhang "\u00CA", 981bdfd91024767893829017918ab565f224ccd35ecChong Zhang "\u00CB", 982bdfd91024767893829017918ab565f224ccd35ecChong Zhang "\u00EB", 983bdfd91024767893829017918ab565f224ccd35ecChong Zhang "\u00CE", 984bdfd91024767893829017918ab565f224ccd35ecChong Zhang "\u00CF", 985bdfd91024767893829017918ab565f224ccd35ecChong Zhang "\u00EF", 986bdfd91024767893829017918ab565f224ccd35ecChong Zhang "\u00D4", 987bdfd91024767893829017918ab565f224ccd35ecChong Zhang "\u00D9", 988bdfd91024767893829017918ab565f224ccd35ecChong Zhang "\u00F9", 989bdfd91024767893829017918ab565f224ccd35ecChong Zhang "\u00DB", 990bdfd91024767893829017918ab565f224ccd35ecChong Zhang "\u00AB", 991bdfd91024767893829017918ab565f224ccd35ecChong Zhang "\u00BB" 992bdfd91024767893829017918ab565f224ccd35ecChong Zhang }; 993bdfd91024767893829017918ab565f224ccd35ecChong Zhang 994bdfd91024767893829017918ab565f224ccd35ecChong Zhang private static final String[] mProtugueseCharMap = { 995bdfd91024767893829017918ab565f224ccd35ecChong Zhang // Portuguese 996bdfd91024767893829017918ab565f224ccd35ecChong Zhang "\u00C3", 997bdfd91024767893829017918ab565f224ccd35ecChong Zhang "\u00E3", 998bdfd91024767893829017918ab565f224ccd35ecChong Zhang "\u00CD", 999bdfd91024767893829017918ab565f224ccd35ecChong Zhang "\u00CC", 1000bdfd91024767893829017918ab565f224ccd35ecChong Zhang "\u00EC", 1001bdfd91024767893829017918ab565f224ccd35ecChong Zhang "\u00D2", 1002bdfd91024767893829017918ab565f224ccd35ecChong Zhang "\u00F2", 1003bdfd91024767893829017918ab565f224ccd35ecChong Zhang "\u00D5", 1004bdfd91024767893829017918ab565f224ccd35ecChong Zhang "\u00F5", 1005bdfd91024767893829017918ab565f224ccd35ecChong Zhang "{", 1006bdfd91024767893829017918ab565f224ccd35ecChong Zhang "}", 1007bdfd91024767893829017918ab565f224ccd35ecChong Zhang "\\", 1008bdfd91024767893829017918ab565f224ccd35ecChong Zhang "^", 1009bdfd91024767893829017918ab565f224ccd35ecChong Zhang "_", 1010bdfd91024767893829017918ab565f224ccd35ecChong Zhang "|", 1011bdfd91024767893829017918ab565f224ccd35ecChong Zhang "~", 1012bdfd91024767893829017918ab565f224ccd35ecChong Zhang // German and misc chars 1013bdfd91024767893829017918ab565f224ccd35ecChong Zhang "\u00C4", 1014bdfd91024767893829017918ab565f224ccd35ecChong Zhang "\u00E4", 1015bdfd91024767893829017918ab565f224ccd35ecChong Zhang "\u00D6", 1016bdfd91024767893829017918ab565f224ccd35ecChong Zhang "\u00F6", 1017bdfd91024767893829017918ab565f224ccd35ecChong Zhang "\u00DF", 1018bdfd91024767893829017918ab565f224ccd35ecChong Zhang "\u00A5", 1019bdfd91024767893829017918ab565f224ccd35ecChong Zhang "\u00A4", 1020bdfd91024767893829017918ab565f224ccd35ecChong Zhang "\u2502", // vertical bar 1021bdfd91024767893829017918ab565f224ccd35ecChong Zhang "\u00C5", 1022bdfd91024767893829017918ab565f224ccd35ecChong Zhang "\u00E5", 1023bdfd91024767893829017918ab565f224ccd35ecChong Zhang "\u00D8", 1024bdfd91024767893829017918ab565f224ccd35ecChong Zhang "\u00F8", 1025bdfd91024767893829017918ab565f224ccd35ecChong Zhang "\u250C", // top-left corner 1026bdfd91024767893829017918ab565f224ccd35ecChong Zhang "\u2510", // top-right corner 1027bdfd91024767893829017918ab565f224ccd35ecChong Zhang "\u2514", // lower-left corner 1028bdfd91024767893829017918ab565f224ccd35ecChong Zhang "\u2518", // lower-right corner 1029bdfd91024767893829017918ab565f224ccd35ecChong Zhang }; 1030bdfd91024767893829017918ab565f224ccd35ecChong Zhang 1031bdfd91024767893829017918ab565f224ccd35ecChong Zhang static CCData[] fromByteArray(byte[] data) { 1032bdfd91024767893829017918ab565f224ccd35ecChong Zhang CCData[] ccData = new CCData[data.length / 3]; 1033bdfd91024767893829017918ab565f224ccd35ecChong Zhang 1034bdfd91024767893829017918ab565f224ccd35ecChong Zhang for (int i = 0; i < ccData.length; i++) { 1035bdfd91024767893829017918ab565f224ccd35ecChong Zhang ccData[i] = new CCData( 1036bdfd91024767893829017918ab565f224ccd35ecChong Zhang data[i * 3], 1037bdfd91024767893829017918ab565f224ccd35ecChong Zhang data[i * 3 + 1], 1038bdfd91024767893829017918ab565f224ccd35ecChong Zhang data[i * 3 + 2]); 1039bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 1040bdfd91024767893829017918ab565f224ccd35ecChong Zhang 1041bdfd91024767893829017918ab565f224ccd35ecChong Zhang return ccData; 1042bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 1043bdfd91024767893829017918ab565f224ccd35ecChong Zhang 1044bdfd91024767893829017918ab565f224ccd35ecChong Zhang CCData(byte type, byte data1, byte data2) { 1045bdfd91024767893829017918ab565f224ccd35ecChong Zhang mType = type; 1046bdfd91024767893829017918ab565f224ccd35ecChong Zhang mData1 = data1; 1047bdfd91024767893829017918ab565f224ccd35ecChong Zhang mData2 = data2; 1048bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 1049bdfd91024767893829017918ab565f224ccd35ecChong Zhang 1050bdfd91024767893829017918ab565f224ccd35ecChong Zhang int getCtrlCode() { 1051bdfd91024767893829017918ab565f224ccd35ecChong Zhang if ((mData1 == 0x14 || mData1 == 0x1c) 1052bdfd91024767893829017918ab565f224ccd35ecChong Zhang && mData2 >= 0x20 && mData2 <= 0x2f) { 1053bdfd91024767893829017918ab565f224ccd35ecChong Zhang return mData2; 1054bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 1055bdfd91024767893829017918ab565f224ccd35ecChong Zhang return INVALID; 1056bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 1057bdfd91024767893829017918ab565f224ccd35ecChong Zhang 1058bdfd91024767893829017918ab565f224ccd35ecChong Zhang StyleCode getMidRow() { 1059bdfd91024767893829017918ab565f224ccd35ecChong Zhang // only support standard Mid-row codes, ignore 1060bdfd91024767893829017918ab565f224ccd35ecChong Zhang // optional background/foreground mid-row codes 1061bdfd91024767893829017918ab565f224ccd35ecChong Zhang if ((mData1 == 0x11 || mData1 == 0x19) 1062bdfd91024767893829017918ab565f224ccd35ecChong Zhang && mData2 >= 0x20 && mData2 <= 0x2f) { 1063bdfd91024767893829017918ab565f224ccd35ecChong Zhang return StyleCode.fromByte(mData2); 1064bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 1065bdfd91024767893829017918ab565f224ccd35ecChong Zhang return null; 1066bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 1067bdfd91024767893829017918ab565f224ccd35ecChong Zhang 1068bdfd91024767893829017918ab565f224ccd35ecChong Zhang PAC getPAC() { 1069bdfd91024767893829017918ab565f224ccd35ecChong Zhang if ((mData1 & 0x70) == 0x10 1070bdfd91024767893829017918ab565f224ccd35ecChong Zhang && (mData2 & 0x40) == 0x40 1071bdfd91024767893829017918ab565f224ccd35ecChong Zhang && ((mData1 & 0x07) != 0 || (mData2 & 0x20) == 0)) { 1072bdfd91024767893829017918ab565f224ccd35ecChong Zhang return PAC.fromBytes(mData1, mData2); 1073bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 1074bdfd91024767893829017918ab565f224ccd35ecChong Zhang return null; 1075bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 1076bdfd91024767893829017918ab565f224ccd35ecChong Zhang 1077bdfd91024767893829017918ab565f224ccd35ecChong Zhang int getTabOffset() { 1078bdfd91024767893829017918ab565f224ccd35ecChong Zhang if ((mData1 == 0x17 || mData1 == 0x1f) 1079bdfd91024767893829017918ab565f224ccd35ecChong Zhang && mData2 >= 0x21 && mData2 <= 0x23) { 1080bdfd91024767893829017918ab565f224ccd35ecChong Zhang return mData2 & 0x3; 1081bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 1082bdfd91024767893829017918ab565f224ccd35ecChong Zhang return 0; 1083bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 1084bdfd91024767893829017918ab565f224ccd35ecChong Zhang 1085bdfd91024767893829017918ab565f224ccd35ecChong Zhang boolean isDisplayableChar() { 1086bdfd91024767893829017918ab565f224ccd35ecChong Zhang return isBasicChar() || isSpecialChar() || isExtendedChar(); 1087bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 1088bdfd91024767893829017918ab565f224ccd35ecChong Zhang 1089bdfd91024767893829017918ab565f224ccd35ecChong Zhang String getDisplayText() { 1090bdfd91024767893829017918ab565f224ccd35ecChong Zhang String str = getBasicChars(); 1091bdfd91024767893829017918ab565f224ccd35ecChong Zhang 1092bdfd91024767893829017918ab565f224ccd35ecChong Zhang if (str == null) { 1093bdfd91024767893829017918ab565f224ccd35ecChong Zhang str = getSpecialChar(); 1094bdfd91024767893829017918ab565f224ccd35ecChong Zhang 1095bdfd91024767893829017918ab565f224ccd35ecChong Zhang if (str == null) { 1096bdfd91024767893829017918ab565f224ccd35ecChong Zhang str = getExtendedChar(); 1097bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 1098bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 1099bdfd91024767893829017918ab565f224ccd35ecChong Zhang 1100bdfd91024767893829017918ab565f224ccd35ecChong Zhang return str; 1101bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 1102bdfd91024767893829017918ab565f224ccd35ecChong Zhang 1103bdfd91024767893829017918ab565f224ccd35ecChong Zhang private String ctrlCodeToString(int ctrlCode) { 1104bdfd91024767893829017918ab565f224ccd35ecChong Zhang return mCtrlCodeMap[ctrlCode - 0x20]; 1105bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 1106bdfd91024767893829017918ab565f224ccd35ecChong Zhang 1107bdfd91024767893829017918ab565f224ccd35ecChong Zhang private boolean isBasicChar() { 1108bdfd91024767893829017918ab565f224ccd35ecChong Zhang return mData1 >= 0x20 && mData1 <= 0x7f; 1109bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 1110bdfd91024767893829017918ab565f224ccd35ecChong Zhang 1111bdfd91024767893829017918ab565f224ccd35ecChong Zhang private boolean isSpecialChar() { 1112bdfd91024767893829017918ab565f224ccd35ecChong Zhang return ((mData1 == 0x11 || mData1 == 0x19) 1113bdfd91024767893829017918ab565f224ccd35ecChong Zhang && mData2 >= 0x30 && mData2 <= 0x3f); 1114bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 1115bdfd91024767893829017918ab565f224ccd35ecChong Zhang 1116bdfd91024767893829017918ab565f224ccd35ecChong Zhang private boolean isExtendedChar() { 1117bdfd91024767893829017918ab565f224ccd35ecChong Zhang return ((mData1 == 0x12 || mData1 == 0x1A 1118bdfd91024767893829017918ab565f224ccd35ecChong Zhang || mData1 == 0x13 || mData1 == 0x1B) 1119bdfd91024767893829017918ab565f224ccd35ecChong Zhang && mData2 >= 0x20 && mData2 <= 0x3f); 1120bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 1121bdfd91024767893829017918ab565f224ccd35ecChong Zhang 1122bdfd91024767893829017918ab565f224ccd35ecChong Zhang private char getBasicChar(byte data) { 1123bdfd91024767893829017918ab565f224ccd35ecChong Zhang char c; 1124bdfd91024767893829017918ab565f224ccd35ecChong Zhang // replace the non-ASCII ones 1125bdfd91024767893829017918ab565f224ccd35ecChong Zhang switch (data) { 1126bdfd91024767893829017918ab565f224ccd35ecChong Zhang case 0x2A: c = '\u00E1'; break; 1127bdfd91024767893829017918ab565f224ccd35ecChong Zhang case 0x5C: c = '\u00E9'; break; 1128bdfd91024767893829017918ab565f224ccd35ecChong Zhang case 0x5E: c = '\u00ED'; break; 1129bdfd91024767893829017918ab565f224ccd35ecChong Zhang case 0x5F: c = '\u00F3'; break; 1130bdfd91024767893829017918ab565f224ccd35ecChong Zhang case 0x60: c = '\u00FA'; break; 1131bdfd91024767893829017918ab565f224ccd35ecChong Zhang case 0x7B: c = '\u00E7'; break; 1132bdfd91024767893829017918ab565f224ccd35ecChong Zhang case 0x7C: c = '\u00F7'; break; 1133bdfd91024767893829017918ab565f224ccd35ecChong Zhang case 0x7D: c = '\u00D1'; break; 1134bdfd91024767893829017918ab565f224ccd35ecChong Zhang case 0x7E: c = '\u00F1'; break; 1135bdfd91024767893829017918ab565f224ccd35ecChong Zhang case 0x7F: c = '\u2588'; break; // Full block 1136bdfd91024767893829017918ab565f224ccd35ecChong Zhang default: c = (char) data; break; 1137bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 1138bdfd91024767893829017918ab565f224ccd35ecChong Zhang return c; 1139bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 1140bdfd91024767893829017918ab565f224ccd35ecChong Zhang 1141bdfd91024767893829017918ab565f224ccd35ecChong Zhang private String getBasicChars() { 1142bdfd91024767893829017918ab565f224ccd35ecChong Zhang if (mData1 >= 0x20 && mData1 <= 0x7f) { 1143bdfd91024767893829017918ab565f224ccd35ecChong Zhang StringBuilder builder = new StringBuilder(2); 1144bdfd91024767893829017918ab565f224ccd35ecChong Zhang builder.append(getBasicChar(mData1)); 1145bdfd91024767893829017918ab565f224ccd35ecChong Zhang if (mData2 >= 0x20 && mData2 <= 0x7f) { 1146bdfd91024767893829017918ab565f224ccd35ecChong Zhang builder.append(getBasicChar(mData2)); 1147bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 1148bdfd91024767893829017918ab565f224ccd35ecChong Zhang return builder.toString(); 1149bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 1150bdfd91024767893829017918ab565f224ccd35ecChong Zhang 1151bdfd91024767893829017918ab565f224ccd35ecChong Zhang return null; 1152bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 1153bdfd91024767893829017918ab565f224ccd35ecChong Zhang 1154bdfd91024767893829017918ab565f224ccd35ecChong Zhang private String getSpecialChar() { 1155bdfd91024767893829017918ab565f224ccd35ecChong Zhang if ((mData1 == 0x11 || mData1 == 0x19) 1156bdfd91024767893829017918ab565f224ccd35ecChong Zhang && mData2 >= 0x30 && mData2 <= 0x3f) { 1157bdfd91024767893829017918ab565f224ccd35ecChong Zhang return mSpecialCharMap[mData2 - 0x30]; 1158bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 1159bdfd91024767893829017918ab565f224ccd35ecChong Zhang 1160bdfd91024767893829017918ab565f224ccd35ecChong Zhang return null; 1161bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 1162bdfd91024767893829017918ab565f224ccd35ecChong Zhang 1163bdfd91024767893829017918ab565f224ccd35ecChong Zhang private String getExtendedChar() { 1164bdfd91024767893829017918ab565f224ccd35ecChong Zhang if ((mData1 == 0x12 || mData1 == 0x1A) 1165bdfd91024767893829017918ab565f224ccd35ecChong Zhang && mData2 >= 0x20 && mData2 <= 0x3f){ 1166bdfd91024767893829017918ab565f224ccd35ecChong Zhang // 1 Spanish/French char 1167bdfd91024767893829017918ab565f224ccd35ecChong Zhang return mSpanishCharMap[mData2 - 0x20]; 1168bdfd91024767893829017918ab565f224ccd35ecChong Zhang } else if ((mData1 == 0x13 || mData1 == 0x1B) 1169bdfd91024767893829017918ab565f224ccd35ecChong Zhang && mData2 >= 0x20 && mData2 <= 0x3f){ 1170bdfd91024767893829017918ab565f224ccd35ecChong Zhang // 1 Portuguese/German/Danish char 1171bdfd91024767893829017918ab565f224ccd35ecChong Zhang return mProtugueseCharMap[mData2 - 0x20]; 1172bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 1173bdfd91024767893829017918ab565f224ccd35ecChong Zhang 1174bdfd91024767893829017918ab565f224ccd35ecChong Zhang return null; 1175bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 1176bdfd91024767893829017918ab565f224ccd35ecChong Zhang 1177bdfd91024767893829017918ab565f224ccd35ecChong Zhang @Override 1178bdfd91024767893829017918ab565f224ccd35ecChong Zhang public String toString() { 1179bdfd91024767893829017918ab565f224ccd35ecChong Zhang String str; 1180bdfd91024767893829017918ab565f224ccd35ecChong Zhang 1181bdfd91024767893829017918ab565f224ccd35ecChong Zhang if (mData1 < 0x10 && mData2 < 0x10) { 1182bdfd91024767893829017918ab565f224ccd35ecChong Zhang // Null Pad, ignore 1183bdfd91024767893829017918ab565f224ccd35ecChong Zhang return String.format("[%d]Null: %02x %02x", mType, mData1, mData2); 1184bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 1185bdfd91024767893829017918ab565f224ccd35ecChong Zhang 1186bdfd91024767893829017918ab565f224ccd35ecChong Zhang int ctrlCode = getCtrlCode(); 1187bdfd91024767893829017918ab565f224ccd35ecChong Zhang if (ctrlCode != INVALID) { 1188bdfd91024767893829017918ab565f224ccd35ecChong Zhang return String.format("[%d]%s", mType, ctrlCodeToString(ctrlCode)); 1189bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 1190bdfd91024767893829017918ab565f224ccd35ecChong Zhang 1191bdfd91024767893829017918ab565f224ccd35ecChong Zhang int tabOffset = getTabOffset(); 1192bdfd91024767893829017918ab565f224ccd35ecChong Zhang if (tabOffset > 0) { 1193bdfd91024767893829017918ab565f224ccd35ecChong Zhang return String.format("[%d]Tab%d", mType, tabOffset); 1194bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 1195bdfd91024767893829017918ab565f224ccd35ecChong Zhang 1196bdfd91024767893829017918ab565f224ccd35ecChong Zhang PAC pac = getPAC(); 1197bdfd91024767893829017918ab565f224ccd35ecChong Zhang if (pac != null) { 1198bdfd91024767893829017918ab565f224ccd35ecChong Zhang return String.format("[%d]PAC: %s", mType, pac.toString()); 1199bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 1200bdfd91024767893829017918ab565f224ccd35ecChong Zhang 1201bdfd91024767893829017918ab565f224ccd35ecChong Zhang StyleCode m = getMidRow(); 1202bdfd91024767893829017918ab565f224ccd35ecChong Zhang if (m != null) { 1203bdfd91024767893829017918ab565f224ccd35ecChong Zhang return String.format("[%d]Mid-row: %s", mType, m.toString()); 1204bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 1205bdfd91024767893829017918ab565f224ccd35ecChong Zhang 1206bdfd91024767893829017918ab565f224ccd35ecChong Zhang if (isDisplayableChar()) { 1207bdfd91024767893829017918ab565f224ccd35ecChong Zhang return String.format("[%d]Displayable: %s (%02x %02x)", 1208bdfd91024767893829017918ab565f224ccd35ecChong Zhang mType, getDisplayText(), mData1, mData2); 1209bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 1210bdfd91024767893829017918ab565f224ccd35ecChong Zhang 1211bdfd91024767893829017918ab565f224ccd35ecChong Zhang return String.format("[%d]Invalid: %02x %02x", mType, mData1, mData2); 1212bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 1213bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 1214bdfd91024767893829017918ab565f224ccd35ecChong Zhang} 1215bdfd91024767893829017918ab565f224ccd35ecChong Zhang 1216bdfd91024767893829017918ab565f224ccd35ecChong Zhang/** 1217bdfd91024767893829017918ab565f224ccd35ecChong Zhang * Widget capable of rendering CEA-608 closed captions. 1218bdfd91024767893829017918ab565f224ccd35ecChong Zhang * 1219bdfd91024767893829017918ab565f224ccd35ecChong Zhang * @hide 1220bdfd91024767893829017918ab565f224ccd35ecChong Zhang */ 1221978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chungclass Cea608CCWidget extends ClosedCaptionWidget implements Cea608CCParser.DisplayListener { 1222bdfd91024767893829017918ab565f224ccd35ecChong Zhang private static final Rect mTextBounds = new Rect(); 1223bdfd91024767893829017918ab565f224ccd35ecChong Zhang private static final String mDummyText = "1234567890123456789012345678901234"; 1224bdfd91024767893829017918ab565f224ccd35ecChong Zhang 1225978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung public Cea608CCWidget(Context context) { 1226bdfd91024767893829017918ab565f224ccd35ecChong Zhang this(context, null); 1227bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 1228bdfd91024767893829017918ab565f224ccd35ecChong Zhang 1229978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung public Cea608CCWidget(Context context, AttributeSet attrs) { 1230978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung this(context, attrs, 0); 1231bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 1232bdfd91024767893829017918ab565f224ccd35ecChong Zhang 1233978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung public Cea608CCWidget(Context context, AttributeSet attrs, int defStyle) { 1234978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung this(context, attrs, defStyle, 0); 1235bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 1236bdfd91024767893829017918ab565f224ccd35ecChong Zhang 1237978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung public Cea608CCWidget(Context context, AttributeSet attrs, int defStyleAttr, 1238978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung int defStyleRes) { 1239978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung super(context, attrs, defStyleAttr, defStyleRes); 1240bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 1241bdfd91024767893829017918ab565f224ccd35ecChong Zhang 1242bdfd91024767893829017918ab565f224ccd35ecChong Zhang @Override 1243978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung public ClosedCaptionLayout createCaptionLayout(Context context) { 1244978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung return new CCLayout(context); 1245bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 1246bdfd91024767893829017918ab565f224ccd35ecChong Zhang 1247bdfd91024767893829017918ab565f224ccd35ecChong Zhang @Override 1248bdfd91024767893829017918ab565f224ccd35ecChong Zhang public void onDisplayChanged(SpannableStringBuilder[] styledTexts) { 1249978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung ((CCLayout) mClosedCaptionLayout).update(styledTexts); 1250bdfd91024767893829017918ab565f224ccd35ecChong Zhang 1251bdfd91024767893829017918ab565f224ccd35ecChong Zhang if (mListener != null) { 1252bdfd91024767893829017918ab565f224ccd35ecChong Zhang mListener.onChanged(this); 1253bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 1254bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 1255bdfd91024767893829017918ab565f224ccd35ecChong Zhang 1256bdfd91024767893829017918ab565f224ccd35ecChong Zhang @Override 1257bdfd91024767893829017918ab565f224ccd35ecChong Zhang public CaptionStyle getCaptionStyle() { 1258bdfd91024767893829017918ab565f224ccd35ecChong Zhang return mCaptionStyle; 1259bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 1260bdfd91024767893829017918ab565f224ccd35ecChong Zhang 1261bdfd91024767893829017918ab565f224ccd35ecChong Zhang private static class CCLineBox extends TextView { 1262bdfd91024767893829017918ab565f224ccd35ecChong Zhang private static final float FONT_PADDING_RATIO = 0.75f; 126319ed4a179ccea6915f1852139d520dddba779490Chong Zhang private static final float EDGE_OUTLINE_RATIO = 0.1f; 126419ed4a179ccea6915f1852139d520dddba779490Chong Zhang private static final float EDGE_SHADOW_RATIO = 0.05f; 126519ed4a179ccea6915f1852139d520dddba779490Chong Zhang private float mOutlineWidth; 126619ed4a179ccea6915f1852139d520dddba779490Chong Zhang private float mShadowRadius; 126719ed4a179ccea6915f1852139d520dddba779490Chong Zhang private float mShadowOffset; 126819ed4a179ccea6915f1852139d520dddba779490Chong Zhang 126919ed4a179ccea6915f1852139d520dddba779490Chong Zhang private int mTextColor = Color.WHITE; 127019ed4a179ccea6915f1852139d520dddba779490Chong Zhang private int mBgColor = Color.BLACK; 127119ed4a179ccea6915f1852139d520dddba779490Chong Zhang private int mEdgeType = CaptionStyle.EDGE_TYPE_NONE; 127219ed4a179ccea6915f1852139d520dddba779490Chong Zhang private int mEdgeColor = Color.TRANSPARENT; 1273bdfd91024767893829017918ab565f224ccd35ecChong Zhang 1274bdfd91024767893829017918ab565f224ccd35ecChong Zhang CCLineBox(Context context) { 1275bdfd91024767893829017918ab565f224ccd35ecChong Zhang super(context); 1276bdfd91024767893829017918ab565f224ccd35ecChong Zhang setGravity(Gravity.CENTER); 1277bdfd91024767893829017918ab565f224ccd35ecChong Zhang setBackgroundColor(Color.TRANSPARENT); 1278bdfd91024767893829017918ab565f224ccd35ecChong Zhang setTextColor(Color.WHITE); 1279bdfd91024767893829017918ab565f224ccd35ecChong Zhang setTypeface(Typeface.MONOSPACE); 1280bdfd91024767893829017918ab565f224ccd35ecChong Zhang setVisibility(View.INVISIBLE); 128119ed4a179ccea6915f1852139d520dddba779490Chong Zhang 128219ed4a179ccea6915f1852139d520dddba779490Chong Zhang final Resources res = getContext().getResources(); 128319ed4a179ccea6915f1852139d520dddba779490Chong Zhang 128419ed4a179ccea6915f1852139d520dddba779490Chong Zhang // get the default (will be updated later during measure) 128519ed4a179ccea6915f1852139d520dddba779490Chong Zhang mOutlineWidth = res.getDimensionPixelSize( 128619ed4a179ccea6915f1852139d520dddba779490Chong Zhang com.android.internal.R.dimen.subtitle_outline_width); 128719ed4a179ccea6915f1852139d520dddba779490Chong Zhang mShadowRadius = res.getDimensionPixelSize( 128819ed4a179ccea6915f1852139d520dddba779490Chong Zhang com.android.internal.R.dimen.subtitle_shadow_radius); 128919ed4a179ccea6915f1852139d520dddba779490Chong Zhang mShadowOffset = res.getDimensionPixelSize( 129019ed4a179ccea6915f1852139d520dddba779490Chong Zhang com.android.internal.R.dimen.subtitle_shadow_offset); 1291bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 1292bdfd91024767893829017918ab565f224ccd35ecChong Zhang 1293bdfd91024767893829017918ab565f224ccd35ecChong Zhang void setCaptionStyle(CaptionStyle captionStyle) { 129419ed4a179ccea6915f1852139d520dddba779490Chong Zhang mTextColor = captionStyle.foregroundColor; 129519ed4a179ccea6915f1852139d520dddba779490Chong Zhang mBgColor = captionStyle.backgroundColor; 129619ed4a179ccea6915f1852139d520dddba779490Chong Zhang mEdgeType = captionStyle.edgeType; 129719ed4a179ccea6915f1852139d520dddba779490Chong Zhang mEdgeColor = captionStyle.edgeColor; 129819ed4a179ccea6915f1852139d520dddba779490Chong Zhang 129919ed4a179ccea6915f1852139d520dddba779490Chong Zhang setTextColor(mTextColor); 130019ed4a179ccea6915f1852139d520dddba779490Chong Zhang if (mEdgeType == CaptionStyle.EDGE_TYPE_DROP_SHADOW) { 130119ed4a179ccea6915f1852139d520dddba779490Chong Zhang setShadowLayer(mShadowRadius, mShadowOffset, mShadowOffset, mEdgeColor); 130219ed4a179ccea6915f1852139d520dddba779490Chong Zhang } else { 130319ed4a179ccea6915f1852139d520dddba779490Chong Zhang setShadowLayer(0, 0, 0, 0); 130419ed4a179ccea6915f1852139d520dddba779490Chong Zhang } 130519ed4a179ccea6915f1852139d520dddba779490Chong Zhang invalidate(); 1306bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 1307bdfd91024767893829017918ab565f224ccd35ecChong Zhang 1308bdfd91024767893829017918ab565f224ccd35ecChong Zhang @Override 1309bdfd91024767893829017918ab565f224ccd35ecChong Zhang protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 1310978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung float fontSize = MeasureSpec.getSize(heightMeasureSpec) * FONT_PADDING_RATIO; 1311bdfd91024767893829017918ab565f224ccd35ecChong Zhang setTextSize(TypedValue.COMPLEX_UNIT_PX, fontSize); 1312bdfd91024767893829017918ab565f224ccd35ecChong Zhang 131319ed4a179ccea6915f1852139d520dddba779490Chong Zhang mOutlineWidth = EDGE_OUTLINE_RATIO * fontSize + 1.0f; 131419ed4a179ccea6915f1852139d520dddba779490Chong Zhang mShadowRadius = EDGE_SHADOW_RATIO * fontSize + 1.0f;; 131519ed4a179ccea6915f1852139d520dddba779490Chong Zhang mShadowOffset = mShadowRadius; 131619ed4a179ccea6915f1852139d520dddba779490Chong Zhang 1317bdfd91024767893829017918ab565f224ccd35ecChong Zhang // set font scale in the X direction to match the required width 1318bdfd91024767893829017918ab565f224ccd35ecChong Zhang setScaleX(1.0f); 1319bdfd91024767893829017918ab565f224ccd35ecChong Zhang getPaint().getTextBounds(mDummyText, 0, mDummyText.length(), mTextBounds); 1320bdfd91024767893829017918ab565f224ccd35ecChong Zhang float actualTextWidth = mTextBounds.width(); 1321bdfd91024767893829017918ab565f224ccd35ecChong Zhang float requiredTextWidth = MeasureSpec.getSize(widthMeasureSpec); 1322bdfd91024767893829017918ab565f224ccd35ecChong Zhang setScaleX(requiredTextWidth / actualTextWidth); 1323bdfd91024767893829017918ab565f224ccd35ecChong Zhang 1324bdfd91024767893829017918ab565f224ccd35ecChong Zhang super.onMeasure(widthMeasureSpec, heightMeasureSpec); 1325bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 132619ed4a179ccea6915f1852139d520dddba779490Chong Zhang 132719ed4a179ccea6915f1852139d520dddba779490Chong Zhang @Override 132819ed4a179ccea6915f1852139d520dddba779490Chong Zhang protected void onDraw(Canvas c) { 132919ed4a179ccea6915f1852139d520dddba779490Chong Zhang if (mEdgeType == CaptionStyle.EDGE_TYPE_UNSPECIFIED 133019ed4a179ccea6915f1852139d520dddba779490Chong Zhang || mEdgeType == CaptionStyle.EDGE_TYPE_NONE 133119ed4a179ccea6915f1852139d520dddba779490Chong Zhang || mEdgeType == CaptionStyle.EDGE_TYPE_DROP_SHADOW) { 133219ed4a179ccea6915f1852139d520dddba779490Chong Zhang // these edge styles don't require a second pass 133319ed4a179ccea6915f1852139d520dddba779490Chong Zhang super.onDraw(c); 133419ed4a179ccea6915f1852139d520dddba779490Chong Zhang return; 133519ed4a179ccea6915f1852139d520dddba779490Chong Zhang } 133619ed4a179ccea6915f1852139d520dddba779490Chong Zhang 133719ed4a179ccea6915f1852139d520dddba779490Chong Zhang if (mEdgeType == CaptionStyle.EDGE_TYPE_OUTLINE) { 133819ed4a179ccea6915f1852139d520dddba779490Chong Zhang drawEdgeOutline(c); 133919ed4a179ccea6915f1852139d520dddba779490Chong Zhang } else { 134019ed4a179ccea6915f1852139d520dddba779490Chong Zhang // Raised or depressed 134119ed4a179ccea6915f1852139d520dddba779490Chong Zhang drawEdgeRaisedOrDepressed(c); 134219ed4a179ccea6915f1852139d520dddba779490Chong Zhang } 134319ed4a179ccea6915f1852139d520dddba779490Chong Zhang } 134419ed4a179ccea6915f1852139d520dddba779490Chong Zhang 134519ed4a179ccea6915f1852139d520dddba779490Chong Zhang private void drawEdgeOutline(Canvas c) { 134619ed4a179ccea6915f1852139d520dddba779490Chong Zhang TextPaint textPaint = getPaint(); 134719ed4a179ccea6915f1852139d520dddba779490Chong Zhang 134819ed4a179ccea6915f1852139d520dddba779490Chong Zhang Paint.Style previousStyle = textPaint.getStyle(); 134919ed4a179ccea6915f1852139d520dddba779490Chong Zhang Paint.Join previousJoin = textPaint.getStrokeJoin(); 135019ed4a179ccea6915f1852139d520dddba779490Chong Zhang float previousWidth = textPaint.getStrokeWidth(); 135119ed4a179ccea6915f1852139d520dddba779490Chong Zhang 135219ed4a179ccea6915f1852139d520dddba779490Chong Zhang setTextColor(mEdgeColor); 135319ed4a179ccea6915f1852139d520dddba779490Chong Zhang textPaint.setStyle(Paint.Style.FILL_AND_STROKE); 135419ed4a179ccea6915f1852139d520dddba779490Chong Zhang textPaint.setStrokeJoin(Paint.Join.ROUND); 135519ed4a179ccea6915f1852139d520dddba779490Chong Zhang textPaint.setStrokeWidth(mOutlineWidth); 135619ed4a179ccea6915f1852139d520dddba779490Chong Zhang 135719ed4a179ccea6915f1852139d520dddba779490Chong Zhang // Draw outline and background only. 135819ed4a179ccea6915f1852139d520dddba779490Chong Zhang super.onDraw(c); 135919ed4a179ccea6915f1852139d520dddba779490Chong Zhang 136019ed4a179ccea6915f1852139d520dddba779490Chong Zhang // Restore original settings. 136119ed4a179ccea6915f1852139d520dddba779490Chong Zhang setTextColor(mTextColor); 136219ed4a179ccea6915f1852139d520dddba779490Chong Zhang textPaint.setStyle(previousStyle); 136319ed4a179ccea6915f1852139d520dddba779490Chong Zhang textPaint.setStrokeJoin(previousJoin); 136419ed4a179ccea6915f1852139d520dddba779490Chong Zhang textPaint.setStrokeWidth(previousWidth); 136519ed4a179ccea6915f1852139d520dddba779490Chong Zhang 136619ed4a179ccea6915f1852139d520dddba779490Chong Zhang // Remove the background. 136719ed4a179ccea6915f1852139d520dddba779490Chong Zhang setBackgroundSpans(Color.TRANSPARENT); 136819ed4a179ccea6915f1852139d520dddba779490Chong Zhang // Draw foreground only. 136919ed4a179ccea6915f1852139d520dddba779490Chong Zhang super.onDraw(c); 137019ed4a179ccea6915f1852139d520dddba779490Chong Zhang // Restore the background. 137119ed4a179ccea6915f1852139d520dddba779490Chong Zhang setBackgroundSpans(mBgColor); 137219ed4a179ccea6915f1852139d520dddba779490Chong Zhang } 137319ed4a179ccea6915f1852139d520dddba779490Chong Zhang 137419ed4a179ccea6915f1852139d520dddba779490Chong Zhang private void drawEdgeRaisedOrDepressed(Canvas c) { 137519ed4a179ccea6915f1852139d520dddba779490Chong Zhang TextPaint textPaint = getPaint(); 137619ed4a179ccea6915f1852139d520dddba779490Chong Zhang 137719ed4a179ccea6915f1852139d520dddba779490Chong Zhang Paint.Style previousStyle = textPaint.getStyle(); 137819ed4a179ccea6915f1852139d520dddba779490Chong Zhang textPaint.setStyle(Paint.Style.FILL); 137919ed4a179ccea6915f1852139d520dddba779490Chong Zhang 138019ed4a179ccea6915f1852139d520dddba779490Chong Zhang final boolean raised = mEdgeType == CaptionStyle.EDGE_TYPE_RAISED; 138119ed4a179ccea6915f1852139d520dddba779490Chong Zhang final int colorUp = raised ? Color.WHITE : mEdgeColor; 138219ed4a179ccea6915f1852139d520dddba779490Chong Zhang final int colorDown = raised ? mEdgeColor : Color.WHITE; 138319ed4a179ccea6915f1852139d520dddba779490Chong Zhang final float offset = mShadowRadius / 2f; 138419ed4a179ccea6915f1852139d520dddba779490Chong Zhang 138519ed4a179ccea6915f1852139d520dddba779490Chong Zhang // Draw background and text with shadow up 138619ed4a179ccea6915f1852139d520dddba779490Chong Zhang setShadowLayer(mShadowRadius, -offset, -offset, colorUp); 138719ed4a179ccea6915f1852139d520dddba779490Chong Zhang super.onDraw(c); 138819ed4a179ccea6915f1852139d520dddba779490Chong Zhang 138919ed4a179ccea6915f1852139d520dddba779490Chong Zhang // Remove the background. 139019ed4a179ccea6915f1852139d520dddba779490Chong Zhang setBackgroundSpans(Color.TRANSPARENT); 139119ed4a179ccea6915f1852139d520dddba779490Chong Zhang 139219ed4a179ccea6915f1852139d520dddba779490Chong Zhang // Draw text with shadow down 139319ed4a179ccea6915f1852139d520dddba779490Chong Zhang setShadowLayer(mShadowRadius, +offset, +offset, colorDown); 139419ed4a179ccea6915f1852139d520dddba779490Chong Zhang super.onDraw(c); 139519ed4a179ccea6915f1852139d520dddba779490Chong Zhang 139619ed4a179ccea6915f1852139d520dddba779490Chong Zhang // Restore settings 139719ed4a179ccea6915f1852139d520dddba779490Chong Zhang textPaint.setStyle(previousStyle); 139819ed4a179ccea6915f1852139d520dddba779490Chong Zhang 139919ed4a179ccea6915f1852139d520dddba779490Chong Zhang // Restore the background. 140019ed4a179ccea6915f1852139d520dddba779490Chong Zhang setBackgroundSpans(mBgColor); 140119ed4a179ccea6915f1852139d520dddba779490Chong Zhang } 140219ed4a179ccea6915f1852139d520dddba779490Chong Zhang 140319ed4a179ccea6915f1852139d520dddba779490Chong Zhang private void setBackgroundSpans(int color) { 140419ed4a179ccea6915f1852139d520dddba779490Chong Zhang CharSequence text = getText(); 140519ed4a179ccea6915f1852139d520dddba779490Chong Zhang if (text instanceof Spannable) { 140619ed4a179ccea6915f1852139d520dddba779490Chong Zhang Spannable spannable = (Spannable) text; 1407978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung Cea608CCParser.MutableBackgroundColorSpan[] bgSpans = spannable.getSpans( 1408978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung 0, spannable.length(), Cea608CCParser.MutableBackgroundColorSpan.class); 140919ed4a179ccea6915f1852139d520dddba779490Chong Zhang for (int i = 0; i < bgSpans.length; i++) { 141019ed4a179ccea6915f1852139d520dddba779490Chong Zhang bgSpans[i].setBackgroundColor(color); 141119ed4a179ccea6915f1852139d520dddba779490Chong Zhang } 141219ed4a179ccea6915f1852139d520dddba779490Chong Zhang } 141319ed4a179ccea6915f1852139d520dddba779490Chong Zhang } 1414bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 1415bdfd91024767893829017918ab565f224ccd35ecChong Zhang 1416978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung private static class CCLayout extends LinearLayout implements ClosedCaptionLayout { 1417978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung private static final int MAX_ROWS = Cea608CCParser.MAX_ROWS; 1418bdfd91024767893829017918ab565f224ccd35ecChong Zhang private static final float SAFE_AREA_RATIO = 0.9f; 1419bdfd91024767893829017918ab565f224ccd35ecChong Zhang 1420bdfd91024767893829017918ab565f224ccd35ecChong Zhang private final CCLineBox[] mLineBoxes = new CCLineBox[MAX_ROWS]; 1421bdfd91024767893829017918ab565f224ccd35ecChong Zhang 1422bdfd91024767893829017918ab565f224ccd35ecChong Zhang CCLayout(Context context) { 1423bdfd91024767893829017918ab565f224ccd35ecChong Zhang super(context); 1424bdfd91024767893829017918ab565f224ccd35ecChong Zhang setGravity(Gravity.START); 1425bdfd91024767893829017918ab565f224ccd35ecChong Zhang setOrientation(LinearLayout.VERTICAL); 1426bdfd91024767893829017918ab565f224ccd35ecChong Zhang for (int i = 0; i < MAX_ROWS; i++) { 1427bdfd91024767893829017918ab565f224ccd35ecChong Zhang mLineBoxes[i] = new CCLineBox(getContext()); 1428bdfd91024767893829017918ab565f224ccd35ecChong Zhang addView(mLineBoxes[i], LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); 1429bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 1430bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 1431bdfd91024767893829017918ab565f224ccd35ecChong Zhang 1432978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung @Override 1433978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung public void setCaptionStyle(CaptionStyle captionStyle) { 1434bdfd91024767893829017918ab565f224ccd35ecChong Zhang for (int i = 0; i < MAX_ROWS; i++) { 1435bdfd91024767893829017918ab565f224ccd35ecChong Zhang mLineBoxes[i].setCaptionStyle(captionStyle); 1436bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 1437bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 1438bdfd91024767893829017918ab565f224ccd35ecChong Zhang 1439978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung @Override 1440978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung public void setFontScale(float fontScale) { 1441978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung // Ignores the font scale changes of the system wide CC preference. 1442978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung } 1443978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung 1444bdfd91024767893829017918ab565f224ccd35ecChong Zhang void update(SpannableStringBuilder[] textBuffer) { 1445bdfd91024767893829017918ab565f224ccd35ecChong Zhang for (int i = 0; i < MAX_ROWS; i++) { 1446bdfd91024767893829017918ab565f224ccd35ecChong Zhang if (textBuffer[i] != null) { 144719ed4a179ccea6915f1852139d520dddba779490Chong Zhang mLineBoxes[i].setText(textBuffer[i], TextView.BufferType.SPANNABLE); 1448bdfd91024767893829017918ab565f224ccd35ecChong Zhang mLineBoxes[i].setVisibility(View.VISIBLE); 1449bdfd91024767893829017918ab565f224ccd35ecChong Zhang } else { 1450bdfd91024767893829017918ab565f224ccd35ecChong Zhang mLineBoxes[i].setVisibility(View.INVISIBLE); 1451bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 1452bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 1453bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 1454bdfd91024767893829017918ab565f224ccd35ecChong Zhang 1455bdfd91024767893829017918ab565f224ccd35ecChong Zhang @Override 1456bdfd91024767893829017918ab565f224ccd35ecChong Zhang protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 1457bdfd91024767893829017918ab565f224ccd35ecChong Zhang super.onMeasure(widthMeasureSpec, heightMeasureSpec); 1458bdfd91024767893829017918ab565f224ccd35ecChong Zhang 1459bdfd91024767893829017918ab565f224ccd35ecChong Zhang int safeWidth = getMeasuredWidth(); 1460bdfd91024767893829017918ab565f224ccd35ecChong Zhang int safeHeight = getMeasuredHeight(); 1461bdfd91024767893829017918ab565f224ccd35ecChong Zhang 1462bdfd91024767893829017918ab565f224ccd35ecChong Zhang // CEA-608 assumes 4:3 video 1463bdfd91024767893829017918ab565f224ccd35ecChong Zhang if (safeWidth * 3 >= safeHeight * 4) { 1464bdfd91024767893829017918ab565f224ccd35ecChong Zhang safeWidth = safeHeight * 4 / 3; 1465bdfd91024767893829017918ab565f224ccd35ecChong Zhang } else { 1466bdfd91024767893829017918ab565f224ccd35ecChong Zhang safeHeight = safeWidth * 3 / 4; 1467bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 1468bdfd91024767893829017918ab565f224ccd35ecChong Zhang safeWidth *= SAFE_AREA_RATIO; 1469bdfd91024767893829017918ab565f224ccd35ecChong Zhang safeHeight *= SAFE_AREA_RATIO; 1470bdfd91024767893829017918ab565f224ccd35ecChong Zhang 1471bdfd91024767893829017918ab565f224ccd35ecChong Zhang int lineHeight = safeHeight / MAX_ROWS; 1472bdfd91024767893829017918ab565f224ccd35ecChong Zhang int lineHeightMeasureSpec = MeasureSpec.makeMeasureSpec( 1473bdfd91024767893829017918ab565f224ccd35ecChong Zhang lineHeight, MeasureSpec.EXACTLY); 1474bdfd91024767893829017918ab565f224ccd35ecChong Zhang int lineWidthMeasureSpec = MeasureSpec.makeMeasureSpec( 1475bdfd91024767893829017918ab565f224ccd35ecChong Zhang safeWidth, MeasureSpec.EXACTLY); 1476bdfd91024767893829017918ab565f224ccd35ecChong Zhang 1477bdfd91024767893829017918ab565f224ccd35ecChong Zhang for (int i = 0; i < MAX_ROWS; i++) { 1478bdfd91024767893829017918ab565f224ccd35ecChong Zhang mLineBoxes[i].measure(lineWidthMeasureSpec, lineHeightMeasureSpec); 1479bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 1480bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 1481bdfd91024767893829017918ab565f224ccd35ecChong Zhang 1482bdfd91024767893829017918ab565f224ccd35ecChong Zhang @Override 1483bdfd91024767893829017918ab565f224ccd35ecChong Zhang protected void onLayout(boolean changed, int l, int t, int r, int b) { 1484bdfd91024767893829017918ab565f224ccd35ecChong Zhang // safe caption area 1485bdfd91024767893829017918ab565f224ccd35ecChong Zhang int viewPortWidth = r - l; 1486bdfd91024767893829017918ab565f224ccd35ecChong Zhang int viewPortHeight = b - t; 1487bdfd91024767893829017918ab565f224ccd35ecChong Zhang int safeWidth, safeHeight; 1488bdfd91024767893829017918ab565f224ccd35ecChong Zhang // CEA-608 assumes 4:3 video 1489bdfd91024767893829017918ab565f224ccd35ecChong Zhang if (viewPortWidth * 3 >= viewPortHeight * 4) { 1490bdfd91024767893829017918ab565f224ccd35ecChong Zhang safeWidth = viewPortHeight * 4 / 3; 1491bdfd91024767893829017918ab565f224ccd35ecChong Zhang safeHeight = viewPortHeight; 1492bdfd91024767893829017918ab565f224ccd35ecChong Zhang } else { 1493bdfd91024767893829017918ab565f224ccd35ecChong Zhang safeWidth = viewPortWidth; 1494bdfd91024767893829017918ab565f224ccd35ecChong Zhang safeHeight = viewPortWidth * 3 / 4; 1495bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 1496bdfd91024767893829017918ab565f224ccd35ecChong Zhang safeWidth *= SAFE_AREA_RATIO; 1497bdfd91024767893829017918ab565f224ccd35ecChong Zhang safeHeight *= SAFE_AREA_RATIO; 1498bdfd91024767893829017918ab565f224ccd35ecChong Zhang int left = (viewPortWidth - safeWidth) / 2; 1499bdfd91024767893829017918ab565f224ccd35ecChong Zhang int top = (viewPortHeight - safeHeight) / 2; 1500bdfd91024767893829017918ab565f224ccd35ecChong Zhang 1501bdfd91024767893829017918ab565f224ccd35ecChong Zhang for (int i = 0; i < MAX_ROWS; i++) { 1502bdfd91024767893829017918ab565f224ccd35ecChong Zhang mLineBoxes[i].layout( 1503bdfd91024767893829017918ab565f224ccd35ecChong Zhang left, 1504bdfd91024767893829017918ab565f224ccd35ecChong Zhang top + safeHeight * i / MAX_ROWS, 1505bdfd91024767893829017918ab565f224ccd35ecChong Zhang left + safeWidth, 1506bdfd91024767893829017918ab565f224ccd35ecChong Zhang top + safeHeight * (i + 1) / MAX_ROWS); 1507bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 1508bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 1509bdfd91024767893829017918ab565f224ccd35ecChong Zhang } 1510978bf5eff0f5bc8a8878696bc886d8b99a29ee39Jaesung Chung} 1511