• 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.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     /**
49      * Ratio of inner padding to font size.
50      */
51     private static final float INNER_PADDING_RATIO = 0.125f;
52 
53     /**
54      * Temporary rectangle used for computing line bounds.
55      */
56     private final RectF mLineBounds = new RectF();
57 
58     // Styled dimensions.
59     private final float mCornerRadius;
60     private final float mOutlineWidth;
61     private final float mShadowRadius;
62     private final float mShadowOffset;
63 
64     private final TextPaint mTextPaint;
65     private final Paint mPaint;
66 
67     private CharSequence mText;
68 
69     private int mForegroundColor;
70     private int mBackgroundColor;
71     private int mEdgeColor;
72     private int mEdgeType;
73 
74     private boolean mHasMeasurements;
75     private int mLastMeasuredWidth;
76     private StaticLayout mLayout;
77 
78     private Alignment mAlignment;
79     private final float mSpacingMult;
80     private final float mSpacingAdd;
81     private int mInnerPaddingX;
82     private float mWhiteSpaceWidth;
83     private ArrayList<Integer> mPrefixSpaces = new ArrayList<>();
84 
SubtitleView(Context context)85     public SubtitleView(Context context) {
86         this(context, null);
87     }
88 
SubtitleView(Context context, AttributeSet attrs)89     public SubtitleView(Context context, AttributeSet attrs) {
90         this(context, attrs, 0);
91     }
92 
SubtitleView(Context context, AttributeSet attrs, int defStyleAttr)93     public SubtitleView(Context context, AttributeSet attrs, int defStyleAttr) {
94         super(context, attrs, defStyleAttr);
95 
96         int[] viewAttr = {android.R.attr.text, android.R.attr.textSize,
97                 android.R.attr.lineSpacingExtra, android.R.attr.lineSpacingMultiplier};
98         TypedArray a = context.obtainStyledAttributes(attrs, viewAttr, defStyleAttr, 0);
99         CharSequence text = a.getText(0);
100         int textSize = a.getDimensionPixelSize(1, 15);
101         mSpacingAdd = a.getDimensionPixelSize(2, 0);
102         mSpacingMult = a.getFloat(3, 1);
103         a.recycle();
104 
105         Resources resources = getContext().getResources();
106         DisplayMetrics displayMetrics = resources.getDisplayMetrics();
107         int twoDpInPx =
108                 Math.round((2f * displayMetrics.densityDpi) / DisplayMetrics.DENSITY_DEFAULT);
109         mCornerRadius = twoDpInPx;
110         mOutlineWidth = twoDpInPx;
111         mShadowRadius = twoDpInPx;
112         mShadowOffset = twoDpInPx;
113 
114         mTextPaint = new TextPaint();
115         mTextPaint.setAntiAlias(true);
116         mTextPaint.setSubpixelText(true);
117 
118         mAlignment = Alignment.ALIGN_CENTER;
119 
120         mPaint = new Paint();
121         mPaint.setAntiAlias(true);
122 
123         mInnerPaddingX = 0;
124         setText(text);
125         setTextSize(textSize);
126         setStyle(CaptionStyleCompat.DEFAULT);
127     }
128 
129     @Override
setBackgroundColor(int color)130     public void setBackgroundColor(int color) {
131         mBackgroundColor = color;
132         forceUpdate(false);
133     }
134 
135     /**
136      * Sets the text to be displayed by the view.
137      *
138      * @param text The text to display.
139      */
setText(CharSequence text)140     public void setText(CharSequence text) {
141         this.mText = text;
142         forceUpdate(true);
143     }
144 
145     /**
146      * Sets the text size in pixels.
147      *
148      * @param size The text size in pixels.
149      */
setTextSize(float size)150     public void setTextSize(float size) {
151         if (mTextPaint.getTextSize() != size) {
152             mTextPaint.setTextSize(size);
153             mInnerPaddingX = (int) (size * INNER_PADDING_RATIO + 0.5f);
154             mWhiteSpaceWidth -= mInnerPaddingX * 2;
155             forceUpdate(true);
156         }
157     }
158 
159     /**
160      * Sets the text alignment.
161      *
162      * @param textAlignment The text alignment.
163      */
setTextAlignment(Alignment textAlignment)164     public void setTextAlignment(Alignment textAlignment) {
165         mAlignment = textAlignment;
166     }
167 
168     /**
169      * Configures the view according to the given style.
170      *
171      * @param style A style for the view.
172      */
setStyle(CaptionStyleCompat style)173     public void setStyle(CaptionStyleCompat style) {
174         mForegroundColor = style.foregroundColor;
175         mBackgroundColor = style.backgroundColor;
176         mEdgeType = style.edgeType;
177         mEdgeColor = style.edgeColor;
178         setTypeface(style.typeface);
179         super.setBackgroundColor(style.windowColor);
180         forceUpdate(true);
181     }
182 
setPrefixSpaces(ArrayList<Integer> prefixSpaces)183     public void setPrefixSpaces(ArrayList<Integer> prefixSpaces) {
184         mPrefixSpaces = prefixSpaces;
185     }
186 
setWhiteSpaceWidth(float whiteSpaceWidth)187     public void setWhiteSpaceWidth(float whiteSpaceWidth) {
188         mWhiteSpaceWidth = whiteSpaceWidth;
189     }
190 
setTypeface(Typeface typeface)191     private void setTypeface(Typeface typeface) {
192         if (mTextPaint.getTypeface() != typeface) {
193             mTextPaint.setTypeface(typeface);
194             forceUpdate(true);
195         }
196     }
197 
forceUpdate(boolean needsLayout)198     private void forceUpdate(boolean needsLayout) {
199         if (needsLayout) {
200             mHasMeasurements = false;
201             requestLayout();
202         }
203         invalidate();
204     }
205 
206     @Override
onMeasure(int widthMeasureSpec, int heightMeasureSpec)207     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
208         final int widthSpec = MeasureSpec.getSize(widthMeasureSpec);
209 
210         if (computeMeasurements(widthSpec)) {
211             final StaticLayout layout = this.mLayout;
212             final int paddingX = getPaddingLeft() + getPaddingRight() + mInnerPaddingX * 2;
213             final int height = layout.getHeight() + getPaddingTop() + getPaddingBottom();
214             int width = 0;
215             int lineCount = layout.getLineCount();
216             for (int i = 0; i < lineCount; i++) {
217                 width = Math.max((int) Math.ceil(layout.getLineWidth(i)), width);
218             }
219             width += paddingX;
220             setMeasuredDimension(width, height);
221         } else if (Util.SDK_INT >= 11) {
222             setTooSmallMeasureDimensionV11();
223         } else {
224             setMeasuredDimension(0, 0);
225         }
226     }
227 
228     @TargetApi(11)
setTooSmallMeasureDimensionV11()229     private void setTooSmallMeasureDimensionV11() {
230         setMeasuredDimension(MEASURED_STATE_TOO_SMALL, MEASURED_STATE_TOO_SMALL);
231     }
232 
233     @Override
onLayout(boolean changed, int l, int t, int r, int b)234     public void onLayout(boolean changed, int l, int t, int r, int b) {
235         final int width = r - l;
236         computeMeasurements(width);
237     }
238 
computeMeasurements(int maxWidth)239     private boolean computeMeasurements(int maxWidth) {
240         if (mHasMeasurements && maxWidth == mLastMeasuredWidth) {
241             return true;
242         }
243 
244         // Account for padding.
245         final int paddingX = getPaddingLeft() + getPaddingRight() + mInnerPaddingX * 2;
246         maxWidth -= paddingX;
247         if (maxWidth <= 0) {
248             return false;
249         }
250 
251         mHasMeasurements = true;
252         mLastMeasuredWidth = maxWidth;
253         mLayout = new StaticLayout(mText, mTextPaint, maxWidth, mAlignment,
254                 mSpacingMult, mSpacingAdd, true);
255         return true;
256     }
257 
258     @Override
onDraw(Canvas c)259     protected void onDraw(Canvas c) {
260         final StaticLayout layout = this.mLayout;
261         if (layout == null) {
262             return;
263         }
264 
265         final int saveCount = c.save();
266         final int innerPaddingX = this.mInnerPaddingX;
267         c.translate(getPaddingLeft() + innerPaddingX, getPaddingTop());
268 
269         final int lineCount = layout.getLineCount();
270         final Paint textPaint = this.mTextPaint;
271         final Paint paint = this.mPaint;
272         final RectF bounds = mLineBounds;
273 
274         if (Color.alpha(mBackgroundColor) > 0) {
275             final float cornerRadius = this.mCornerRadius;
276             float previousBottom = layout.getLineTop(0);
277 
278             paint.setColor(mBackgroundColor);
279             paint.setStyle(Style.FILL);
280 
281             for (int i = 0; i < lineCount; i++) {
282                 float spacesPadding = 0.0f;
283                 if (i < mPrefixSpaces.size()) {
284                     spacesPadding += mPrefixSpaces.get(i) * mWhiteSpaceWidth;
285                 }
286                 bounds.left = layout.getLineLeft(i) - innerPaddingX + spacesPadding;
287                 bounds.right = layout.getLineRight(i) + innerPaddingX;
288                 bounds.top = previousBottom;
289                 bounds.bottom = layout.getLineBottom(i);
290                 previousBottom = bounds.bottom;
291 
292                 c.drawRoundRect(bounds, cornerRadius, cornerRadius, paint);
293             }
294         }
295 
296         if (mEdgeType == CaptionStyleCompat.EDGE_TYPE_OUTLINE) {
297             textPaint.setStrokeJoin(Join.ROUND);
298             textPaint.setStrokeWidth(mOutlineWidth);
299             textPaint.setColor(mEdgeColor);
300             textPaint.setStyle(Style.FILL_AND_STROKE);
301             layout.draw(c);
302         } else if (mEdgeType == CaptionStyleCompat.EDGE_TYPE_DROP_SHADOW) {
303             textPaint.setShadowLayer(mShadowRadius, mShadowOffset, mShadowOffset, mEdgeColor);
304         } else if (mEdgeType == CaptionStyleCompat.EDGE_TYPE_RAISED
305                 || mEdgeType == CaptionStyleCompat.EDGE_TYPE_DEPRESSED) {
306             boolean raised = mEdgeType == CaptionStyleCompat.EDGE_TYPE_RAISED;
307             int colorUp = raised ? Color.WHITE : mEdgeColor;
308             int colorDown = raised ? mEdgeColor : Color.WHITE;
309             float offset = mShadowRadius / 2f;
310             textPaint.setColor(mForegroundColor);
311             textPaint.setStyle(Style.FILL);
312             textPaint.setShadowLayer(mShadowRadius, -offset, -offset, colorUp);
313             layout.draw(c);
314             textPaint.setShadowLayer(mShadowRadius, offset, offset, colorDown);
315         }
316 
317         textPaint.setColor(mForegroundColor);
318         textPaint.setStyle(Style.FILL);
319         layout.draw(c);
320         textPaint.setShadowLayer(0, 0, 0, 0);
321         c.restoreToCount(saveCount);
322     }
323 
324 }
325