@Override
  public boolean onTouchEvent(MotionEvent event) {
    CharSequence text = getText();
    int action = event.getAction();

    if (!(text instanceof Spannable)) {
      return super.onTouchEvent(event);
    }

    Spannable buffer = (Spannable) text;

    if (action == MotionEvent.ACTION_UP
        || action == MotionEvent.ACTION_DOWN
        || action == MotionEvent.ACTION_MOVE) {
      TextView widget = this;

      int x = (int) event.getX();
      int y = (int) event.getY();

      x -= widget.getTotalPaddingLeft();
      y -= widget.getTotalPaddingTop();

      x += widget.getScrollX();
      y += widget.getScrollY();

      Layout layout = widget.getLayout();
      int line = layout.getLineForVertical(y);
      int off = layout.getOffsetForHorizontal(line, x);

      URLSpan[] link = buffer.getSpans(off, off, URLSpan.class);

      if (link.length != 0) {
        if (action == MotionEvent.ACTION_UP) {
          if (mCurrentLink == link[0]) {
            link[0].onClick(widget);
          }
          mCurrentLink = null;
          buffer.removeSpan(mLinkFocusStyle);
        } else if (action == MotionEvent.ACTION_DOWN) {
          mCurrentLink = link[0];
          buffer.setSpan(
              mLinkFocusStyle,
              buffer.getSpanStart(link[0]),
              buffer.getSpanEnd(link[0]),
              Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        }

        return true;
      }
    }

    mCurrentLink = null;
    buffer.removeSpan(mLinkFocusStyle);

    return super.onTouchEvent(event);
  }
Beispiel #2
0
 public static Spannable getSmiledText(Context context, CharSequence text) {
   Spannable spannable = spannableFactory.newSpannable(text);
   for (Entry entry : ANDROID_EMOTICONS.entrySet()) {
     Matcher matcher = ((Pattern) entry.getKey()).matcher(spannable);
     while (matcher.find()) {
       for (Object obj :
           (ImageSpan[]) spannable.getSpans(matcher.start(), matcher.end(), ImageSpan.class)) {
         if (spannable.getSpanStart(obj) < matcher.start()
             || spannable.getSpanEnd(obj) > matcher.end()) {
           Object obj2 = null;
           break;
         }
         spannable.removeSpan(obj);
       }
       int i = 1;
       if (obj2 != null) {
         spannable.setSpan(
             new ImageSpan(context, ((Integer) entry.getValue()).intValue()),
             matcher.start(),
             matcher.end(),
             33);
       }
     }
   }
   return spannable;
 }
 /**
  * replace existing spannable with smiles
  *
  * @param context
  * @param spannable
  * @return
  */
 public static boolean addSmiles(Context context, Spannable spannable) {
   boolean hasChanges = false;
   for (Entry<Pattern, Integer> entry : emoticons.entrySet()) {
     Matcher matcher = entry.getKey().matcher(spannable);
     while (matcher.find()) {
       boolean set = true;
       for (ImageSpan span : spannable.getSpans(matcher.start(), matcher.end(), ImageSpan.class))
         if (spannable.getSpanStart(span) >= matcher.start()
             && spannable.getSpanEnd(span) <= matcher.end()) spannable.removeSpan(span);
         else {
           set = false;
           break;
         }
       if (set) {
         hasChanges = true;
         spannable.setSpan(
             new ImageSpan(context, entry.getValue()),
             matcher.start(),
             matcher.end(),
             Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
       }
     }
   }
   return hasChanges;
 }
Beispiel #4
0
  public boolean addImages(Context context, Spannable spannable) {
    Pattern refImg = Pattern.compile("\\Q[img src=\\E([a-zA-Z0-9_]+?)\\Q/]\\E");
    boolean hasChanges = false;

    Matcher matcher = refImg.matcher(spannable);
    while (matcher.find()) {
      boolean set = true;
      for (ImageSpan span : spannable.getSpans(matcher.start(), matcher.end(), ImageSpan.class)) {
        if (spannable.getSpanStart(span) >= matcher.start()
            && spannable.getSpanEnd(span) <= matcher.end()) {
          spannable.removeSpan(span);
        } else {
          set = false;
          break;
        }
      }
      String resname = spannable.subSequence(matcher.start(1), matcher.end(1)).toString().trim();
      int id = context.getResources().getIdentifier(resname, "drawable", context.getPackageName());
      Drawable icon = context.getResources().getDrawable(id); // ,this.getTheme());
      icon.setBounds(0, 0, tv_test1.getLineHeight(), tv_test1.getLineHeight());
      if (set) {
        hasChanges = true;
        spannable.setSpan(
            new ImageSpan(icon, ImageSpan.ALIGN_BASELINE),
            matcher.start(),
            matcher.end(),
            Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
      }
    }
    return hasChanges;
  }
  public static void formatSignificant(
      @Nonnull final Spannable spannable,
      @Nullable final RelativeSizeSpan insignificantRelativeSizeSpan) {
    spannable.removeSpan(SIGNIFICANT_SPAN);
    if (insignificantRelativeSizeSpan != null) spannable.removeSpan(insignificantRelativeSizeSpan);

    final Matcher m = P_SIGNIFICANT.matcher(spannable);
    if (m.find()) {
      final int pivot = m.group().length();
      if (pivot > 0)
        spannable.setSpan(SIGNIFICANT_SPAN, 0, pivot, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
      if (spannable.length() > pivot && insignificantRelativeSizeSpan != null)
        spannable.setSpan(
            insignificantRelativeSizeSpan,
            pivot,
            spannable.length(),
            Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
    }
  }
Beispiel #6
0
 /**
  * 清除textview链接的下划线
  *
  * @param textView
  */
 public static void removeLinkUnderline(TextView textView) {
   Spannable s = Spannable.Factory.getInstance().newSpannable(textView.getText());
   URLSpan[] spans = s.getSpans(0, s.length(), URLSpan.class);
   for (URLSpan span : spans) {
     int start = s.getSpanStart(span);
     int end = s.getSpanEnd(span);
     s.removeSpan(span);
     span = new TweetURLSpan(span.getURL());
     s.setSpan(span, start, end, 0);
   }
   textView.setText(s);
 }
  /**
   * Removes any {@link android.text.style.URLSpan}s from the Spannable and replaces them with a
   * non-underline version {@link LinkStyleSpan} which calls the supplied listener when clicked.
   */
  public static void stripUnderlinesAndLinkUrls(
      Spannable input, View.OnClickListener onClickListener) {
    final URLSpan[] urls = input.getSpans(0, input.length(), URLSpan.class);

    for (URLSpan span : urls) {
      final int start = input.getSpanStart(span);
      final int end = input.getSpanEnd(span);
      input.removeSpan(span);
      input.setSpan(
          new LinkStyleSpan(onClickListener), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
    }
  }
  /**
   * Scans the text of the provided Spannable and turns all occurrences of the link types indicated
   * in the mask into clickable links. If the mask is nonzero, it also removes any existing URLSpans
   * attached to the Spannable, to avoid problems if you call it repeatedly on the same text.
   */
  public static final boolean addLinks(Spannable text, int mask) {
    if (mask == 0) {
      return false;
    }

    URLSpan[] old = text.getSpans(0, text.length(), URLSpan.class);

    for (int i = old.length - 1; i >= 0; i--) {
      text.removeSpan(old[i]);
    }

    ArrayList<LinkSpec> links = new ArrayList<LinkSpec>();

    if ((mask & WEB_URLS) != 0) {
      gatherLinks(
          links,
          text,
          MyPatterns.WEB_URL,
          new String[] {"http://", "https://", "rtsp://"},
          sUrlMatchFilter,
          null);
    }

    if ((mask & EMAIL_ADDRESSES) != 0) {
      gatherLinks(links, text, Patterns.EMAIL_ADDRESS, new String[] {"mailto:"}, null, null);
    }

    if ((mask & PHONE_NUMBERS) != 0) {
      gatherLinks(
          links,
          text,
          Patterns.PHONE,
          new String[] {"tel:"},
          sPhoneNumberMatchFilter,
          sPhoneNumberTransformFilter);
    }

    if ((mask & MAP_ADDRESSES) != 0) {
      gatherMapLinks(links, text);
    }

    pruneOverlaps(links);

    if (links.size() == 0) {
      return false;
    }

    for (LinkSpec link : links) {
      applyLink(link.url, link.start, link.end, text);
    }

    return true;
  }
 private void stripUnderlines(TextView textView) {
   Spannable s = (Spannable) textView.getText();
   URLSpan[] spans = s.getSpans(0, s.length(), URLSpan.class);
   for (URLSpan span : spans) {
     int start = s.getSpanStart(span);
     int end = s.getSpanEnd(span);
     s.removeSpan(span);
     span = new URLSpanNoUnderline(span.getURL());
     s.setSpan(span, start, end, 0);
   }
   textView.setText(s);
 }
Beispiel #10
0
 public static Spannable changeHyperlinkColor(
     String content, Html.ImageGetter imageGetter, Html.TagHandler tagHandler, int color) {
   Spannable s = (Spannable) Html.fromHtml(content, imageGetter, tagHandler);
   URLSpan[] spans = s.getSpans(0, s.length(), URLSpan.class);
   for (URLSpan span : spans) {
     int start = s.getSpanStart(span);
     int end = s.getSpanEnd(span);
     s.removeSpan(span);
     span = new URLSpanNoUnderline(span.getURL(), color);
     s.setSpan(span, start, end, 0);
   }
   return s;
 }
Beispiel #11
0
  public static Spannable recentMessage(
      String content, Html.ImageGetter imageGetter, Html.TagHandler tagHandler) {
    String parse = parseNoMonkeyImage(content);

    Spannable s = (Spannable) Html.fromHtml(parse, imageGetter, null);
    URLSpan[] spans = s.getSpans(0, s.length(), URLSpan.class);
    for (URLSpan span : spans) {
      int start = s.getSpanStart(span);
      int end = s.getSpanEnd(span);
      s.removeSpan(span);
      span = new URLSpanNoUnderline(span.getURL(), 0xff999999);
      s.setSpan(span, start, end, 0);
    }
    return s;
  }
Beispiel #12
0
    public static void removeSpans(TextView textView) {
      CharSequence t = textView.getText();

      if (t instanceof Spannable) {
        Spannable span = (Spannable) t;
        URLSpan[] old = span.getSpans(0, span.length(), URLSpan.class);
        for (int i = old.length - 1; i >= 0; i--) {
          span.removeSpan(old[i]);
        }
      } else {
        SpannableString span = SpannableString.valueOf(t);
        URLSpan[] old = span.getSpans(0, span.length(), URLSpan.class);
        for (int i = old.length - 1; i >= 0; i--) {
          span.removeSpan(old[i]);
        }
      }
    }
    public void run() {
      Spannable buf = mBuffer;

      if (buf != null) {
        int st = Selection.getSelectionStart(buf);
        int en = Selection.getSelectionEnd(buf);

        int start = buf.getSpanStart(TextKeyListener.ACTIVE);
        int end = buf.getSpanEnd(TextKeyListener.ACTIVE);

        if (st == start && en == end) {
          Selection.setSelection(buf, Selection.getSelectionEnd(buf));
        }

        buf.removeSpan(Timeout.this);
      }
    }
Beispiel #14
0
 public static CharSequence getHtmlText(String text) {
   // fixes an android bug (?): text layout fails on text with nested style tags
   text = removeNestedTags(text, new String[] {"i", "b", "strong"});
   final Spanned htmlText = Html.fromHtml(text);
   if (htmlText.getSpans(0, htmlText.length(), URLSpan.class).length == 0) {
     return htmlText;
   }
   final Spannable newHtmlText = Spannable.Factory.getInstance().newSpannable(htmlText);
   for (URLSpan span : newHtmlText.getSpans(0, newHtmlText.length(), URLSpan.class)) {
     final int start = newHtmlText.getSpanStart(span);
     final int end = newHtmlText.getSpanEnd(span);
     final int flags = newHtmlText.getSpanFlags(span);
     final String url = NetworkLibrary.Instance().rewriteUrl(span.getURL(), true);
     newHtmlText.removeSpan(span);
     newHtmlText.setSpan(new URLSpan(url), start, end, flags);
   }
   return newHtmlText;
 }
Beispiel #15
0
 private static void fixLinks(Spannable spannable) {
   for (URLSpan span : spannable.getSpans(0, spannable.length(), URLSpan.class)) {
     final String url = span.getURL();
     int start = spannable.getSpanStart(span);
     int end = spannable.getSpanEnd(span);
     int flags = spannable.getSpanFlags(span);
     URLSpan newSpan =
         new URLSpan(url) {
           @Override
           public void updateDrawState(TextPaint paramTextPaint) {
             super.updateDrawState(paramTextPaint);
             paramTextPaint.setUnderlineText(false);
             paramTextPaint.setColor(0xff006FC8);
           }
         };
     spannable.removeSpan(span);
     spannable.setSpan(newSpan, start, end, flags);
   }
 }
  public void setOrRemoveSpoilerSpans(
      SpoilerRobotoTextView commentView, Spannable text, int endOfLink) {
    // add 2 to end of link since there is a white space between the link text and the spoiler
    ForegroundColorSpan[] foregroundColors =
        text.getSpans(endOfLink + 2, endOfLink + 2, ForegroundColorSpan.class);

    if (foregroundColors.length > 0) {
      text.removeSpan(foregroundColors[0]);
      commentView.setText(text);
    } else {
      for (int i = 0; i < storedSpoilerStarts.size(); i++) {
        if (storedSpoilerStarts.get(i) < endOfLink + 2
            && storedSpoilerEnds.get(i) > endOfLink + 2) {
          text.setSpan(
              storedSpoilerSpans.get(i),
              storedSpoilerStarts.get(i),
              storedSpoilerEnds.get(i),
              Spanned.SPAN_INCLUSIVE_INCLUSIVE);
        }
      }
      commentView.setText(text);
    }
  }
  /**
   * When the user selects a section of the text, this method is used to toggle the defined style on
   * it. If the selected text already has the style applied, we remove it, otherwise we apply it.
   *
   * @param style The styles that should be toggled on the selected text.
   */
  private void toggleStyle(int style) {
    // Gets the current cursor position, or the starting position of the
    // selection
    int selectionStart = this.getSelectionStart();

    // Gets the current cursor position, or the end position of the
    // selection
    // Note: The end can be smaller than the start
    int selectionEnd = this.getSelectionEnd();

    // Reverse if the case is what's noted above
    if (selectionStart > selectionEnd) {
      int temp = selectionEnd;
      selectionEnd = selectionStart;
      selectionStart = temp;
    }

    // The selectionEnd is only greater then the selectionStart position
    // when the user selected a section of the text. Otherwise, the 2
    // variables
    // should be equal (the cursor position).
    if (selectionEnd > selectionStart) {
      Spannable str = this.getText();
      boolean exists = false;
      StyleSpan[] styleSpans;

      switch (style) {
        case STYLE_BOLD:
          styleSpans = str.getSpans(selectionStart, selectionEnd, StyleSpan.class);

          // If the selected text-part already has BOLD style on it, then
          // we need to disable it
          for (int i = 0; i < styleSpans.length; i++) {
            if (styleSpans[i].getStyle() == android.graphics.Typeface.BOLD) {
              str.removeSpan(styleSpans[i]);
              exists = true;
            }
          }

          // Else we set BOLD style on it
          if (!exists) {
            str.setSpan(
                new StyleSpan(android.graphics.Typeface.BOLD),
                selectionStart,
                selectionEnd,
                Spannable.SPAN_EXCLUSIVE_INCLUSIVE);
          }

          this.setSelection(selectionStart, selectionEnd);
          break;
        case STYLE_ITALIC:
          styleSpans = str.getSpans(selectionStart, selectionEnd, StyleSpan.class);

          // If the selected text-part already has ITALIC style on it,
          // then we need to disable it
          for (int i = 0; i < styleSpans.length; i++) {
            if (styleSpans[i].getStyle() == android.graphics.Typeface.ITALIC) {
              str.removeSpan(styleSpans[i]);
              exists = true;
            }
          }

          // Else we set ITALIC style on it
          if (!exists) {
            str.setSpan(
                new StyleSpan(android.graphics.Typeface.ITALIC),
                selectionStart,
                selectionEnd,
                Spannable.SPAN_EXCLUSIVE_INCLUSIVE);
          }

          this.setSelection(selectionStart, selectionEnd);
          break;
        case STYLE_UNDERLINED:
          UnderlineSpan[] underSpan =
              str.getSpans(selectionStart, selectionEnd, UnderlineSpan.class);

          // If the selected text-part already has UNDERLINE style on it,
          // then we need to disable it
          for (int i = 0; i < underSpan.length; i++) {
            str.removeSpan(underSpan[i]);
            exists = true;
          }

          // Else we set UNDERLINE style on it
          if (!exists) {
            str.setSpan(
                new UnderlineSpan(),
                selectionStart,
                selectionEnd,
                Spannable.SPAN_EXCLUSIVE_INCLUSIVE);
          }

          this.setSelection(selectionStart, selectionEnd);
          break;
      }
    }
  }
 /** Resets all meta state to inactive. */
 public static void resetMetaState(Spannable text) {
   text.removeSpan(CAP);
   text.removeSpan(ALT);
   text.removeSpan(SYM);
   text.removeSpan(SELECTING);
 }
 /**
  * Stop selecting text. This does not actually collapse the selection; call {@link
  * android.text.Selection#setSelection} too.
  *
  * @hide pending API review
  */
 public static void stopSelecting(View view, Spannable content) {
   content.removeSpan(SELECTING);
 }
  private static void resetLock(Spannable content, Object what) {
    int current = content.getSpanFlags(what);

    if (current == LOCKED) content.removeSpan(what);
  }
  private static void adjust(Spannable content, Object what) {
    int current = content.getSpanFlags(what);

    if (current == PRESSED) content.setSpan(what, 0, 0, USED);
    else if (current == RELEASED) content.removeSpan(what);
  }
        @Override
        public boolean onTouchEvent(TextView widget, Spannable buffer, MotionEvent event) {
          int action = event.getAction();

          if (action == MotionEvent.ACTION_UP
              || action == MotionEvent.ACTION_DOWN
              || action == MotionEvent.ACTION_CANCEL) {
            int x = (int) event.getX();
            int y = (int) event.getY();

            x -= widget.getTotalPaddingLeft();
            y -= widget.getTotalPaddingTop();

            x += widget.getScrollX();
            y += widget.getScrollY();

            Layout layout = widget.getLayout();
            int line = layout.getLineForVertical(y);
            int off = layout.getOffsetForHorizontal(line, x);

            ClickableSpan[] link = buffer.getSpans(off, off, ClickableSpan.class);
            if (link.length != 0) {
              if (action == MotionEvent.ACTION_UP) {
                // 按下后松开,移除所有 BackgroundColorSpan。
                BackgroundColorSpan[] backgroundColorSpans =
                    buffer.getSpans(0, buffer.length(), BackgroundColorSpan.class);
                for (BackgroundColorSpan bkcolor : backgroundColorSpans) {
                  buffer.removeSpan(bkcolor);
                }

                // Selection.removeSelection(buffer);
                link[0].onClick(widget);
              } else if (action == MotionEvent.ACTION_DOWN) {
                // 按下,给按下的 ClickableSpan 设置 BackgroundColorSpan。
                /*
                Selection.setSelection(buffer,
                                       buffer.getSpanStart(link[0]),
                                       buffer.getSpanEnd(link[0]));
                */

                BackgroundColorSpan bkcolor = new BackgroundColorSpan(0xff89660f);
                buffer.setSpan(
                    bkcolor,
                    buffer.getSpanStart(link[0]),
                    buffer.getSpanEnd(link[0]),
                    Spanned.SPAN_INCLUSIVE_INCLUSIVE);
              } else if (action == MotionEvent.ACTION_CANCEL) {
                // 按下不松开而是移动,则变成取消事件,移除所有 BackgroundColorSpan。
                BackgroundColorSpan[] backgroundColorSpans =
                    buffer.getSpans(0, buffer.length(), BackgroundColorSpan.class);
                for (BackgroundColorSpan bkcolor : backgroundColorSpans) {
                  buffer.removeSpan(bkcolor);
                }
              }

              return true;
            } else {
              BackgroundColorSpan[] backgroundColorSpans =
                  buffer.getSpans(0, buffer.length(), BackgroundColorSpan.class);
              for (BackgroundColorSpan bkcolor : backgroundColorSpans) {
                buffer.removeSpan(bkcolor);
              }
              // Selection.removeSelection(buffer);
            }
          }

          return super.onTouchEvent(widget, buffer, event);
        }
  /**
   * Applies formatting to selected text, or marks the entry for a new text style at the current
   * cursor position
   *
   * @param toggleButton button from formatting bar
   * @param tag HTML tag name for text style
   */
  private void onFormatButtonClick(ToggleButton toggleButton, String tag) {
    Spannable s = mContentEditText.getText();
    if (s == null) return;
    int selectionStart = mContentEditText.getSelectionStart();
    mStyleStart = selectionStart;
    int selectionEnd = mContentEditText.getSelectionEnd();

    if (selectionStart > selectionEnd) {
      int temp = selectionEnd;
      selectionEnd = selectionStart;
      selectionStart = temp;
    }

    Class styleClass = null;
    if (tag.equals(TAG_FORMAT_BAR_BUTTON_STRONG) || tag.equals(TAG_FORMAT_BAR_BUTTON_EM))
      styleClass = StyleSpan.class;
    else if (tag.equals(TAG_FORMAT_BAR_BUTTON_UNDERLINE)) styleClass = WPUnderlineSpan.class;
    else if (tag.equals(TAG_FORMAT_BAR_BUTTON_STRIKE)) styleClass = StrikethroughSpan.class;
    else if (tag.equals(TAG_FORMAT_BAR_BUTTON_QUOTE)) styleClass = QuoteSpan.class;

    if (styleClass == null) return;

    Object[] allSpans = s.getSpans(selectionStart, selectionEnd, styleClass);
    boolean textIsSelected = selectionEnd > selectionStart;
    if (mIsLocalDraft) {
      // Local drafts can use the rich text editor. Yay!
      boolean shouldAddSpan = true;
      for (Object span : allSpans) {
        if (span instanceof StyleSpan) {
          StyleSpan styleSpan = (StyleSpan) span;
          if ((styleSpan.getStyle() == Typeface.BOLD && !tag.equals(TAG_FORMAT_BAR_BUTTON_STRONG))
              || (styleSpan.getStyle() == Typeface.ITALIC
                  && !tag.equals(TAG_FORMAT_BAR_BUTTON_EM))) {
            continue;
          }
        }
        if (!toggleButton.isChecked() && textIsSelected) {
          // If span exists and text is selected, remove the span
          s.removeSpan(span);
          shouldAddSpan = false;
          break;
        } else if (!toggleButton.isChecked()) {
          // Remove span at cursor point if button isn't checked
          Object[] spans = s.getSpans(mStyleStart - 1, mStyleStart, styleClass);
          for (Object removeSpan : spans) {
            selectionStart = s.getSpanStart(removeSpan);
            selectionEnd = s.getSpanEnd(removeSpan);
            s.removeSpan(removeSpan);
          }
        }
      }

      if (shouldAddSpan) {
        if (tag.equals(TAG_FORMAT_BAR_BUTTON_STRONG)) {
          s.setSpan(
              new StyleSpan(android.graphics.Typeface.BOLD),
              selectionStart,
              selectionEnd,
              Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
        } else if (tag.equals(TAG_FORMAT_BAR_BUTTON_EM)) {
          s.setSpan(
              new StyleSpan(android.graphics.Typeface.ITALIC),
              selectionStart,
              selectionEnd,
              Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
        } else {
          try {
            s.setSpan(
                styleClass.newInstance(),
                selectionStart,
                selectionEnd,
                Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
          } catch (java.lang.InstantiationException e) {
            AppLog.e(T.POSTS, e);
          } catch (IllegalAccessException e) {
            AppLog.e(T.POSTS, e);
          }
        }
      }
    } else {
      // Add HTML tags when editing an existing post
      String startTag = "<" + tag + ">";
      String endTag = "</" + tag + ">";
      Editable content = mContentEditText.getText();
      if (textIsSelected) {
        content.insert(selectionStart, startTag);
        content.insert(selectionEnd + startTag.length(), endTag);
        toggleButton.setChecked(false);
        mContentEditText.setSelection(selectionEnd + startTag.length() + endTag.length());
      } else if (toggleButton.isChecked()) {
        content.insert(selectionStart, startTag);
        mContentEditText.setSelection(selectionEnd + startTag.length());
      } else if (!toggleButton.isChecked()) {
        content.insert(selectionEnd, endTag);
        mContentEditText.setSelection(selectionEnd + endTag.length());
      }
    }
  }
 public void onSpanChanged(Spannable buf, Object what, int s, int e, int start, int stop) {
   if (what == Selection.SELECTION_END) {
     buf.removeSpan(TextKeyListener.ACTIVE);
     removeTimeouts(buf);
   }
 }
  @Override
  public boolean onTouchEvent(TextView widget, Spannable buffer, MotionEvent event) {
    if (mGray == null) {
      mGray =
          new BackgroundColorSpan(
              widget.getContext().getResources().getColor(R.color.selector_gray));
    }

    mIsLinkHit = false;

    int action = event.getAction();

    if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_UP) {
      int x = (int) event.getX();
      int y = (int) event.getY();

      /*if (DEBUG) {
      	Log.d(TAG, "x = " + x + " y = " + y);
      }*/

      x -= widget.getTotalPaddingLeft();
      y -= widget.getTotalPaddingTop();

      /*if (DEBUG) {
      	Log.d(TAG, "x = " + x + " y = " + y);
      }*/

      x += widget.getScrollX();
      y += widget.getScrollY();

      int line = widget.getLayout().getLineForVertical(y);
      int offset = widget.getLayout().getOffsetForHorizontal(line, x);

      ClickableSpan[] spans = buffer.getSpans(offset, offset, ClickableSpan.class);

      /*if (DEBUG) {
      	Log.d(TAG, "x = " + x + " y = " + y);
      	Log.d(TAG, "line = " + line + " offset = " + offset);
      	Log.d(TAG, "spans.lenth = " + spans.length);
      }*/

      if (spans.length != 0) {
        int start = buffer.getSpanStart(spans[0]);
        int end = buffer.getSpanEnd(spans[0]);

        mIsLinkHit = true;

        if (action == MotionEvent.ACTION_DOWN) {
          /*if (DEBUG) {
          	Log.d(TAG, "Down event detected");
          }*/

          buffer.setSpan(mGray, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        } else if (action == MotionEvent.ACTION_UP) {
          /*if (DEBUG) {
          	Log.d(TAG, "Up event detected");
          }*/

          spans[0].onClick(widget);

          buffer.removeSpan(mGray);
        }

        return true;
      }
    } else {
      buffer.removeSpan(mGray);
    }

    return Touch.onTouchEvent(widget, buffer, event);
  }
 private static void removeUnprintable(Spannable spannable, int start, int end) {
   UnprintableSpan[] spans = spannable.getSpans(start, end, UnprintableSpan.class);
   for (UnprintableSpan s : spans) spannable.removeSpan(s);
 }
  private static void removeTimeouts(Spannable buf) {
    Timeout[] timeout = buf.getSpans(0, buf.length(), Timeout.class);

    for (int i = 0; i < timeout.length; i++) {
      Timeout t = timeout[i];

      t.removeCallbacks(t);
      t.mBuffer = null;
      buf.removeSpan(t);
    }
  }