diff options
author | Alan Viverette <alanv@google.com> | 2013-08-21 13:43:00 -0700 |
---|---|---|
committer | Alan Viverette <alanv@google.com> | 2013-08-21 13:43:00 -0700 |
commit | 398ec0368bc82dad6f3d120cd15e01b741320436 (patch) | |
tree | 738049b420998a318bbb87c67d38d8e337dddc67 | |
parent | 6b223c6a5be788ca28d5d911ab10650be673684b (diff) | |
download | frameworks_base-398ec0368bc82dad6f3d120cd15e01b741320436.zip frameworks_base-398ec0368bc82dad6f3d120cd15e01b741320436.tar.gz frameworks_base-398ec0368bc82dad6f3d120cd15e01b741320436.tar.bz2 |
Move caption view from Settings into framework
Renamed to SubtitleView, moved dimensions into resources.
BUG: 10260603
Change-Id: I5d86ee21ca260540ba3201c5295a5c1353e2d963
-rw-r--r-- | core/java/com/android/internal/widget/SubtitleView.java | 365 | ||||
-rw-r--r-- | core/res/res/values/dimens.xml | 12 | ||||
-rwxr-xr-x | core/res/res/values/symbols.xml | 5 |
3 files changed, 382 insertions, 0 deletions
diff --git a/core/java/com/android/internal/widget/SubtitleView.java b/core/java/com/android/internal/widget/SubtitleView.java new file mode 100644 index 0000000..13101512 --- /dev/null +++ b/core/java/com/android/internal/widget/SubtitleView.java @@ -0,0 +1,365 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.widget; + +import android.content.ContentResolver; +import android.content.Context; +import android.content.res.Resources.Theme; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Paint.Join; +import android.graphics.Paint.Style; +import android.graphics.RectF; +import android.graphics.Typeface; +import android.text.Layout.Alignment; +import android.text.StaticLayout; +import android.text.TextPaint; +import android.text.TextUtils; +import android.util.AttributeSet; +import android.util.DisplayMetrics; +import android.util.TypedValue; +import android.view.View; +import android.view.accessibility.CaptioningManager.CaptionStyle; + +public class SubtitleView extends View { + // Ratio of inner padding to font size. + private static final float INNER_PADDING_RATIO = 0.125f; + + // Styled dimensions. + private final float mCornerRadius; + private final float mOutlineWidth; + private final float mShadowRadius; + private final float mShadowOffsetX; + private final float mShadowOffsetY; + + /** Temporary rectangle used for computing line bounds. */ + private final RectF mLineBounds = new RectF(); + + /** Temporary array used for computing line wrapping. */ + private float[] mTextWidths; + + /** Reusable string builder used for holding text. */ + private final StringBuilder mText = new StringBuilder(); + private final StringBuilder mBreakText = new StringBuilder(); + + private TextPaint mPaint; + + private int mForegroundColor; + private int mBackgroundColor; + private int mEdgeColor; + private int mEdgeType; + + private boolean mHasMeasurements; + private int mLastMeasuredWidth; + private StaticLayout mLayout; + + private float mSpacingMult = 1; + private float mSpacingAdd = 0; + private int mInnerPaddingX = 0; + + public SubtitleView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public SubtitleView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs); + + final Theme theme = context.getTheme(); + final TypedArray a = theme.obtainStyledAttributes( + attrs, android.R.styleable.TextView, defStyle, 0); + + CharSequence text = ""; + int textSize = 15; + + final int n = a.getIndexCount(); + for (int i = 0; i < n; i++) { + int attr = a.getIndex(i); + + switch (attr) { + case android.R.styleable.TextView_text: + text = a.getText(attr); + break; + case android.R.styleable.TextView_lineSpacingExtra: + mSpacingAdd = a.getDimensionPixelSize(attr, (int) mSpacingAdd); + break; + case android.R.styleable.TextView_lineSpacingMultiplier: + mSpacingMult = a.getFloat(attr, mSpacingMult); + break; + case android.R.styleable.TextAppearance_textSize: + textSize = a.getDimensionPixelSize(attr, textSize); + break; + } + } + + // Set up density-dependent properties. + // TODO: Move these to a default style. + final Resources res = getContext().getResources(); + final DisplayMetrics m = res.getDisplayMetrics(); + mCornerRadius = res.getDimension(com.android.internal.R.dimen.subtitle_corner_radius); + mOutlineWidth = res.getDimension(com.android.internal.R.dimen.subtitle_outline_width); + mShadowRadius = res.getDimension(com.android.internal.R.dimen.subtitle_shadow_radius); + mShadowOffsetX = res.getDimension(com.android.internal.R.dimen.subtitle_shadow_offset); + mShadowOffsetY = mShadowOffsetX; + + final TextPaint paint = new TextPaint(); + paint.setAntiAlias(true); + paint.setSubpixelText(true); + + mPaint = paint; + + setText(text); + setTextSize(textSize); + } + + public void setText(int resId) { + final CharSequence text = getContext().getText(resId); + setText(text); + } + + public void setText(CharSequence text) { + mText.setLength(0); + mText.append(text); + + mHasMeasurements = false; + + requestLayout(); + } + + public void setForegroundColor(int color) { + mForegroundColor = color; + + invalidate(); + } + + @Override + public void setBackgroundColor(int color) { + mBackgroundColor = color; + + invalidate(); + } + + public void setEdgeType(int edgeType) { + mEdgeType = edgeType; + + invalidate(); + } + + public void setEdgeColor(int color) { + mEdgeColor = color; + + invalidate(); + } + + public void setTextSize(float size) { + final DisplayMetrics metrics = getContext().getResources().getDisplayMetrics(); + final float pixels = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, size, metrics); + if (mPaint.getTextSize() != size) { + mHasMeasurements = false; + mInnerPaddingX = (int) (size * INNER_PADDING_RATIO + 0.5f); + mPaint.setTextSize(size); + + requestLayout(); + } + } + + public void setTypeface(Typeface typeface) { + if (mPaint.getTypeface() != typeface) { + mHasMeasurements = false; + mPaint.setTypeface(typeface); + + requestLayout(); + } + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + final int widthSpec = MeasureSpec.getSize(widthMeasureSpec); + + if (computeMeasurements(widthSpec)) { + final StaticLayout layout = mLayout; + + // Account for padding. + final int paddingX = mPaddingLeft + mPaddingRight + mInnerPaddingX * 2; + final int width = layout.getWidth() + paddingX; + final int height = layout.getHeight() + mPaddingTop + mPaddingBottom; + setMeasuredDimension(width, height); + } else { + setMeasuredDimension(MEASURED_STATE_TOO_SMALL, MEASURED_STATE_TOO_SMALL); + } + } + + @Override + public void onLayout(boolean changed, int l, int t, int r, int b) { + final int width = r - l; + + computeMeasurements(width); + } + + private boolean computeMeasurements(int maxWidth) { + if (mHasMeasurements && maxWidth == mLastMeasuredWidth) { + return true; + } + + // Account for padding. + final int paddingX = mPaddingLeft + mPaddingRight + mInnerPaddingX; + maxWidth -= paddingX; + + if (maxWidth <= 0) { + return false; + } + + final TextPaint paint = mPaint; + final CharSequence text = mText; + final int textLength = text.length(); + if (mTextWidths == null || mTextWidths.length < textLength) { + mTextWidths = new float[textLength]; + } + + final float[] textWidths = mTextWidths; + paint.getTextWidths(text, 0, textLength, textWidths); + + // Compute total length. + float runLength = 0; + for (int i = 0; i < textLength; i++) { + runLength += textWidths[i]; + } + + final int lineCount = (int) (runLength / maxWidth) + 1; + final int lineLength = (int) (runLength / lineCount); + + // Build line break buffer. + final StringBuilder breakText = mBreakText; + breakText.setLength(0); + + int line = 0; + int lastBreak = 0; + int maxRunLength = 0; + runLength = 0; + for (int i = 0; i < textLength; i++) { + if (runLength > lineLength) { + final CharSequence sequence = text.subSequence(lastBreak, i); + final int trimmedLength = TextUtils.getTrimmedLength(sequence); + breakText.append(sequence, 0, trimmedLength); + breakText.append('\n'); + lastBreak = i; + runLength = 0; + } + + runLength += textWidths[i]; + + if (runLength > maxRunLength) { + maxRunLength = (int) Math.ceil(runLength); + } + } + breakText.append(text.subSequence(lastBreak, textLength)); + + mHasMeasurements = true; + mLastMeasuredWidth = maxWidth; + + mLayout = new StaticLayout(breakText, paint, maxRunLength, Alignment.ALIGN_LEFT, + mSpacingMult, mSpacingAdd, true); + + return true; + } + + public void setStyle(int styleId) { + final Context context = mContext; + final ContentResolver cr = context.getContentResolver(); + final CaptionStyle style; + if (styleId == CaptionStyle.PRESET_CUSTOM) { + style = CaptionStyle.getCustomStyle(cr); + } else { + style = CaptionStyle.PRESETS[styleId]; + } + + mForegroundColor = style.foregroundColor; + mBackgroundColor = style.backgroundColor; + mEdgeType = style.edgeType; + mEdgeColor = style.edgeColor; + mHasMeasurements = false; + + final Typeface typeface = style.getTypeface(); + setTypeface(typeface); + + requestLayout(); + } + + @Override + protected void onDraw(Canvas c) { + final StaticLayout layout = mLayout; + if (layout == null) { + return; + } + + final int saveCount = c.save(); + final int innerPaddingX = mInnerPaddingX; + c.translate(mPaddingLeft + innerPaddingX, mPaddingTop); + + final RectF bounds = mLineBounds; + final int lineCount = layout.getLineCount(); + final Paint paint = layout.getPaint(); + paint.setShadowLayer(0, 0, 0, 0); + + final int backgroundColor = mBackgroundColor; + if (Color.alpha(backgroundColor) > 0) { + paint.setColor(backgroundColor); + paint.setStyle(Style.FILL); + + final float cornerRadius = mCornerRadius; + float previousBottom = layout.getLineTop(0); + + for (int i = 0; i < lineCount; i++) { + bounds.left = layout.getLineLeft(i) - innerPaddingX; + bounds.right = layout.getLineRight(i) + innerPaddingX; + bounds.top = previousBottom; + bounds.bottom = layout.getLineBottom(i); + + previousBottom = bounds.bottom; + + c.drawRoundRect(bounds, cornerRadius, cornerRadius, paint); + } + } + + final int edgeType = mEdgeType; + if (edgeType == CaptionStyle.EDGE_TYPE_OUTLINE) { + paint.setColor(mEdgeColor); + paint.setStyle(Style.FILL_AND_STROKE); + paint.setStrokeJoin(Join.ROUND); + paint.setStrokeWidth(mOutlineWidth); + + for (int i = 0; i < lineCount; i++) { + layout.drawText(c, i, i); + } + } + + if (edgeType == CaptionStyle.EDGE_TYPE_DROP_SHADOW) { + paint.setShadowLayer(mShadowRadius, mShadowOffsetX, mShadowOffsetY, mEdgeColor); + } + + paint.setColor(mForegroundColor); + paint.setStyle(Style.FILL); + + for (int i = 0; i < lineCount; i++) { + layout.drawText(c, i, i); + } + + c.restoreToCount(saveCount); + } +} diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index 00caac9..e902354 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -344,4 +344,16 @@ security mode. --> <dimen name="kg_small_widget_height">160dp</dimen> + <!-- Rounded corner radius for video subtitles. --> + <dimen name="subtitle_corner_radius">2dp</dimen> + + <!-- Shadow radius for video subtitles. --> + <dimen name="subtitle_shadow_radius">2dp</dimen> + + <!-- Shadow offset for video subtitles. --> + <dimen name="subtitle_shadow_offset">2dp</dimen> + + <!-- Outline width for video subtitles. --> + <dimen name="subtitle_outline_width">2dp</dimen> + </resources> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 076922f..8d3e474 100755 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1661,4 +1661,9 @@ <java-symbol type="attr" name="actionModeWebSearchDrawable" /> <java-symbol type="string" name="websearch" /> + <!-- From SubtitleView --> + <java-symbol type="dimen" name="subtitle_corner_radius" /> + <java-symbol type="dimen" name="subtitle_shadow_radius" /> + <java-symbol type="dimen" name="subtitle_shadow_offset" /> + <java-symbol type="dimen" name="subtitle_outline_width" /> </resources> |