package info.guardianproject.securereaderinterface.widgets; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.LinearGradient; import android.graphics.Rect; import android.graphics.Shader; import android.text.Layout; import android.text.TextUtils; import android.text.TextUtils.TruncateAt; import android.util.AttributeSet; import android.util.Log; import android.util.TypedValue; import android.view.ViewGroup; import android.view.ViewGroup.LayoutParams; import android.widget.TextView; import info.guardianproject.securereaderinterface.uiutil.FontManager; import info.guardianproject.securereaderinterface.uiutil.UIHelpers; import info.guardianproject.securereaderinterface.R; public class CustomFontTextView extends TextView { private Rect mBounds; private Shader mShader; private boolean mFadeLastLine = false; private float mLineSpacingExtra; private float mLineSpacingMulti; private int mMaxLines; @SuppressWarnings("unused") private CustomFontTextViewHelper mHelper; private boolean needsTruncate; private boolean inSetText; private CharSequence mTruncatedText; private TruncateAt mEllipsize; public CustomFontTextView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public CustomFontTextView(Context context) { this(context, null, 0); } public CustomFontTextView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); mBounds = new Rect(); mLineSpacingExtra = 0; mLineSpacingMulti = 1.0f; mMaxLines = Integer.MAX_VALUE; mEllipsize = null; mHelper = new CustomFontTextViewHelper(this, attrs); TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CustomFontTextView, defStyle, 0); mFadeLastLine = a.getBoolean(R.styleable.CustomFontTextView_fade_last_line, false); mMaxLines = a.getInteger(R.styleable.CustomFontTextView_android_maxLines, Integer.MAX_VALUE); if (a.hasValue(R.styleable.CustomFontTextView_android_ellipsize)) { TypedValue tv = new TypedValue(); a.getValue(R.styleable.CustomFontTextView_android_ellipsize, tv); if (tv.data == 3) mEllipsize = TruncateAt.END; } mLineSpacingExtra = a.getFloat(R.styleable.CustomFontTextView_android_lineSpacingExtra, mLineSpacingExtra); mLineSpacingMulti = a.getFloat(R.styleable.CustomFontTextView_android_lineSpacingMultiplier, mLineSpacingMulti); a.recycle(); mShader = getPaint().getShader(); setMaxLines(mMaxLines); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); if (heightMeasureSpec == MeasureSpec.UNSPECIFIED || heightMeasureSpec == MeasureSpec.AT_MOST) { if (getLineCount() > 0) { int bottom = 0; int n = getVisibleLines(this.getMeasuredHeight()); if (n > 0) { this.getLineBounds(n - 1, mBounds); bottom = mBounds.bottom; } super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(bottom + this.getPaddingBottom(), MeasureSpec.EXACTLY)); } } } public void setFadeLastLine(boolean fadeLastLine) { if (mFadeLastLine != fadeLastLine) { mFadeLastLine = fadeLastLine; fadeLastLine(); } } public CharSequence getOverflowingText() { int nVisible = getVisibleLines(); if (getLayout() != null) { Layout layout = getLayout(); int offset = layout.getLineStart(nVisible); CharSequence overflow = ""; if (offset >= 0 && offset < getText().length()) overflow = getText().subSequence(offset, getText().length()); return overflow; } return null; } public int getVisibleLines() { return getVisibleLines(getHeight()); } private int getVisibleLines(int height) { if (getLineCount() > 0 && height > 0 && getLayout() != null) { Rect bounds = new Rect(); for (int i = 0; i < getLineCount(); i++) { getLineBounds(i, bounds); if ((bounds.bottom) > (height - this.getPaddingBottom())) { return i; } } return getLineCount(); // All fit } return 0; } public CustomFontTextView createClone() { CustomFontTextView newClone = new CustomFontTextView(getContext()); newClone.setTypeface(getTypeface()); newClone.setTextSize(TypedValue.COMPLEX_UNIT_PX, this.getTextSize()); newClone.setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(), getPaddingBottom()); newClone.setLineSpacing(mLineSpacingExtra, mLineSpacingMulti); LayoutParams params = getLayoutParams(); if (params instanceof ViewGroup.MarginLayoutParams) { ViewGroup.MarginLayoutParams newParams = new ViewGroup.MarginLayoutParams(params); newClone.setLayoutParams(newParams); } return newClone; } @Override public void setLineSpacing(float add, float mult) { super.setLineSpacing(add, mult); mLineSpacingExtra = add; mLineSpacingMulti = mult; } @Override protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) { super.onTextChanged(text, start, lengthBefore, lengthAfter); fadeLastLine(); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); mTruncatedText = null; fadeLastLine(); } private void fadeLastLine() { int nVisible = getVisibleLines(); if (getLayout() != null && getLineCount() > nVisible) { if (mFadeLastLine) { int bottomStartFade = 0; int bottomEndFade = 0; bottomEndFade = getHeight() - getPaddingBottom(); bottomStartFade = bottomEndFade - UIHelpers.dpToPx(5, getContext()); int defaultColor = getTextColors().getDefaultColor(); int alphaColor = Color.argb(0, Color.red(defaultColor), Color.green(defaultColor), Color.blue(defaultColor)); LinearGradient gradient = new LinearGradient(0f, bottomStartFade, 0f, bottomEndFade, new int[] { defaultColor, alphaColor }, new float[] { 0, 1f }, Shader.TileMode.CLAMP); getPaint().setShader(gradient); return; } } if (!inSetText && !mFadeLastLine && mEllipsize == TruncateAt.END && mTruncatedText == null) { needsTruncate = true; } getPaint().setShader(mShader); // original } @Override protected void onDraw(Canvas canvas) { if (needsTruncate) { if (getLayout() != null) { int nVisible = getVisibleLines(); int lineCount = getLineCount(); int maxLines = Math.max(1, Math.min(nVisible, mMaxLines)); if (lineCount > maxLines) { needsTruncate = false; int offsetLastWhole = 0; if (maxLines > 1) offsetLastWhole = getLayout().getLineEnd(maxLines - 2); int lineEnd = getLayout().getLineEnd(lineCount - 1); CharSequence seq = getText().subSequence(offsetLastWhole, lineEnd); if (this.getTransformationMethod() != null) seq = this.getTransformationMethod().getTransformation(seq, this); CharSequence ellipsized = TextUtils.ellipsize(seq, this.getPaint(), getWidth() - getPaddingLeft() - getPaddingRight(), TruncateAt.END); if (ellipsized != seq) { inSetText = true; mTruncatedText = TextUtils.concat(getText().subSequence(0, offsetLastWhole), ellipsized); super.setText(mTruncatedText); inSetText = false; } } } } super.onDraw(canvas); } @Override public void setText(CharSequence text, BufferType type) { mTruncatedText = null; super.setText(FontManager.transformText(this, text), type); } @Override public void setMaxLines(int maxlines) { mMaxLines = maxlines; if (!inSetText && !mFadeLastLine && mEllipsize == TruncateAt.END) { needsTruncate = true; } } @Override public void setEllipsize(TruncateAt where) { mEllipsize = where; //super.setEllipsize(where); } }