Loading res/layout/preset_picker_item.xml +3 −3 Original line number Diff line number Diff line Loading @@ -27,9 +27,9 @@ <com.android.settings.accessibility.CaptioningTextView android:id="@+id/preview" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:text="@string/captioning_preview_characters" /> </FrameLayout> Loading res/values/arrays.xml +3 −3 Original line number Diff line number Diff line Loading @@ -735,11 +735,11 @@ <!-- Values for captioning font size preference. --> <string-array name="captioning_font_size_selector_values" translatable="false" > <item>6.0</item> <item>12.0</item> <item>24.0</item> <item>32.0</item> <item>48.0</item> <item>72.0</item> <item>96.0</item> </string-array> <!-- Titles for captioning character edge type preference. [CHAR LIMIT=35] --> Loading Loading @@ -854,8 +854,8 @@ <!-- Titles for captioning text style preset preference. [CHAR LIMIT=35] --> <string-array name="captioning_preset_selector_titles" > <item>Black on white</item> <item>White on black</item> <item>Black on white</item> <item>Yellow on black</item> <item>Yellow on blue</item> <item>Custom</item> Loading src/com/android/settings/accessibility/CaptioningTextView.java +276 −215 Original line number Diff line number Diff line Loading @@ -18,292 +18,353 @@ package com.android.settings.accessibility; import android.content.ContentResolver; import android.content.Context; import android.content.res.ColorStateList; import android.content.res.Resources.Theme; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Paint.Cap; import android.graphics.Paint.Join; import android.graphics.Paint.Style; import android.os.Parcel; import android.support.v4.view.ViewCompat; import android.text.Editable; import android.text.ParcelableSpan; 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.text.style.CharacterStyle; import android.text.style.UpdateAppearance; import android.util.AttributeSet; import android.view.accessibility.CaptioningManager; import android.util.DisplayMetrics; import android.util.TypedValue; import android.view.View; import android.view.accessibility.CaptioningManager.CaptionStyle; import android.widget.TextView; public class CaptioningTextView extends TextView { private MutableBackgroundColorSpan mBackgroundSpan; private ColorStateList mOutlineColorState; private float mOutlineWidth; private int mOutlineColor; public class CaptioningTextView extends View { // Ratio of inner padding to font size. private static final float INNER_PADDING_RATIO = 0.125f; private int mEdgeType = CaptionStyle.EDGE_TYPE_NONE; private int mEdgeColor = Color.TRANSPARENT; private float mEdgeWidth = 0; // Default style dimensions in dips. private static final float CORNER_RADIUS = 2.0f; private static final float OUTLINE_WIDTH = 2.0f; private static final float SHADOW_RADIUS = 2.0f; private static final float SHADOW_OFFSET_X = 2.0f; private static final float SHADOW_OFFSET_Y = 2.0f; private boolean mHasBackground = false; // Styled dimensions. private final float mCornerRadius; private final float mOutlineWidth; private final float mShadowRadius; private final float mShadowOffsetX; private final float mShadowOffsetY; public CaptioningTextView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } /** 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 CaptioningTextView(Context context, AttributeSet attrs) { super(context, attrs); this(context, attrs, 0); } public CaptioningTextView(Context context) { super(context); } public CaptioningTextView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs); public void applyStyleAndFontSize(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]; } final Theme theme = context.getTheme(); final TypedArray a = theme.obtainStyledAttributes( attrs, android.R.styleable.TextView, defStyle, 0); setTextColor(style.foregroundColor); setBackgroundColor(style.backgroundColor); setTypeface(style.getTypeface()); CharSequence text = ""; int textSize = 15; // Clears all outlines. applyEdge(style.edgeType, style.edgeColor, 4.0f); final int n = a.getIndexCount(); for (int i = 0; i < n; i++) { int attr = a.getIndex(i); final float fontSize = CaptioningManager.getFontSize(cr); if (fontSize != 0) { setTextSize(fontSize); 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; } } /** * Applies an edge preset using a combination of {@link #setOutlineLayer} * and {@link #setShadowLayer}. Any subsequent calls to either of these * methods will invalidate the applied preset. * * @param type Type of edge to apply, one of: * <ul> * <li>{@link CaptionStyle#EDGE_TYPE_NONE} * <li>{@link CaptionStyle#EDGE_TYPE_OUTLINE} * <li>{@link CaptionStyle#EDGE_TYPE_DROP_SHADOW} * </ul> * @param color Edge color as a packed 32-bit ARGB color. * @param width Width of the edge in pixels. */ public void applyEdge(int type, int color, float width) { if (mEdgeType != type || mEdgeColor != color || mEdgeWidth != width) { final int textColor = getTextColors().getDefaultColor(); switch (type) { case CaptionStyle.EDGE_TYPE_DROP_SHADOW: setOutlineLayer(0, 0); super.setShadowLayer(width, width, width, color); break; case CaptionStyle.EDGE_TYPE_OUTLINE: setOutlineLayer(width, color); super.setShadowLayer(0, 0, 0, 0); break; default: super.setShadowLayer(0, 0, 0, 0); setOutlineLayer(0, 0); // Set up density-dependent properties. // TODO: Move these to a default style. final DisplayMetrics m = getContext().getResources().getDisplayMetrics(); mCornerRadius = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, CORNER_RADIUS, m); mOutlineWidth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, OUTLINE_WIDTH, m); mShadowRadius = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, SHADOW_RADIUS, m); mShadowOffsetX = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, SHADOW_OFFSET_Y, m); mShadowOffsetY = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, SHADOW_OFFSET_X, m); final TextPaint paint = new TextPaint(); paint.setAntiAlias(true); paint.setSubpixelText(true); mPaint = paint; setText(text); setTextSize(textSize); } mEdgeType = type; mEdgeColor = color; mEdgeWidth = width; 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(); } @Override public void setShadowLayer(float radius, float dx, float dy, int color) { mEdgeType = CaptionStyle.EDGE_TYPE_NONE; public void setForegroundColor(int color) { mForegroundColor = color; super.setShadowLayer(radius, dx, dy, color); invalidate(); } /** * Gives the text an outline of the specified pixel width and color. */ public void setOutlineLayer(float width, int color) { width *= 2.0f; @Override public void setBackgroundColor(int color) { mBackgroundColor = color; invalidate(); } mEdgeType = CaptionStyle.EDGE_TYPE_NONE; public void setEdgeType(int edgeType) { mEdgeType = edgeType; if (mOutlineColor != color || mOutlineWidth != width) { mOutlineColorState = ColorStateList.valueOf(color); mOutlineColor = color; mOutlineWidth = width; invalidate(); } // TODO: Remove after display list bug is fixed. if (width > 0 && Color.alpha(color) != 0) { setLayerType(ViewCompat.LAYER_TYPE_SOFTWARE, null); } else { setLayerType(ViewCompat.LAYER_TYPE_HARDWARE, null); 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(); } } /** * @return the color of the outline layer * @see #setOutlineLayer(float, int) */ public int getOutlineColor() { return mOutlineColor; } public void setTypeface(Typeface typeface) { if (mPaint.getTypeface() != typeface) { mHasMeasurements = false; mPaint.setTypeface(typeface); /** * @return the width of the outline layer * @see #setOutlineLayer(float, int) */ public float getOutlineWidth() { return mOutlineWidth; requestLayout(); } } @Override public Editable getEditableText() { final CharSequence text = getText(); if (text instanceof Editable) { return (Editable) text; } protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { final int widthSpec = MeasureSpec.getSize(widthMeasureSpec); setText(text, BufferType.EDITABLE); return (Editable) getText(); } if (computeMeasurements(widthSpec)) { final StaticLayout layout = mLayout; @Override public void setBackgroundColor(int color) { if (Color.alpha(color) == 0) { if (mHasBackground) { mHasBackground = false; getEditableText().removeSpan(mBackgroundSpan); } } else { if (mBackgroundSpan == null) { mBackgroundSpan = new MutableBackgroundColorSpan(color); // 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 { mBackgroundSpan.setColor(color); setMeasuredDimension(MEASURED_STATE_TOO_SMALL, MEASURED_STATE_TOO_SMALL); } if (mHasBackground) { invalidate(); } else { mHasBackground = true; getEditableText().setSpan(mBackgroundSpan, 0, length(), 0); } @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; } @Override protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) { super.onTextChanged(text, start, lengthBefore, lengthAfter); // Account for padding. final int paddingX = mPaddingLeft + mPaddingRight + mInnerPaddingX; maxWidth -= paddingX; if (mBackgroundSpan != null) { getEditableText().setSpan(mBackgroundSpan, 0, lengthAfter, 0); 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]; } @Override protected void onDraw(Canvas c) { if (mOutlineWidth > 0 && Color.alpha(mOutlineColor) > 0) { final TextPaint textPaint = getPaint(); final Paint.Style previousStyle = textPaint.getStyle(); final ColorStateList previousColors = getTextColors(); textPaint.setStyle(Style.STROKE); textPaint.setStrokeWidth(mOutlineWidth); textPaint.setStrokeCap(Cap.ROUND); textPaint.setStrokeJoin(Join.ROUND); setTextColor(mOutlineColorState); // Remove the shadow. final float shadowRadius = getShadowRadius(); final float shadowDx = getShadowDx(); final float shadowDy = getShadowDy(); final int shadowColor = getShadowColor(); if (shadowRadius > 0) { setShadowLayer(0, 0, 0, 0); } // Draw outline and background only. super.onDraw(c); // Restore the shadow. if (shadowRadius > 0) { setShadowLayer(shadowRadius, shadowDx, shadowDy, shadowColor); } // Restore original settings. textPaint.setStyle(previousStyle); setTextColor(previousColors); // Remove the background. final int color; if (mBackgroundSpan != null) { color = mBackgroundSpan.getBackgroundColor(); mBackgroundSpan.setColor(Color.TRANSPARENT); } else { color = 0; 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]; } // Draw foreground only. super.onDraw(c); final int lineCount = (int) (runLength / maxWidth) + 1; final int lineLength = (int) (runLength / lineCount); // Build line break buffer. final StringBuilder breakText = mBreakText; breakText.setLength(0); // Restore the background. if (mBackgroundSpan != null) { mBackgroundSpan.setColor(color); 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; } } else { super.onDraw(c); runLength += textWidths[i]; if (runLength > maxRunLength) { maxRunLength = (int) Math.ceil(runLength); } } breakText.append(text.subSequence(lastBreak, textLength)); public static class MutableBackgroundColorSpan extends CharacterStyle implements UpdateAppearance, ParcelableSpan { private int mColor; mHasMeasurements = true; mLastMeasuredWidth = maxWidth; public MutableBackgroundColorSpan(int color) { mColor = color; mLayout = new StaticLayout(breakText, paint, maxRunLength, Alignment.ALIGN_LEFT, mSpacingMult, mSpacingAdd, true); return true; } public MutableBackgroundColorSpan(Parcel src) { mColor = src.readInt(); 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]; } public void setColor(int color) { mColor = color; mForegroundColor = style.foregroundColor; mBackgroundColor = style.backgroundColor; mEdgeType = style.edgeType; mEdgeColor = style.edgeColor; mHasMeasurements = false; final Typeface typeface = style.getTypeface(); setTypeface(typeface); requestLayout(); } @Override public int getSpanTypeId() { return TextUtils.BACKGROUND_COLOR_SPAN; protected void onDraw(Canvas c) { final StaticLayout layout = mLayout; if (layout == null) { return; } @Override public int describeContents() { return 0; 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); } } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mColor); 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); } } public int getBackgroundColor() { return mColor; if (edgeType == CaptionStyle.EDGE_TYPE_DROP_SHADOW) { paint.setShadowLayer(mShadowRadius, mShadowOffsetX, mShadowOffsetY, mEdgeColor); } @Override public void updateDrawState(TextPaint ds) { ds.bgColor = mColor; paint.setColor(mForegroundColor); paint.setStyle(Style.FILL); for (int i = 0; i < lineCount; i++) { layout.drawText(c, i, i); } c.restoreToCount(saveCount); } } src/com/android/settings/accessibility/EdgeTypePreference.java +4 −2 Original line number Diff line number Diff line Loading @@ -50,12 +50,14 @@ public class EdgeTypePreference extends ListDialogPreference { protected void onBindListItem(View view, int index) { final float fontSize = CaptioningManager.getFontSize(getContext().getContentResolver()); final CaptioningTextView preview = (CaptioningTextView) view.findViewById(R.id.preview); preview.setTextColor(Color.WHITE); preview.setForegroundColor(Color.WHITE); preview.setBackgroundColor(Color.TRANSPARENT); preview.setTextSize(fontSize); final int value = getValueAt(index); preview.applyEdge(value, Color.BLACK, 4.0f); preview.setEdgeType(value); preview.setEdgeColor(Color.BLACK); final CharSequence title = getTitleAt(index); if (title != null) { Loading src/com/android/settings/accessibility/ToggleCaptioningPreferenceFragment.java +4 −1 Original line number Diff line number Diff line Loading @@ -86,10 +86,13 @@ public class ToggleCaptioningPreferenceFragment extends Fragment { } public static void applyCaptionProperties(CaptioningTextView previewText, int styleId) { previewText.applyStyleAndFontSize(styleId); previewText.setStyle(styleId); final Context context = previewText.getContext(); final ContentResolver cr = context.getContentResolver(); final float fontSize = CaptioningManager.getFontSize(cr); previewText.setTextSize(fontSize); final Locale locale = CaptioningManager.getLocale(cr); if (locale != null) { final CharSequence localizedText = AccessibilityUtils.getTextForLocale( Loading Loading
res/layout/preset_picker_item.xml +3 −3 Original line number Diff line number Diff line Loading @@ -27,9 +27,9 @@ <com.android.settings.accessibility.CaptioningTextView android:id="@+id/preview" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:text="@string/captioning_preview_characters" /> </FrameLayout> Loading
res/values/arrays.xml +3 −3 Original line number Diff line number Diff line Loading @@ -735,11 +735,11 @@ <!-- Values for captioning font size preference. --> <string-array name="captioning_font_size_selector_values" translatable="false" > <item>6.0</item> <item>12.0</item> <item>24.0</item> <item>32.0</item> <item>48.0</item> <item>72.0</item> <item>96.0</item> </string-array> <!-- Titles for captioning character edge type preference. [CHAR LIMIT=35] --> Loading Loading @@ -854,8 +854,8 @@ <!-- Titles for captioning text style preset preference. [CHAR LIMIT=35] --> <string-array name="captioning_preset_selector_titles" > <item>Black on white</item> <item>White on black</item> <item>Black on white</item> <item>Yellow on black</item> <item>Yellow on blue</item> <item>Custom</item> Loading
src/com/android/settings/accessibility/CaptioningTextView.java +276 −215 Original line number Diff line number Diff line Loading @@ -18,292 +18,353 @@ package com.android.settings.accessibility; import android.content.ContentResolver; import android.content.Context; import android.content.res.ColorStateList; import android.content.res.Resources.Theme; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Paint.Cap; import android.graphics.Paint.Join; import android.graphics.Paint.Style; import android.os.Parcel; import android.support.v4.view.ViewCompat; import android.text.Editable; import android.text.ParcelableSpan; 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.text.style.CharacterStyle; import android.text.style.UpdateAppearance; import android.util.AttributeSet; import android.view.accessibility.CaptioningManager; import android.util.DisplayMetrics; import android.util.TypedValue; import android.view.View; import android.view.accessibility.CaptioningManager.CaptionStyle; import android.widget.TextView; public class CaptioningTextView extends TextView { private MutableBackgroundColorSpan mBackgroundSpan; private ColorStateList mOutlineColorState; private float mOutlineWidth; private int mOutlineColor; public class CaptioningTextView extends View { // Ratio of inner padding to font size. private static final float INNER_PADDING_RATIO = 0.125f; private int mEdgeType = CaptionStyle.EDGE_TYPE_NONE; private int mEdgeColor = Color.TRANSPARENT; private float mEdgeWidth = 0; // Default style dimensions in dips. private static final float CORNER_RADIUS = 2.0f; private static final float OUTLINE_WIDTH = 2.0f; private static final float SHADOW_RADIUS = 2.0f; private static final float SHADOW_OFFSET_X = 2.0f; private static final float SHADOW_OFFSET_Y = 2.0f; private boolean mHasBackground = false; // Styled dimensions. private final float mCornerRadius; private final float mOutlineWidth; private final float mShadowRadius; private final float mShadowOffsetX; private final float mShadowOffsetY; public CaptioningTextView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } /** 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 CaptioningTextView(Context context, AttributeSet attrs) { super(context, attrs); this(context, attrs, 0); } public CaptioningTextView(Context context) { super(context); } public CaptioningTextView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs); public void applyStyleAndFontSize(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]; } final Theme theme = context.getTheme(); final TypedArray a = theme.obtainStyledAttributes( attrs, android.R.styleable.TextView, defStyle, 0); setTextColor(style.foregroundColor); setBackgroundColor(style.backgroundColor); setTypeface(style.getTypeface()); CharSequence text = ""; int textSize = 15; // Clears all outlines. applyEdge(style.edgeType, style.edgeColor, 4.0f); final int n = a.getIndexCount(); for (int i = 0; i < n; i++) { int attr = a.getIndex(i); final float fontSize = CaptioningManager.getFontSize(cr); if (fontSize != 0) { setTextSize(fontSize); 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; } } /** * Applies an edge preset using a combination of {@link #setOutlineLayer} * and {@link #setShadowLayer}. Any subsequent calls to either of these * methods will invalidate the applied preset. * * @param type Type of edge to apply, one of: * <ul> * <li>{@link CaptionStyle#EDGE_TYPE_NONE} * <li>{@link CaptionStyle#EDGE_TYPE_OUTLINE} * <li>{@link CaptionStyle#EDGE_TYPE_DROP_SHADOW} * </ul> * @param color Edge color as a packed 32-bit ARGB color. * @param width Width of the edge in pixels. */ public void applyEdge(int type, int color, float width) { if (mEdgeType != type || mEdgeColor != color || mEdgeWidth != width) { final int textColor = getTextColors().getDefaultColor(); switch (type) { case CaptionStyle.EDGE_TYPE_DROP_SHADOW: setOutlineLayer(0, 0); super.setShadowLayer(width, width, width, color); break; case CaptionStyle.EDGE_TYPE_OUTLINE: setOutlineLayer(width, color); super.setShadowLayer(0, 0, 0, 0); break; default: super.setShadowLayer(0, 0, 0, 0); setOutlineLayer(0, 0); // Set up density-dependent properties. // TODO: Move these to a default style. final DisplayMetrics m = getContext().getResources().getDisplayMetrics(); mCornerRadius = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, CORNER_RADIUS, m); mOutlineWidth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, OUTLINE_WIDTH, m); mShadowRadius = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, SHADOW_RADIUS, m); mShadowOffsetX = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, SHADOW_OFFSET_Y, m); mShadowOffsetY = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, SHADOW_OFFSET_X, m); final TextPaint paint = new TextPaint(); paint.setAntiAlias(true); paint.setSubpixelText(true); mPaint = paint; setText(text); setTextSize(textSize); } mEdgeType = type; mEdgeColor = color; mEdgeWidth = width; 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(); } @Override public void setShadowLayer(float radius, float dx, float dy, int color) { mEdgeType = CaptionStyle.EDGE_TYPE_NONE; public void setForegroundColor(int color) { mForegroundColor = color; super.setShadowLayer(radius, dx, dy, color); invalidate(); } /** * Gives the text an outline of the specified pixel width and color. */ public void setOutlineLayer(float width, int color) { width *= 2.0f; @Override public void setBackgroundColor(int color) { mBackgroundColor = color; invalidate(); } mEdgeType = CaptionStyle.EDGE_TYPE_NONE; public void setEdgeType(int edgeType) { mEdgeType = edgeType; if (mOutlineColor != color || mOutlineWidth != width) { mOutlineColorState = ColorStateList.valueOf(color); mOutlineColor = color; mOutlineWidth = width; invalidate(); } // TODO: Remove after display list bug is fixed. if (width > 0 && Color.alpha(color) != 0) { setLayerType(ViewCompat.LAYER_TYPE_SOFTWARE, null); } else { setLayerType(ViewCompat.LAYER_TYPE_HARDWARE, null); 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(); } } /** * @return the color of the outline layer * @see #setOutlineLayer(float, int) */ public int getOutlineColor() { return mOutlineColor; } public void setTypeface(Typeface typeface) { if (mPaint.getTypeface() != typeface) { mHasMeasurements = false; mPaint.setTypeface(typeface); /** * @return the width of the outline layer * @see #setOutlineLayer(float, int) */ public float getOutlineWidth() { return mOutlineWidth; requestLayout(); } } @Override public Editable getEditableText() { final CharSequence text = getText(); if (text instanceof Editable) { return (Editable) text; } protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { final int widthSpec = MeasureSpec.getSize(widthMeasureSpec); setText(text, BufferType.EDITABLE); return (Editable) getText(); } if (computeMeasurements(widthSpec)) { final StaticLayout layout = mLayout; @Override public void setBackgroundColor(int color) { if (Color.alpha(color) == 0) { if (mHasBackground) { mHasBackground = false; getEditableText().removeSpan(mBackgroundSpan); } } else { if (mBackgroundSpan == null) { mBackgroundSpan = new MutableBackgroundColorSpan(color); // 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 { mBackgroundSpan.setColor(color); setMeasuredDimension(MEASURED_STATE_TOO_SMALL, MEASURED_STATE_TOO_SMALL); } if (mHasBackground) { invalidate(); } else { mHasBackground = true; getEditableText().setSpan(mBackgroundSpan, 0, length(), 0); } @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; } @Override protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) { super.onTextChanged(text, start, lengthBefore, lengthAfter); // Account for padding. final int paddingX = mPaddingLeft + mPaddingRight + mInnerPaddingX; maxWidth -= paddingX; if (mBackgroundSpan != null) { getEditableText().setSpan(mBackgroundSpan, 0, lengthAfter, 0); 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]; } @Override protected void onDraw(Canvas c) { if (mOutlineWidth > 0 && Color.alpha(mOutlineColor) > 0) { final TextPaint textPaint = getPaint(); final Paint.Style previousStyle = textPaint.getStyle(); final ColorStateList previousColors = getTextColors(); textPaint.setStyle(Style.STROKE); textPaint.setStrokeWidth(mOutlineWidth); textPaint.setStrokeCap(Cap.ROUND); textPaint.setStrokeJoin(Join.ROUND); setTextColor(mOutlineColorState); // Remove the shadow. final float shadowRadius = getShadowRadius(); final float shadowDx = getShadowDx(); final float shadowDy = getShadowDy(); final int shadowColor = getShadowColor(); if (shadowRadius > 0) { setShadowLayer(0, 0, 0, 0); } // Draw outline and background only. super.onDraw(c); // Restore the shadow. if (shadowRadius > 0) { setShadowLayer(shadowRadius, shadowDx, shadowDy, shadowColor); } // Restore original settings. textPaint.setStyle(previousStyle); setTextColor(previousColors); // Remove the background. final int color; if (mBackgroundSpan != null) { color = mBackgroundSpan.getBackgroundColor(); mBackgroundSpan.setColor(Color.TRANSPARENT); } else { color = 0; 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]; } // Draw foreground only. super.onDraw(c); final int lineCount = (int) (runLength / maxWidth) + 1; final int lineLength = (int) (runLength / lineCount); // Build line break buffer. final StringBuilder breakText = mBreakText; breakText.setLength(0); // Restore the background. if (mBackgroundSpan != null) { mBackgroundSpan.setColor(color); 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; } } else { super.onDraw(c); runLength += textWidths[i]; if (runLength > maxRunLength) { maxRunLength = (int) Math.ceil(runLength); } } breakText.append(text.subSequence(lastBreak, textLength)); public static class MutableBackgroundColorSpan extends CharacterStyle implements UpdateAppearance, ParcelableSpan { private int mColor; mHasMeasurements = true; mLastMeasuredWidth = maxWidth; public MutableBackgroundColorSpan(int color) { mColor = color; mLayout = new StaticLayout(breakText, paint, maxRunLength, Alignment.ALIGN_LEFT, mSpacingMult, mSpacingAdd, true); return true; } public MutableBackgroundColorSpan(Parcel src) { mColor = src.readInt(); 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]; } public void setColor(int color) { mColor = color; mForegroundColor = style.foregroundColor; mBackgroundColor = style.backgroundColor; mEdgeType = style.edgeType; mEdgeColor = style.edgeColor; mHasMeasurements = false; final Typeface typeface = style.getTypeface(); setTypeface(typeface); requestLayout(); } @Override public int getSpanTypeId() { return TextUtils.BACKGROUND_COLOR_SPAN; protected void onDraw(Canvas c) { final StaticLayout layout = mLayout; if (layout == null) { return; } @Override public int describeContents() { return 0; 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); } } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mColor); 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); } } public int getBackgroundColor() { return mColor; if (edgeType == CaptionStyle.EDGE_TYPE_DROP_SHADOW) { paint.setShadowLayer(mShadowRadius, mShadowOffsetX, mShadowOffsetY, mEdgeColor); } @Override public void updateDrawState(TextPaint ds) { ds.bgColor = mColor; paint.setColor(mForegroundColor); paint.setStyle(Style.FILL); for (int i = 0; i < lineCount; i++) { layout.drawText(c, i, i); } c.restoreToCount(saveCount); } }
src/com/android/settings/accessibility/EdgeTypePreference.java +4 −2 Original line number Diff line number Diff line Loading @@ -50,12 +50,14 @@ public class EdgeTypePreference extends ListDialogPreference { protected void onBindListItem(View view, int index) { final float fontSize = CaptioningManager.getFontSize(getContext().getContentResolver()); final CaptioningTextView preview = (CaptioningTextView) view.findViewById(R.id.preview); preview.setTextColor(Color.WHITE); preview.setForegroundColor(Color.WHITE); preview.setBackgroundColor(Color.TRANSPARENT); preview.setTextSize(fontSize); final int value = getValueAt(index); preview.applyEdge(value, Color.BLACK, 4.0f); preview.setEdgeType(value); preview.setEdgeColor(Color.BLACK); final CharSequence title = getTitleAt(index); if (title != null) { Loading
src/com/android/settings/accessibility/ToggleCaptioningPreferenceFragment.java +4 −1 Original line number Diff line number Diff line Loading @@ -86,10 +86,13 @@ public class ToggleCaptioningPreferenceFragment extends Fragment { } public static void applyCaptionProperties(CaptioningTextView previewText, int styleId) { previewText.applyStyleAndFontSize(styleId); previewText.setStyle(styleId); final Context context = previewText.getContext(); final ContentResolver cr = context.getContentResolver(); final float fontSize = CaptioningManager.getFontSize(cr); previewText.setTextSize(fontSize); final Locale locale = CaptioningManager.getLocale(cr); if (locale != null) { final CharSequence localizedText = AccessibilityUtils.getTextForLocale( Loading