@Override
  public boolean onTouchEvent(MotionEvent event) {
    float x = event.getX();
    float y = event.getY();

    boolean result = false;
    if (event.getAction() == MotionEvent.ACTION_DOWN) {
      if (delegate != null) {
        if (currentMessageObject.type == 11 && imageReceiver.isInsideImage(x, y)) {
          imagePressed = true;
          result = true;
        }
        if (result) {
          startCheckLongPress();
        }
      }
    } else {
      if (event.getAction() != MotionEvent.ACTION_MOVE) {
        cancelCheckLongPress();
      }
      if (imagePressed) {
        if (event.getAction() == MotionEvent.ACTION_UP) {
          imagePressed = false;
          if (delegate != null) {
            delegate.didClickedImage(this);
            playSoundEffect(SoundEffectConstants.CLICK);
          }
        } else if (event.getAction() == MotionEvent.ACTION_CANCEL) {
          imagePressed = false;
        } else if (event.getAction() == MotionEvent.ACTION_MOVE) {
          if (!imageReceiver.isInsideImage(x, y)) {
            imagePressed = false;
          }
        }
      }
    }
    if (!result) {
      if (event.getAction() == MotionEvent.ACTION_DOWN
          || pressedLink != null && event.getAction() == MotionEvent.ACTION_UP) {
        if (x >= textX && y >= textY && x <= textX + textWidth && y <= textY + textHeight) {
          y -= textY;
          x -= textXLeft;

          final int line = textLayout.getLineForVertical((int) y);
          final int off = textLayout.getOffsetForHorizontal(line, x);
          final float left = textLayout.getLineLeft(line);
          if (left <= x
              && left + textLayout.getLineWidth(line) >= x
              && currentMessageObject.messageText instanceof Spannable) {
            Spannable buffer = (Spannable) currentMessageObject.messageText;
            URLSpan[] link = buffer.getSpans(off, off, URLSpan.class);

            if (link.length != 0) {
              if (event.getAction() == MotionEvent.ACTION_DOWN) {
                pressedLink = link[0];
                result = true;
              } else {
                if (link[0] == pressedLink) {
                  if (delegate != null) {
                    delegate.needOpenUserProfile(Integer.parseInt(link[0].getURL()));
                  }
                  result = true;
                }
              }
            } else {
              pressedLink = null;
            }
          } else {
            pressedLink = null;
          }
        } else {
          pressedLink = null;
        }
      }
    }

    if (!result) {
      result = super.onTouchEvent(event);
    }

    return result;
  }
  private void build(int width) {

    positions.clear();
    width = width - getPaddingLeft() - getPaddingRight();

    if (width <= 0 || TextUtils.isEmpty(text)) {
      truncatedLayout = expandedLayout = null;
      return;
    }

    for (BubbleSpan span : spans) {
      span.resetWidth(width);
    }

    try {

      truncatedLayout =
          expandedLayout =
              new StaticLayout(
                  text, textPaint, width, Layout.Alignment.ALIGN_NORMAL, lineSpacing, 1, false);

      if (maxLines > 0 && truncatedLayout.getLineCount() > maxLines) {

        int lineEnd = truncatedLayout.getLineEnd(maxLines - 1);
        int offset = -1;
        StaticLayout sl =
            new StaticLayout(
                moreText, textPaint, width, Layout.Alignment.ALIGN_NORMAL, lineSpacing, 1, false);

        sl.getWidth();
        while (truncatedLayout.getLineCount() > maxLines && lineEnd > 0) {

          if (offset == -1
              && truncatedLayout.getLineWidth(maxLines - 1) + sl.getLineWidth(0) > width) {

            offset =
                truncatedLayout.getOffsetForHorizontal(maxLines - 1, width - sl.getLineWidth(0));

            lineEnd = offset;

          } else if (offset > 0) {
            lineEnd--;
          }

          SpannableStringBuilder textTruncated =
              new SpannableStringBuilder(text.subSequence(0, lineEnd));
          textTruncated.append(moreText);

          truncatedLayout =
              new StaticLayout(
                  textTruncated,
                  textPaint,
                  width,
                  Layout.Alignment.ALIGN_NORMAL,
                  lineSpacing,
                  1,
                  false);
        }
      }
    } catch (java.lang.ArrayIndexOutOfBoundsException e) {
      return;
    }

    if (truncated) {
      recomputeSpans((Spannable) truncatedLayout.getText());
    } else {
      recomputeSpans((Spannable) expandedLayout.getText());
    }

    for (BubbleSpan span : spans) {
      positions.put(span, span.rect(this));
    }
  }
 public static StaticLayout createStaticLayout(
     CharSequence source,
     int bufstart,
     int bufend,
     TextPaint paint,
     int outerWidth,
     Layout.Alignment align,
     float spacingMult,
     float spacingAdd,
     boolean includePad,
     TextUtils.TruncateAt ellipsize,
     int ellipsisWidth,
     int maxLines) {
   /*if (Build.VERSION.SDK_INT >= 14) {
       init();
       try {
           sConstructorArgs[0] = source;
           sConstructorArgs[1] = bufstart;
           sConstructorArgs[2] = bufend;
           sConstructorArgs[3] = paint;
           sConstructorArgs[4] = outerWidth;
           sConstructorArgs[5] = align;
           sConstructorArgs[6] = sTextDirection;
           sConstructorArgs[7] = spacingMult;
           sConstructorArgs[8] = spacingAdd;
           sConstructorArgs[9] = includePad;
           sConstructorArgs[10] = ellipsize;
           sConstructorArgs[11] = ellipsisWidth;
           sConstructorArgs[12] = maxLines;
           return sConstructor.newInstance(sConstructorArgs);
       } catch (Exception e) {
           FileLog.e("tmessages", e);
       }
   }*/
   try {
     if (maxLines == 1) {
       CharSequence text =
           TextUtils.ellipsize(source, paint, ellipsisWidth, TextUtils.TruncateAt.END);
       return new StaticLayout(
           text, 0, text.length(), paint, outerWidth, align, spacingMult, spacingAdd, includePad);
     } else {
       StaticLayout layout =
           new StaticLayout(source, paint, outerWidth, align, spacingMult, spacingAdd, includePad);
       if (layout.getLineCount() <= maxLines) {
         return layout;
       } else {
         int off;
         float left = layout.getLineLeft(maxLines - 1);
         if (left != 0) {
           off = layout.getOffsetForHorizontal(maxLines - 1, left);
         } else {
           off = layout.getOffsetForHorizontal(maxLines - 1, layout.getLineWidth(maxLines - 1));
         }
         SpannableStringBuilder stringBuilder =
             new SpannableStringBuilder(source.subSequence(0, Math.max(0, off - 1)));
         stringBuilder.append("\u2026");
         return new StaticLayout(
             stringBuilder, paint, outerWidth, align, spacingMult, spacingAdd, includePad);
       }
     }
   } catch (Exception e) {
     FileLog.e("tmessages", e);
   }
   return null;
 }
  @Override
  public boolean onTouchEvent(MotionEvent event) {
    float x = event.getX();
    float y = event.getY();

    boolean result = false;
    int side = AndroidUtilities.dp(48);
    if (textLayout != null) {
      if (event.getAction() == MotionEvent.ACTION_DOWN
          || pressedLink != null && event.getAction() == MotionEvent.ACTION_UP) {
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
          resetPressedLink();
          try {
            int x2 = (int) (x - textX);
            int y2 = (int) (y - textY);
            final int line = textLayout.getLineForVertical(y2);
            final int off = textLayout.getOffsetForHorizontal(line, x2);

            final float left = textLayout.getLineLeft(line);
            if (left <= x2 && left + textLayout.getLineWidth(line) >= x2) {
              Spannable buffer = (Spannable) textLayout.getText();
              ClickableSpan[] link = buffer.getSpans(off, off, ClickableSpan.class);
              if (link.length != 0) {
                resetPressedLink();
                pressedLink = link[0];
                result = true;
                try {
                  int start = buffer.getSpanStart(pressedLink);
                  urlPath.setCurrentLayout(textLayout, start);
                  textLayout.getSelectionPath(start, buffer.getSpanEnd(pressedLink), urlPath);
                } catch (Exception e) {
                  FileLog.e("tmessages", e);
                }
              } else {
                resetPressedLink();
              }
            } else {
              resetPressedLink();
            }
          } catch (Exception e) {
            resetPressedLink();
            FileLog.e("tmessages", e);
          }
        } else if (pressedLink != null) {
          try {
            if (pressedLink instanceof URLSpanNoUnderline) {
              String url = ((URLSpanNoUnderline) pressedLink).getURL();
              if (url.startsWith("@") || url.startsWith("#") || url.startsWith("/")) {
                if (delegate != null) {
                  delegate.didPressUrl(url);
                }
              }
            } else {
              pressedLink.onClick(this);
            }
          } catch (Exception e) {
            FileLog.e("tmessages", e);
          }
          resetPressedLink();
          result = true;
        }
      } else if (event.getAction() == MotionEvent.ACTION_CANCEL) {
        resetPressedLink();
      }
    }
    return result || super.onTouchEvent(event);
  }