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