diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/utils/TextDrawable.java b/mastodon/src/main/java/org/joinmastodon/android/ui/utils/TextDrawable.java
new file mode 100644
index 000000000..c280cc938
--- /dev/null
+++ b/mastodon/src/main/java/org/joinmastodon/android/ui/utils/TextDrawable.java
@@ -0,0 +1,242 @@
+package org.joinmastodon.android.ui.utils;
+
+/*
+ * Copyright 2016 Ali Muzaffar
+ *
+ * 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.
+ */
+
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.widget.TextView;
+
+import java.lang.ref.WeakReference;
+
+public class TextDrawable extends Drawable implements TextWatcher {
+ private WeakReference ref;
+ private String mText;
+ private Paint mPaint;
+ private Rect mHeightBounds;
+ private boolean mBindToViewPaint = false;
+ private float mPrevTextSize = 0;
+ private boolean mInitFitText = false;
+ private boolean mFitTextEnabled = false;
+
+ /**
+ * Create a TextDrawable using the given paint object and string
+ *
+ * @param paint
+ * @param s
+ */
+ public TextDrawable(Paint paint, String s) {
+ mText = s;
+ mPaint = new Paint(paint);
+ mHeightBounds = new Rect();
+ init();
+ }
+
+ /**
+ * Create a TextDrawable. This uses the given TextView to initialize paint and has initial text
+ * that will be drawn. Initial text can also be useful for reserving space that may otherwise
+ * not be available when setting compound drawables.
+ *
+ * @param tv The TextView / EditText using to initialize this drawable
+ * @param initialText Optional initial text to display
+ * @param bindToViewsText Should this drawable mirror the text in the TextView
+ * @param bindToViewsPaint Should this drawable mirror changes to Paint in the TextView, like textColor, typeface, alpha etc.
+ * Note, this will override any changes made using setColorFilter or setAlpha.
+ */
+ public TextDrawable(TextView tv, String initialText, boolean bindToViewsText, boolean bindToViewsPaint) {
+ this(tv.getPaint(), initialText);
+ ref = new WeakReference<>(tv);
+ if (bindToViewsText || bindToViewsPaint) {
+ if (bindToViewsText) {
+ tv.addTextChangedListener(this);
+ }
+ mBindToViewPaint = bindToViewsPaint;
+ }
+ }
+
+ /**
+ * Create a TextDrawable. This uses the given TextView to initialize paint and the text that
+ * will be drawn.
+ *
+ * @param tv The TextView / EditText using to initialize this drawable
+ * @param bindToViewsText Should this drawable mirror the text in the TextView
+ * @param bindToViewsPaint Should this drawable mirror changes to Paint in the TextView, like textColor, typeface, alpha etc.
+ * Note, this will override any changes made using setColorFilter or setAlpha.
+ */
+ public TextDrawable(TextView tv, boolean bindToViewsText, boolean bindToViewsPaint) {
+ this(tv, tv.getText().toString(), bindToViewsText, bindToViewsPaint);
+ }
+
+ /**
+ * Use the provided TextView/EditText to initialize the drawable.
+ * The Drawable will copy the Text and the Paint properties, however it will from that
+ * point on be independant of the TextView.
+ *
+ * @param tv a TextView or EditText or any of their children.
+ */
+ public TextDrawable(TextView tv) {
+ this(tv, false, false);
+ }
+
+ /**
+ * Use the provided TextView/EditText to initialize the drawable.
+ * The Drawable will copy the Paint properties, and use the provided text to initialise itself.
+ *
+ * @param tv a TextView or EditText or any of their children.
+ * @param s The String to draw
+ */
+ public TextDrawable(TextView tv, String s) {
+ this(tv, s, false, false);
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ if (mBindToViewPaint && ref.get() != null) {
+ Paint p = ref.get().getPaint();
+ canvas.drawText(mText, 0, getBounds().height(), p);
+ } else {
+ if (mInitFitText) {
+ fitTextAndInit();
+ }
+ canvas.drawText(mText, 0, getBounds().height(), mPaint);
+ }
+ }
+
+ @Override
+ public void setAlpha(int alpha) {
+ mPaint.setAlpha(alpha);
+ }
+
+ @Override
+ public void setColorFilter(ColorFilter colorFilter) {
+ mPaint.setColorFilter(colorFilter);
+ }
+
+ @Override
+ public int getOpacity() {
+ int alpha = mPaint.getAlpha();
+ if (alpha == 0) {
+ return PixelFormat.TRANSPARENT;
+ } else if (alpha == 255) {
+ return PixelFormat.OPAQUE;
+ } else {
+ return PixelFormat.TRANSLUCENT;
+ }
+ }
+
+ private void init() {
+ Rect bounds = getBounds();
+ //We want to use some character to determine the max height of the text.
+ //Otherwise if we draw something like "..." they will appear centered
+ //Here I'm just going to use the entire alphabet to determine max height.
+ mPaint.getTextBounds("1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", 0, 1, mHeightBounds);
+ //This doesn't account for leading or training white spaces.
+ //mPaint.getTextBounds(mText, 0, mText.length(), bounds);
+ float width = mPaint.measureText(mText);
+ bounds.top = mHeightBounds.top;
+ bounds.bottom = mHeightBounds.bottom;
+ bounds.right = (int) width;
+ bounds.left = 0;
+ setBounds(bounds);
+ }
+
+ public void setPaint(Paint paint) {
+ mPaint = new Paint(paint);
+ //Since this can change the font used, we need to recalculate bounds.
+ if (mFitTextEnabled && !mInitFitText) {
+ fitTextAndInit();
+ } else {
+ init();
+ }
+ invalidateSelf();
+ }
+
+ public Paint getPaint() {
+ return mPaint;
+ }
+
+ public void setText(String text) {
+ mText = text;
+ //Since this can change the bounds of the text, we need to recalculate.
+ if (mFitTextEnabled && !mInitFitText) {
+ fitTextAndInit();
+ } else {
+ init();
+ }
+ invalidateSelf();
+ }
+
+ public String getText() {
+ return mText;
+ }
+
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ setText(s.toString());
+ }
+
+ /**
+ * Make the TextDrawable match the width of the View it's associated with.
+ *
+ * Note: While this option will not work if bindToViewPaint is true.
+ *
+ * @param fitText
+ */
+ public void setFillText(boolean fitText) {
+ mFitTextEnabled = fitText;
+ if (fitText) {
+ mPrevTextSize = mPaint.getTextSize();
+ if (ref.get() != null) {
+ if (ref.get().getWidth() > 0) {
+ fitTextAndInit();
+ } else {
+ mInitFitText = true;
+ }
+ }
+ } else {
+ if (mPrevTextSize > 0) {
+ mPaint.setTextSize(mPrevTextSize);
+ }
+ init();
+ }
+ }
+
+ private void fitTextAndInit() {
+ float fitWidth = ref.get().getWidth();
+ float textWidth = mPaint.measureText(mText);
+ float multi = fitWidth / textWidth;
+ mPaint.setTextSize(mPaint.getTextSize() * multi);
+ mInitFitText = false;
+ init();
+ }
+
+}