@SuppressWarnings("NullableProblems")
  @Override
  public boolean onTouchEvent(MotionEvent event) {

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

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

    boolean retValue = false;

    switch (action) {
      case MotionEvent.ACTION_DOWN:
        retValue = selectBubble(x, y);
        break;
      case MotionEvent.ACTION_MOVE:
        if (selectedSpan != null) {
          selectedSpan.setPressed(spanContains(selectedSpan, x, y), (Spannable) layout().getText());
          retValue = true;
        }
        break;
      case MotionEvent.ACTION_UP:
        if (listener != null && selectedSpan != null && spanContains(selectedSpan, x, y)) {
          listener.onBubbleClicked(this, selectedSpan);
        }

      case MotionEvent.ACTION_CANCEL:
        if (selectedSpan != null) {
          retValue = true;
          if (-1 != ((Spannable) truncatedLayout.getText()).getSpanEnd(selectedSpan)) {
            selectedSpan.setPressed(false, (Spannable) truncatedLayout.getText());
          }
          if (-1 != ((Spannable) expandedLayout.getText()).getSpanEnd(selectedSpan)) {
            selectedSpan.setPressed(false, (Spannable) expandedLayout.getText());
          }
        }
        selectedSpan = null;
        break;

      default:
        break;
    }

    invalidate();
    return retValue;
  }
 public boolean selectBubble(int x, int y) {
   for (BubbleSpan span : spans) {
     if (spanContains(span, x, y)) {
       span.setPressed(true, (Spannable) layout().getText());
       selectedSpan = span;
       return true;
     }
   }
   return false;
 }