content: /**
   * Copyright 2016 Ali Muzaffar
   * <p>
   * 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
   * <p>
   * http://www.apache.org/licenses/LICENSE-2.0
   * <p>
   * 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.alimuzaffar.lib.widgets;
  
  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<TextView> 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 paint object
       * @param s the string
       */
      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(), false, false);
      }
  
      /**
       * 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 whether to auto resize the text.
       */
      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();
      }
  
  }