summaryrefslogtreecommitdiffstats
path: root/src/com/android/launcher3/AutoExpandTextView.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/launcher3/AutoExpandTextView.java')
-rw-r--r--src/com/android/launcher3/AutoExpandTextView.java246
1 files changed, 246 insertions, 0 deletions
diff --git a/src/com/android/launcher3/AutoExpandTextView.java b/src/com/android/launcher3/AutoExpandTextView.java
new file mode 100644
index 000000000..ea7ac896e
--- /dev/null
+++ b/src/com/android/launcher3/AutoExpandTextView.java
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2014 Grantland Chew
+ * Copyright (C) 2015 The CyanogenMod 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.launcher3;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Color;
+import android.text.SpannableString;
+import android.text.SpannableStringBuilder;
+import android.text.TextPaint;
+import android.text.TextUtils;
+import android.text.method.TransformationMethod;
+import android.text.style.ForegroundColorSpan;
+import android.util.AttributeSet;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+
+/**
+ * A single-line TextView that resizes it's letter spacing to fit the width of the view
+ *
+ * @author Grantland Chew <grantlandchew@gmail.com>
+ * @author Linus Lee <llee@cyngn.com>
+ */
+public class AutoExpandTextView extends TextView {
+ // How precise we want to be when reaching the target textWidth size
+ private static final float PRECISION = 0.01f;
+
+ // Attributes
+ private float mPrecision;
+ private TextPaint mPaint;
+ private float[] mPositions;
+
+ public static class HighlightedText {
+ public String mText;
+ public boolean mHighlight;
+
+ public HighlightedText(String text, boolean highlight) {
+ mText = text;
+ mHighlight = highlight;
+ }
+ }
+
+ public AutoExpandTextView(Context context) {
+ super(context);
+ init(context, null, 0);
+ }
+
+ public AutoExpandTextView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init(context, attrs, 0);
+ }
+
+ public AutoExpandTextView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ init(context, attrs, defStyle);
+ }
+
+ private void init(Context context, AttributeSet attrs, int defStyle) {
+ float precision = PRECISION;
+
+ if (attrs != null) {
+ TypedArray ta = context.obtainStyledAttributes(
+ attrs,
+ R.styleable.AutofitTextView,
+ defStyle,
+ 0);
+ precision = ta.getFloat(R.styleable.AutofitTextView_precision, precision);
+ }
+
+ mPaint = new TextPaint();
+ setPrecision(precision);
+ }
+
+ /**
+ * @return the amount of precision used to calculate the correct text size to fit within it's
+ * bounds.
+ */
+ public float getPrecision() {
+ return mPrecision;
+ }
+
+ /**
+ * Set the amount of precision used to calculate the correct text size to fit within it's
+ * bounds. Lower precision is more precise and takes more time.
+ *
+ * @param precision The amount of precision.
+ */
+ public void setPrecision(float precision) {
+ if (precision != mPrecision) {
+ mPrecision = precision;
+ refitText();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setLines(int lines) {
+ super.setLines(1);
+ refitText();
+ }
+
+ /**
+ * Only allow max lines of 1
+ */
+ @Override
+ public void setMaxLines(int maxLines) {
+ super.setMaxLines(1);
+ refitText();
+ }
+
+ /**
+ * Re size the font so the specified text fits in the text box assuming the text box is the
+ * specified width.
+ */
+ private void refitText() {
+ CharSequence text = getText();
+
+ if (TextUtils.isEmpty(text)) {
+ return;
+ }
+
+ TransformationMethod method = getTransformationMethod();
+ if (method != null) {
+ text = method.getTransformation(text, this);
+ }
+ int targetWidth = getWidth() - getPaddingLeft() - getPaddingRight();
+ if (targetWidth > 0) {
+ float high = 100;
+ float low = 0;
+
+ mPaint.set(getPaint());
+ mPaint.setTextSize(getTextSize());
+ float letterSpacing = getLetterSpacing(text, mPaint, targetWidth, low, high,
+ mPrecision);
+ mPaint.setLetterSpacing(letterSpacing);
+ calculateSections(text);
+
+ super.setLetterSpacing(letterSpacing);
+ }
+ }
+
+ public float getPositionOfSection(int position) {
+ if (mPositions == null || position >= mPositions.length) {
+ return 0;
+ }
+ return mPositions[position];
+ }
+
+ /**
+ * This calculates the different horizontal positions of each character
+ */
+ private void calculateSections(CharSequence text) {
+ mPositions = new float[text.length()];
+ for (int i = 0; i < text.length(); i++) {
+ if (i == 0) {
+ mPositions[0] = mPaint.measureText(text, 0, 1) / 2;
+ } else {
+ // try to be lazy and just add the width of the newly added char
+ mPositions[i] = mPaint.measureText(text, i, i + 1) + mPositions[i - 1];
+ }
+ }
+ }
+
+ /**
+ * Sets the list of sections in the text view. This will take the first character of each
+ * and space it out in the text view using letter spacing
+ */
+ public void setSections(ArrayList<HighlightedText> sections) {
+ mPositions = null;
+ if (sections == null || sections.size() == 0) {
+ setText("");
+ return;
+ }
+
+ Resources r = getContext().getResources();
+ int highlightColor = r.getColor(R.color.app_scrubber_highlight_color);
+ int grayColor = r.getColor(R.color.app_scrubber_gray_color);
+
+ SpannableStringBuilder builder = new SpannableStringBuilder();
+ for (HighlightedText highlightText : sections) {
+ SpannableString spannable = new SpannableString(highlightText.mText.substring(0, 1));
+ spannable.setSpan(
+ new ForegroundColorSpan(highlightText.mHighlight ? highlightColor : grayColor),
+ 0, spannable.length(), 0);
+ builder.append(spannable);
+ }
+
+ setText(builder);
+ }
+
+ private static float getLetterSpacing(CharSequence text, TextPaint paint, float targetWidth,
+ float low, float high, float precision) {
+ float mid = (low + high) / 2.0f;
+ paint.setLetterSpacing(mid);
+
+ float measuredWidth = paint.measureText(text, 0, text.length());
+
+ if (high - low < precision) {
+ if (measuredWidth < targetWidth) {
+ return mid;
+ } else {
+ return low;
+ }
+ } else if (measuredWidth > targetWidth) {
+ return getLetterSpacing(text, paint, targetWidth, low, mid, precision);
+ } else if (measuredWidth < targetWidth) {
+ return getLetterSpacing(text, paint, targetWidth, mid, high, precision);
+ } else {
+ return mid;
+ }
+ }
+
+ @Override
+ protected void onTextChanged(final CharSequence text, final int start,
+ final int lengthBefore, final int lengthAfter) {
+ super.onTextChanged(text, start, lengthBefore, lengthAfter);
+ refitText();
+ }
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ super.onSizeChanged(w, h, oldw, oldh);
+ if (w != oldw) {
+ refitText();
+ }
+ }
+} \ No newline at end of file