• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.google.android.exoplayer.text;
17 
18 import android.annotation.TargetApi;
19 import android.content.Context;
20 import android.content.res.Resources;
21 import android.content.res.TypedArray;
22 import android.graphics.Canvas;
23 import android.graphics.Color;
24 import android.graphics.Paint;
25 import android.graphics.Paint.Join;
26 import android.graphics.Paint.Style;
27 import android.graphics.RectF;
28 import android.graphics.Typeface;
29 import android.text.Layout.Alignment;
30 import android.text.StaticLayout;
31 import android.text.TextPaint;
32 import android.util.AttributeSet;
33 import android.util.DisplayMetrics;
34 import android.view.View;
35 
36 import com.google.android.exoplayer.util.Util;
37 
38 import java.util.ArrayList;
39 
40 /**
41  * Since this class does not exist in recent version of ExoPlayer and used by
42  * {@link com.google.android.tv.tuner.cc.CaptionWindowLayout}, this class is copied from
43  * older version of ExoPlayer.
44  * A view for rendering a single caption.
45  */
46 @Deprecated
47 public class SubtitleView extends View {
48     // TODO: Change usage of this class to up-to-date class of ExoPlayer.
49 
50     /**
51      * Ratio of inner padding to font size.
52      */
53     private static final float INNER_PADDING_RATIO = 0.125f;
54 
55     /**
56      * Temporary rectangle used for computing line bounds.
57      */
58     private final RectF mLineBounds = new RectF();
59 
60     // Styled dimensions.
61     private final float mCornerRadius;
62     private final float mOutlineWidth;
63     private final float mShadowRadius;
64     private final float mShadowOffset;
65 
66     private final TextPaint mTextPaint;
67     private final Paint mPaint;
68 
69     private CharSequence mText;
70 
71     private int mForegroundColor;
72     private int mBackgroundColor;
73     private int mEdgeColor;
74     private int mEdgeType;
75 
76     private boolean mHasMeasurements;
77     private int mLastMeasuredWidth;
78     private StaticLayout mLayout;
79 
80     private Alignment mAlignment;
81     private final float mSpacingMult;
82     private final float mSpacingAdd;
83     private int mInnerPaddingX;
84     private float mWhiteSpaceWidth;
85     private ArrayList<Integer> mPrefixSpaces = new ArrayList<>();
86 
SubtitleView(Context context)87     public SubtitleView(Context context) {
88         this(context, null);
89     }
90 
SubtitleView(Context context, AttributeSet attrs)91     public SubtitleView(Context context, AttributeSet attrs) {
92         this(context, attrs, 0);
93     }
94 
SubtitleView(Context context, AttributeSet attrs, int defStyleAttr)95     public SubtitleView(Context context, AttributeSet attrs, int defStyleAttr) {
96         super(context, attrs, defStyleAttr);
97 
98         int[] viewAttr = {android.R.attr.text, android.R.attr.textSize,
99                 android.R.attr.lineSpacingExtra, android.R.attr.lineSpacingMultiplier};
100         TypedArray a = context.obtainStyledAttributes(attrs, viewAttr, defStyleAttr, 0);
101         CharSequence text = a.getText(0);
102         int textSize = a.getDimensionPixelSize(1, 15);
103         mSpacingAdd = a.getDimensionPixelSize(2, 0);
104         mSpacingMult = a.getFloat(3, 1);
105         a.recycle();
106 
107         Resources resources = getContext().getResources();
108         DisplayMetrics displayMetrics = resources.getDisplayMetrics();
109         int twoDpInPx =
110                 Math.round((2f * displayMetrics.densityDpi) / DisplayMetrics.DENSITY_DEFAULT);
111         mCornerRadius = twoDpInPx;
112         mOutlineWidth = twoDpInPx;
113         mShadowRadius = twoDpInPx;
114         mShadowOffset = twoDpInPx;
115 
116         mTextPaint = new TextPaint();
117         mTextPaint.setAntiAlias(true);
118         mTextPaint.setSubpixelText(true);
119 
120         mAlignment = Alignment.ALIGN_CENTER;
121 
122         mPaint = new Paint();
123         mPaint.setAntiAlias(true);
124 
125         mInnerPaddingX = 0;
126         setText(text);
127         setTextSize(textSize);
128         setStyle(CaptionStyleCompat.DEFAULT);
129     }
130 
131     @Override
setBackgroundColor(int color)132     public void setBackgroundColor(int color) {
133         mBackgroundColor = color;
134         forceUpdate(false);
135     }
136 
137     /**
138      * Sets the text to be displayed by the view.
139      *
140      * @param text The text to display.
141      */
setText(CharSequence text)142     public void setText(CharSequence text) {
143         this.mText = text;
144         forceUpdate(true);
145     }
146 
147     /**
148      * Sets the text size in pixels.
149      *
150      * @param size The text size in pixels.
151      */
setTextSize(float size)152     public void setTextSize(float size) {
153         if (mTextPaint.getTextSize() != size) {
154             mTextPaint.setTextSize(size);
155             mInnerPaddingX = (int) (size * INNER_PADDING_RATIO + 0.5f);
156             mWhiteSpaceWidth -= mInnerPaddingX * 2;
157             forceUpdate(true);
158         }
159     }
160 
161     /**
162      * Sets the text alignment.
163      *
164      * @param textAlignment The text alignment.
165      */
setTextAlignment(Alignment textAlignment)166     public void setTextAlignment(Alignment textAlignment) {
167         mAlignment = textAlignment;
168     }
169 
170     /**
171      * Configures the view according to the given style.
172      *
173      * @param style A style for the view.
174      */
setStyle(CaptionStyleCompat style)175     public void setStyle(CaptionStyleCompat style) {
176         mForegroundColor = style.foregroundColor;
177         mBackgroundColor = style.backgroundColor;
178         mEdgeType = style.edgeType;
179         mEdgeColor = style.edgeColor;
180         setTypeface(style.typeface);
181         super.setBackgroundColor(style.windowColor);
182         forceUpdate(true);
183     }
184 
setPrefixSpaces(ArrayList<Integer> prefixSpaces)185     public void setPrefixSpaces(ArrayList<Integer> prefixSpaces) {
186         mPrefixSpaces = prefixSpaces;
187     }
188 
setWhiteSpaceWidth(float whiteSpaceWidth)189     public void setWhiteSpaceWidth(float whiteSpaceWidth) {
190         mWhiteSpaceWidth = whiteSpaceWidth;
191     }
192 
setTypeface(Typeface typeface)193     private void setTypeface(Typeface typeface) {
194         if (mTextPaint.getTypeface() != typeface) {
195             mTextPaint.setTypeface(typeface);
196             forceUpdate(true);
197         }
198     }
199 
forceUpdate(boolean needsLayout)200     private void forceUpdate(boolean needsLayout) {
201         if (needsLayout) {
202             mHasMeasurements = false;
203             requestLayout();
204         }
205         invalidate();
206     }
207 
208     @Override
onMeasure(int widthMeasureSpec, int heightMeasureSpec)209     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
210         final int widthSpec = MeasureSpec.getSize(widthMeasureSpec);
211 
212         if (computeMeasurements(widthSpec)) {
213             final StaticLayout layout = this.mLayout;
214             final int paddingX = getPaddingLeft() + getPaddingRight() + mInnerPaddingX * 2;
215             final int height = layout.getHeight() + getPaddingTop() + getPaddingBottom();
216             int width = 0;
217             int lineCount = layout.getLineCount();
218             for (int i = 0; i < lineCount; i++) {
219                 width = Math.max((int) Math.ceil(layout.getLineWidth(i)), width);
220             }
221             width += paddingX;
222             setMeasuredDimension(width, height);
223         } else if (Util.SDK_INT >= 11) {
224             setTooSmallMeasureDimensionV11();
225         } else {
226             setMeasuredDimension(0, 0);
227         }
228     }
229 
230     @TargetApi(11)
setTooSmallMeasureDimensionV11()231     private void setTooSmallMeasureDimensionV11() {
232         setMeasuredDimension(MEASURED_STATE_TOO_SMALL, MEASURED_STATE_TOO_SMALL);
233     }
234 
235     @Override
onLayout(boolean changed, int l, int t, int r, int b)236     public void onLayout(boolean changed, int l, int t, int r, int b) {
237         final int width = r - l;
238         computeMeasurements(width);
239     }
240 
computeMeasurements(int maxWidth)241     private boolean computeMeasurements(int maxWidth) {
242         if (mHasMeasurements && maxWidth == mLastMeasuredWidth) {
243             return true;
244         }
245 
246         // Account for padding.
247         final int paddingX = getPaddingLeft() + getPaddingRight() + mInnerPaddingX * 2;
248         maxWidth -= paddingX;
249         if (maxWidth <= 0) {
250             return false;
251         }
252 
253         mHasMeasurements = true;
254         mLastMeasuredWidth = maxWidth;
255         mLayout = new StaticLayout(mText, mTextPaint, maxWidth, mAlignment,
256                 mSpacingMult, mSpacingAdd, true);
257         return true;
258     }
259 
260     @Override
onDraw(Canvas c)261     protected void onDraw(Canvas c) {
262         final StaticLayout layout = this.mLayout;
263         if (layout == null) {
264             return;
265         }
266 
267         final int saveCount = c.save();
268         final int innerPaddingX = this.mInnerPaddingX;
269         c.translate(getPaddingLeft() + innerPaddingX, getPaddingTop());
270 
271         final int lineCount = layout.getLineCount();
272         final Paint textPaint = this.mTextPaint;
273         final Paint paint = this.mPaint;
274         final RectF bounds = mLineBounds;
275 
276         if (Color.alpha(mBackgroundColor) > 0) {
277             final float cornerRadius = this.mCornerRadius;
278             float previousBottom = layout.getLineTop(0);
279 
280             paint.setColor(mBackgroundColor);
281             paint.setStyle(Style.FILL);
282 
283             for (int i = 0; i < lineCount; i++) {
284                 float spacesPadding = 0.0f;
285                 if (i < mPrefixSpaces.size()) {
286                     spacesPadding += mPrefixSpaces.get(i) * mWhiteSpaceWidth;
287                 }
288                 bounds.left = layout.getLineLeft(i) - innerPaddingX + spacesPadding;
289                 bounds.right = layout.getLineRight(i) + innerPaddingX;
290                 bounds.top = previousBottom;
291                 bounds.bottom = layout.getLineBottom(i);
292                 previousBottom = bounds.bottom;
293 
294                 c.drawRoundRect(bounds, cornerRadius, cornerRadius, paint);
295             }
296         }
297 
298         if (mEdgeType == CaptionStyleCompat.EDGE_TYPE_OUTLINE) {
299             textPaint.setStrokeJoin(Join.ROUND);
300             textPaint.setStrokeWidth(mOutlineWidth);
301             textPaint.setColor(mEdgeColor);
302             textPaint.setStyle(Style.FILL_AND_STROKE);
303             layout.draw(c);
304         } else if (mEdgeType == CaptionStyleCompat.EDGE_TYPE_DROP_SHADOW) {
305             textPaint.setShadowLayer(mShadowRadius, mShadowOffset, mShadowOffset, mEdgeColor);
306         } else if (mEdgeType == CaptionStyleCompat.EDGE_TYPE_RAISED
307                 || mEdgeType == CaptionStyleCompat.EDGE_TYPE_DEPRESSED) {
308             boolean raised = mEdgeType == CaptionStyleCompat.EDGE_TYPE_RAISED;
309             int colorUp = raised ? Color.WHITE : mEdgeColor;
310             int colorDown = raised ? mEdgeColor : Color.WHITE;
311             float offset = mShadowRadius / 2f;
312             textPaint.setColor(mForegroundColor);
313             textPaint.setStyle(Style.FILL);
314             textPaint.setShadowLayer(mShadowRadius, -offset, -offset, colorUp);
315             layout.draw(c);
316             textPaint.setShadowLayer(mShadowRadius, offset, offset, colorDown);
317         }
318 
319         textPaint.setColor(mForegroundColor);
320         textPaint.setStyle(Style.FILL);
321         layout.draw(c);
322         textPaint.setShadowLayer(0, 0, 0, 0);
323         c.restoreToCount(saveCount);
324     }
325 
326 }
327