static Action newReplaceText(CharSequence text, int start, int end) {
      if (start < 0 || start > end) {
        Log.e(LOGTAG, "invalid replace text offsets: " + start + " to " + end);
        throw new IllegalArgumentException("invalid replace text offsets");
      }

      int actionType = TYPE_REPLACE_TEXT;

      if (text instanceof Spanned) {
        final Spanned spanned = (Spanned) text;
        final Object[] spans = spanned.getSpans(0, spanned.length(), Object.class);

        for (Object span : spans) {
          if ((spanned.getSpanFlags(span) & Spanned.SPAN_COMPOSING) != 0) {
            actionType = TYPE_COMPOSE_TEXT;
            break;
          }
        }
      }

      final Action action = new Action(actionType);
      action.mSequence = text;
      action.mStart = start;
      action.mEnd = end;
      return action;
    }
Beispiel #2
0
  /**
   * Extract the text from a HTML based string. This is similar to what HTML.fromHtml(...) does, but
   * this method also removes the embedded images instead of replacing them by a small rectangular
   * representation character.
   *
   * @param html
   * @return
   */
  public static String extractText(CharSequence html) {
    String result = html.toString();

    // recognize images in textview HTML contents
    if (html instanceof Spanned) {
      Spanned text = (Spanned) html;
      Object[] styles = text.getSpans(0, text.length(), Object.class);
      ArrayList<Pair<Integer, Integer>> removals = new ArrayList<Pair<Integer, Integer>>();
      for (Object style : styles) {
        if (style instanceof ImageSpan) {
          int start = text.getSpanStart(style);
          int end = text.getSpanEnd(style);
          removals.add(Pair.of(start, end));
        }
      }

      // sort reversed and delete image spans
      Collections.sort(
          removals,
          new Comparator<Pair<Integer, Integer>>() {

            @Override
            public int compare(Pair<Integer, Integer> lhs, Pair<Integer, Integer> rhs) {
              return rhs.getRight().compareTo(lhs.getRight());
            }
          });
      result = text.toString();
      for (Pair<Integer, Integer> removal : removals) {
        result = result.substring(0, removal.getLeft()) + result.substring(removal.getRight());
      }
    }

    // some line breaks are still in the text, source is unknown
    return StringUtils.replace(result, "<br />", "\n").trim();
  }
  public void store(View v) {
    Spanned s = (Spanned) mTextView.getText();
    BackgroundColorSpan[] spans = s.getSpans(0, s.length(), BackgroundColorSpan.class);

    BufferedWriter bw = null;
    try {
      int len = spans.length;
      bw = new BufferedWriter(new FileWriter(mFile));
      bw.write(String.valueOf(len));
      bw.newLine();
      for (BackgroundColorSpan span : spans) {
        int start = s.getSpanStart(span);
        int end = s.getSpanEnd(span);
        int color = span.getBackgroundColor();
        bw.write("" + start + "," + end + "," + color);
        bw.newLine();
      }
      bw.write(mText);
      clear(v);
    } catch (IOException e) {
      Log.e(TAG, "IO error", e);
    } finally {
      closeQuietly(bw);
    }
  }
Beispiel #4
0
  /** See android.text.HtmlToSpannedConverter#getLast(android.text.Spanned, java.lang.Class) */
  private static <T> T getLastSpan(Spanned text, Class<T> kind) {
    T[] spans = text.getSpans(0, text.length(), kind);

    if (spans.length == 0) {
      return null;
    } else {
      return spans[spans.length - 1];
    }
  }
 private static CharSequence removeJumpingBeansSpans(Spanned text) {
   SpannableStringBuilder sbb = new SpannableStringBuilder(text.toString());
   Object[] spans = text.getSpans(0, text.length(), Object.class);
   for (Object span : spans) {
     if (!(span instanceof JumpingBeansSpan)) {
       sbb.setSpan(span, text.getSpanStart(span), text.getSpanEnd(span), text.getSpanFlags(span));
     }
   }
   return sbb;
 }
 @Override
 public void onFinishTemporaryDetach() {
   super.onFinishTemporaryDetach();
   if (mContainsImages && getText() instanceof Spanned) {
     Spanned text = (Spanned) getText();
     TextInlineImageSpan[] spans = text.getSpans(0, text.length(), TextInlineImageSpan.class);
     for (TextInlineImageSpan span : spans) {
       span.onFinishTemporaryDetach();
     }
   }
 }
Beispiel #7
0
  public ClickableSpan[] getLinkAt(float x, float y) {
    Integer offset = findOffsetForPosition(x, y);

    if (offset == null) {
      return null;
    }

    Spanned text = (Spanned) childView.getText();
    ClickableSpan[] spans = text.getSpans(offset, offset, ClickableSpan.class);

    return spans;
  }
Beispiel #8
0
  public boolean hasLinkAt(float x, float y) {
    Integer offset = findOffsetForPosition(x, y);

    if (offset == null) {
      return false;
    }

    Spanned text = (Spanned) childView.getText();
    ClickableSpan[] spans = text.getSpans(offset, offset, ClickableSpan.class);

    return spans != null && spans.length > 0;
  }
Beispiel #9
0
  /**
   * Returns null if not boring; the width, ascent, and descent in the provided Metrics object (or a
   * new one if the provided one was null) if boring.
   */
  public static Metrics isBoring(CharSequence text, TextPaint paint, Metrics metrics) {
    char[] temp = TextUtils.obtain(500);
    int len = text.length();
    boolean boring = true;

    outer:
    for (int i = 0; i < len; i += 500) {
      int j = i + 500;

      if (j > len) j = len;

      TextUtils.getChars(text, i, j, temp, 0);

      int n = j - i;

      for (int a = 0; a < n; a++) {
        char c = temp[a];

        if (c == '\n' || c == '\t' || c >= FIRST_RIGHT_TO_LEFT) {
          boring = false;
          break outer;
        }
      }
    }

    TextUtils.recycle(temp);

    if (boring && text instanceof Spanned) {
      Spanned sp = (Spanned) text;
      Object[] styles = sp.getSpans(0, text.length(), ParagraphStyle.class);
      if (styles.length > 0) {
        boring = false;
      }
    }

    if (boring) {
      Metrics fm = metrics;
      if (fm == null) {
        fm = new Metrics();
      }

      int wid;

      synchronized (sTemp) {
        wid = (int) (FloatMath.ceil(Styled.measureText(paint, sTemp, text, 0, text.length(), fm)));
      }
      fm.width = wid;
      return fm;
    } else {
      return null;
    }
  }
 @Override
 protected boolean verifyDrawable(Drawable drawable) {
   if (mContainsImages && getText() instanceof Spanned) {
     Spanned text = (Spanned) getText();
     TextInlineImageSpan[] spans = text.getSpans(0, text.length(), TextInlineImageSpan.class);
     for (TextInlineImageSpan span : spans) {
       if (span.getDrawable() == drawable) {
         return true;
       }
     }
   }
   return super.verifyDrawable(drawable);
 }
 @Override
 public void invalidateDrawable(Drawable drawable) {
   if (mContainsImages && getText() instanceof Spanned) {
     Spanned text = (Spanned) getText();
     TextInlineImageSpan[] spans = text.getSpans(0, text.length(), TextInlineImageSpan.class);
     for (TextInlineImageSpan span : spans) {
       if (span.getDrawable() == drawable) {
         invalidate();
       }
     }
   }
   super.invalidateDrawable(drawable);
 }
  @Test
  public void testTextDecorationLineLineThroughApplied() {
    UIManagerModule uiManager = getUIManagerModule();

    ReactRootView rootView =
        createText(
            uiManager,
            JavaOnlyMap.of(ViewProps.TEXT_DECORATION_LINE, "line-through"),
            JavaOnlyMap.of(ReactTextShadowNode.PROP_TEXT, "test text"));

    TextView textView = (TextView) rootView.getChildAt(0);
    Spanned text = (Spanned) textView.getText();
    UnderlineSpan[] underlineSpans = text.getSpans(0, text.length(), UnderlineSpan.class);
    StrikethroughSpan strikeThroughSpan = getSingleSpan(textView, StrikethroughSpan.class);
    assertThat(underlineSpans).hasSize(0);
    assertThat(strikeThroughSpan instanceof StrikethroughSpan).isTrue();
  }
 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
 public static String getImageUrl(final Context context) {
   if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) return null;
   final ClipboardManager cm =
       (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
   final ClipData primaryClip = cm.getPrimaryClip();
   if (primaryClip.getItemCount() > 0) {
     final ClipData.Item item = primaryClip.getItemAt(0);
     final CharSequence styledText = item.coerceToStyledText(context);
     if (styledText instanceof Spanned) {
       final Spanned spanned = (Spanned) styledText;
       final ImageSpan[] imageSpans = spanned.getSpans(0, spanned.length(), ImageSpan.class);
       if (imageSpans.length == 1) return imageSpans[0].getSource();
     }
   }
   return null;
 }
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;
 }
  @Override
  public int reactTagForTouch(float touchX, float touchY) {
    Spanned text = (Spanned) getText();
    int target = getId();

    int x = (int) touchX;
    int y = (int) touchY;

    Layout layout = getLayout();
    if (layout == null) {
      // If the layout is null, the view hasn't been properly laid out yet. Therefore, we can't find
      // the exact text tag that has been touched, and the correct tag to return is the default one.
      return target;
    }
    int line = layout.getLineForVertical(y);

    int lineStartX = (int) layout.getLineLeft(line);
    int lineEndX = (int) layout.getLineRight(line);

    // TODO(5966918): Consider extending touchable area for text spans by some DP constant
    if (x >= lineStartX && x <= lineEndX) {
      int index = layout.getOffsetForHorizontal(line, x);

      // We choose the most inner span (shortest) containing character at the given index
      // if no such span can be found we will send the textview's react id as a touch handler
      // In case when there are more than one spans with same length we choose the last one
      // from the spans[] array, since it correspond to the most inner react element
      ReactTagSpan[] spans = text.getSpans(index, index, ReactTagSpan.class);

      if (spans != null) {
        int targetSpanTextLength = text.length();
        for (int i = 0; i < spans.length; i++) {
          int spanStart = text.getSpanStart(spans[i]);
          int spanEnd = text.getSpanEnd(spans[i]);
          if (spanEnd > index && (spanEnd - spanStart) <= targetSpanTextLength) {
            target = spans[i].getReactTag();
            targetSpanTextLength = (spanEnd - spanStart);
          }
        }
      }
    }

    return target;
  }
  /**
   * Returns the advance widths for a uniform left-to-right run of text with no style changes in the
   * middle of the run. If any style is replacement text, the first character will isCancelled the
   * width of the replacement and the remaining characters will isCancelled a width of 0.
   *
   * @param paint the paint, will not be modified
   * @param workPaint a paint to modify; on return will reflect the original paint plus the effect
   *     of all spans on the run
   * @param text the text
   * @param start the start of the run
   * @param end the limit of the run
   * @param widths array to receive the advance widths of the characters. Must be at least a large
   *     as (end - start).
   * @param fmi FontMetrics information; can be null
   * @return the actual number of widths returned
   */
  public static int getTextWidths(
      TextPaint paint,
      TextPaint workPaint,
      Spanned text,
      int start,
      int end,
      float[] widths,
      Paint.FontMetricsInt fmi) {
    MetricAffectingSpan[] spans = text.getSpans(start, end, MetricAffectingSpan.class);

    ReplacementSpan replacement = null;
    workPaint.set(paint);

    for (MetricAffectingSpan span : spans) {
      if (span instanceof ReplacementSpan) {
        replacement = (ReplacementSpan) span;
      } else {
        span.updateMeasureState(workPaint);
      }
    }

    if (replacement == null) {
      workPaint.getFontMetricsInt(fmi);
      workPaint.getTextWidths(text, start, end, widths);
    } else {
      int wid = replacement.getSize(workPaint, text, start, end, fmi);

      if (end > start) {
        widths[0] = wid;
        for (int i = start + 1; i < end; i++) {
          widths[i - start] = 0;
        }
      }
    }
    return end - start;
  }
  @Override
  public void setObject(Item item) {
    try {
      dobroitem = (DobroPostItem) item;
    } catch (ClassCastException e) {
      e.printStackTrace();
      return;
    }
    if (dobroitem == null || dobroitem.post == null) return;
    dobroitem.post.setLastContext(getContext());
    this.board = dobroitem.post.getBoardName();
    this.thread = dobroitem.post.getThreadDisplay_id();
    ((HorizontalScrollView) findViewById(R.id.horizScroll)).scrollTo(0, 0);

    CharSequence txt = dobroitem.post.getFormattedText();
    if (txt.length() > 800) {
      Pattern p1 = Pattern.compile("\\.|\n");
      Matcher m1 = p1.matcher(txt);
      Pattern p2 = Pattern.compile("\\b");
      Matcher m2 = p2.matcher(txt);
      if (m1.find(500) && m1.end() < 700) txt = txt.subSequence(0, m1.end());
      else if (m2.find(500) && m2.start() < 700) txt = txt.subSequence(0, m2.start());
      else txt = txt.subSequence(0, 500);
      messageView.setText(txt, BufferType.SPANNABLE);
      SpannableStringBuilder builder =
          new SpannableStringBuilder("\nСообщение слишком длинное. Полная версия");
      builder.setSpan(
          new ClickableSpan() {
            @Override
            public void onClick(View widget) {
              try {
                messageView.setText(dobroitem.post.getFormattedText(), BufferType.SPANNABLE);
              } catch (IndexOutOfBoundsException e) {

              }
            }
          },
          1,
          builder.length(),
          0);
      messageView.append(builder.subSequence(0, builder.length()));
    } else messageView.setText(txt, BufferType.SPANNABLE);
    // FIXME
    messageView.setMovementMethod(LinkMovementMethod.getInstance());
    messageView.setClickable(false);
    messageView.setFocusable(false);
    messageView.setFocusableInTouchMode(false);
    /*
    refsView.setMovementMethod(LinkMovementMethod.getInstance());
    refsView.setClickable(false);
    refsView.setFocusable(false);
    refsView.setFocusableInTouchMode(false);
    */
    //
    imagesLayout.removeAllViewsInLayout();
    if (dobroitem.post.isOp()) {
      numberView.setVisibility(GONE);
      titleView.setText(
          dobroitem.post.getSubject().length() == 0
              ? getContext().getString(R.string.untitled)
              : dobroitem.post.getSubject());
      titleView.setVisibility(VISIBLE);
    } else {
      int pos = dobroitem.post.getNumber();
      if (pos > 0) {
        numberView.setVisibility(VISIBLE);
        numberView.setText(String.valueOf(pos));
      } else numberView.setVisibility(GONE);
      titleView.setText("");
      titleView.setVisibility(GONE);
    }

    TypedValue backgroundRef = new TypedValue();
    getContext().getTheme().resolveAttribute(R.attr.dcPicrelatedColor, backgroundRef, true);
    int backgroundColor = getContext().getResources().getColor(backgroundRef.resourceId);
    SharedPreferences prefs = DobroApplication.getApplicationStatic().getDefaultPrefs();
    boolean show_info = prefs.getBoolean("show_fileinfo", true);
    boolean force_op_load = prefs.getBoolean("op_pictures_force", false);

    if (dobroitem.post.getFiles().length > 0) {
      for (DobroFile file : dobroitem.post.getFiles()) {
        CachedAsyncImageView imageView = new CachedAsyncImageView(getContext());
        VerticalTextView textView = null;
        String fname1 = file.getThumb().substring(file.getThumb().lastIndexOf("/") + 1);
        int e = fname1.lastIndexOf("s.");
        if (e > 0)
          fname1 = fname1.substring(0, e) + file.getSrc().substring(file.getSrc().lastIndexOf("."));
        String fname2 = file.getSrc().substring(file.getSrc().lastIndexOf("/") + 1);
        String size = humanReadableByteCount(file.getSize(), true);
        String date = getThumbnailDate(file.getThumb());
        if (date == null)
          try {
            date = file.getSrc().split("/")[2];
            date = date.substring(2) + "." + date.substring(0, 2);
          } catch (Exception ex) {
            date = "?";
          }
        imageView.setInfo(
            getContext()
                .getString(
                    R.string.file_info_parretn,
                    fname1,
                    fname2,
                    size,
                    date,
                    file.getRating(),
                    getMetadataText(file.getMetadata())));
        if (file.getMetadata().width != null)
          imageView.setSize(file.getMetadata().height, file.getMetadata().width);
        if (show_info) {
          textView = new VerticalTextView(getContext(), null);
          textView.setLinkedImageView(imageView);
          String text = "";
          if (dobroitem.post.getBoardName().equals("b")
              || dobroitem.post.getBoardName().equals("rf")) text += fname1;
          else text += fname2;
          text += "\n" + size;
          if (file.getMetadata() != null
              && file.getMetadata().width != null
              && file.getMetadata().height != null) {
            text +=
                ", "
                    + String.valueOf(file.getMetadata().width)
                    + "x"
                    + String.valueOf(file.getMetadata().height);
          }
          textView.setText(text);
          textView.setGravity(Gravity.BOTTOM);
          textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 12);
        }
        imageView.setRating(file.getRat());
        imageView.setForceLoad(force_op_load && dobroitem.post.isOp());
        imageView.setUrl(file.getThumb());
        final String urlTag = file.getSrc();
        imageView.setTag(urlTag);
        imageView.setPadding(2, 2, 2, 2);
        imageView.setAdjustViewBounds(true);
        imageView.setMaxHeight(mImageHeight);
        imageView.setMaxWidth(mImageHeight);
        imageView.setBackgroundColor(backgroundColor);
        imageView.setScaleType(ScaleType.CENTER);
        imageView.setOnCreateContextMenuListener(imageView);
        float h = mImageHeight;
        int w =
            Math.min(
                Math.round((h / (float) file.getThumb_height()) * file.getThumb_width()),
                file.getThumb_width());
        imageView.setLayoutParams(new LayoutParams(w + 4, mImageHeight + 4));
        if (show_info) {
          textView.setWidth(mImageHeight + 4);
          textView.setLinkedImageView(imageView);
          imagesLayout.addView(textView);
        }
        imagesLayout.addView(imageView);
      }
      imagesLayout.setVisibility(VISIBLE);
    } else {
      imagesLayout.setVisibility(GONE);
    }
    if (prefs.getBoolean("youtube", true)) {
      Spanned s = null;
      boolean cast_ok = false;
      try {
        s = (Spanned) dobroitem.post.getFormattedText();
        cast_ok = true;
      } catch (ClassCastException e) {
        cast_ok = false;
      }
      if (cast_ok) {
        URLSpan[] spans = s.getSpans(0, s.length(), URLSpan.class);
        for (URLSpan span : spans) {
          String url = span.getURL();
          Pattern p = Pattern.compile("(youtube\\.com/watch\\?v=|youtu\\.be/)(\\S{11})");
          Matcher m = p.matcher(url);
          if (!m.find()) continue;
          String yt_id = m.group(2);
          String text = s.subSequence(s.getSpanStart(span), s.getSpanEnd(span)).toString();
          CachedAsyncImageView imageView = new CachedAsyncImageView(getContext());
          imageView.setLayoutParams(
              new LayoutParams((int) ((mImageHeight + 4) / 0.75), mImageHeight + 4));

          imageView.setRating(Rating.SWF);
          imageView.setForceLoad(false);
          imageView.setTag(url);
          imageView.setPadding(2, 2, 2, 2);
          imageView.setAdjustViewBounds(true);
          imageView.setMaxHeight(mImageHeight);
          imageView.setMaxWidth((int) ((mImageHeight + 4) / 0.75));

          imageView.setUrl("http://img.youtube.com/vi/" + yt_id + "/0.jpg");

          imageView.setBackgroundColor(backgroundColor);
          imageView.setScaleType(ScaleType.CENTER);

          VerticalTextView textView = new VerticalTextView(getContext(), null);
          textView.setGravity(Gravity.BOTTOM);
          textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 12);
          textView.setText(text);
          textView.setLinkedImageView(imageView);
          textView.setWidth(mImageHeight + 4);

          if (show_info) imagesLayout.addView(textView);
          imagesLayout.addView(imageView);
          imagesLayout.setVisibility(VISIBLE);
        }
      }
    }
    metadataRightView.setText(
        getContext()
            .getString(
                R.string.post_date, String.valueOf(dobroitem.post.getDate().replace(' ', '\n'))));
    metadataLeftView.setText(
        dobroitem.post.getName()
            + "\n"
            + getContext()
                .getString(R.string.post_id, String.valueOf(dobroitem.post.getDisplay_id())));
    List<String> refs = dobroitem.post.getRefs();
    if (!refs.isEmpty()) {
      /*
      CharSequence seq = getContext().getText(R.string.answers);
      SpannableStringBuilder s = new SpannableStringBuilder(seq);
      s.setSpan(new StyleSpan(Typeface.BOLD_ITALIC), 0, seq.length(), 0);
      s.append(" ");
      for (String ref : refs) {
      	int start = s.length();
      	s.append(ref + ", ");
      	s.setSpan(
      			new DobroLinkSpan(dobroitem.post.getBoardName(), ref
      					.substring(2), getContext(), null), start, start
      					+ ref.length(), 0);
      }
      refsView.setText(s.subSequence(0, s.length() - 2),
      		BufferType.SPANNABLE);
       */
      refsButton.setText(String.format("/%d", refs.size()));
      refsButton.setVisibility(View.VISIBLE);
    } else refsButton.setVisibility(View.GONE);
    checkSpells();
  }
 private static int getNumForegroundColorSpansBetween(Spanned value, int start, int end) {
   return value.getSpans(start, end, ForegroundColorSpan.class).length;
 }
 /** Make sure TextView has exactly one span and that span has given type. */
 private static <TSPAN> TSPAN getSingleSpan(TextView textView, Class<TSPAN> spanClass) {
   Spanned text = (Spanned) textView.getText();
   TSPAN[] spans = text.getSpans(0, text.length(), spanClass);
   assertThat(spans).hasSize(1);
   return spans[0];
 }
  private void sendTextToGecko(CharSequence text, int caretPos) {
    if (DEBUG) Log.d(LOGTAG, "IME: sendTextToGecko(\"" + text + "\")");

    // Handle composition text styles
    if (text != null && text instanceof Spanned) {
      Spanned span = (Spanned) text;
      int spanStart = 0, spanEnd = 0;
      boolean pastSelStart = false, pastSelEnd = false;

      do {
        int rangeType = GeckoEvent.IME_RANGE_CONVERTEDTEXT;
        int rangeStyles = 0, rangeForeColor = 0, rangeBackColor = 0;

        // Find next offset where there is a style transition
        spanEnd = span.nextSpanTransition(spanStart + 1, text.length(), CharacterStyle.class);

        // Empty range, continue
        if (spanEnd <= spanStart) continue;

        // Get and iterate through list of span objects within range
        CharacterStyle[] styles = span.getSpans(spanStart, spanEnd, CharacterStyle.class);

        for (CharacterStyle style : styles) {
          if (style instanceof UnderlineSpan) {
            // Text should be underlined
            rangeStyles |= GeckoEvent.IME_RANGE_UNDERLINE;
          } else if (style instanceof ForegroundColorSpan) {
            // Text should be of a different foreground color
            rangeStyles |= GeckoEvent.IME_RANGE_FORECOLOR;
            rangeForeColor = ((ForegroundColorSpan) style).getForegroundColor();
          } else if (style instanceof BackgroundColorSpan) {
            // Text should be of a different background color
            rangeStyles |= GeckoEvent.IME_RANGE_BACKCOLOR;
            rangeBackColor = ((BackgroundColorSpan) style).getBackgroundColor();
          }
        }

        // Add range to array, the actual styles are
        //  applied when IME_SET_TEXT is sent
        if (DEBUG) {
          Log.d(
              LOGTAG,
              String.format(
                  ". . . sendTextToGecko: IME_ADD_RANGE, %d, %d, %d, %d, %d, %d",
                  spanStart,
                  spanEnd - spanStart,
                  rangeType,
                  rangeStyles,
                  rangeForeColor,
                  rangeBackColor));
        }

        GeckoAppShell.sendEventToGecko(
            GeckoEvent.createIMERangeEvent(
                spanStart,
                spanEnd - spanStart,
                rangeType,
                rangeStyles,
                rangeForeColor,
                rangeBackColor));

        spanStart = spanEnd;
      } while (spanStart < text.length());
    } else {
      if (DEBUG)
        Log.d(
            LOGTAG,
            ". . . sendTextToGecko: IME_ADD_RANGE, 0, "
                + text.length()
                + ", IME_RANGE_RAWINPUT, IME_RANGE_UNDERLINE)");
      GeckoAppShell.sendEventToGecko(
          GeckoEvent.createIMERangeEvent(
              0,
              text == null ? 0 : text.length(),
              GeckoEvent.IME_RANGE_RAWINPUT,
              GeckoEvent.IME_RANGE_UNDERLINE,
              0,
              0));
    }

    // Change composition (treating selection end as where the caret is)
    if (DEBUG) {
      Log.d(
          LOGTAG,
          ". . . sendTextToGecko: IME_SET_TEXT, IME_RANGE_CARETPOSITION, \"" + text + "\")");
    }

    GeckoAppShell.sendEventToGecko(
        GeckoEvent.createIMERangeEvent(
            caretPos, 0, GeckoEvent.IME_RANGE_CARETPOSITION, 0, 0, 0, text.toString()));
  }
Beispiel #21
0
 private static Object getLast(final Spanned text, final Class<?> kind) {
   Object[] spans = text.getSpans(0, text.length(), kind);
   return spans.length > 0 ? spans[spans.length - 1] : null;
 }
  /**
   * Draws and/or measures a uniform run of text on a single line. No span of interest should start
   * or end in the middle of this run (if not drawing, character spans that don't affect metrics can
   * be ignored). Neither should the run direction change in the middle of the run.
   *
   * <p>
   *
   * <p>The x position is the leading edge of the text. In a right-to-left paragraph, this will be
   * to the right of the text to be drawn. Paint should not have an Align value other than LEFT or
   * positioning will isCancelled confused.
   *
   * <p>
   *
   * <p>On return, workPaint will reflect the original paint plus any modifications made by
   * character styles on the run.
   *
   * <p>
   *
   * <p>The returned width is signed and will be < 0 if the paragraph direction is right-to-left.
   */
  private static float drawUniformRun(
      Canvas canvas,
      Spanned text,
      int start,
      int end,
      int dir,
      boolean runIsRtl,
      float x,
      int top,
      int y,
      int bottom,
      Paint.FontMetricsInt fmi,
      TextPaint paint,
      TextPaint workPaint,
      boolean needWidth) {

    boolean haveWidth = false;
    float ret = 0;
    CharacterStyle[] spans = text.getSpans(start, end, CharacterStyle.class);

    ReplacementSpan replacement = null;

    // XXX: This shouldn't be modifying paint, only workPaint.
    // However, the members belonging to TextPaint should have default
    // values anyway.  Better to ensure this in the Layout constructor.
    paint.bgColor = 0;
    paint.baselineShift = 0;
    workPaint.set(paint);

    if (spans.length > 0) {
      for (CharacterStyle span : spans) {
        if (span instanceof ReplacementSpan) {
          replacement = (ReplacementSpan) span;
        } else {
          span.updateDrawState(workPaint);
        }
      }
    }

    if (replacement == null) {
      CharSequence tmp;
      int tmpstart, tmpend;

      if (runIsRtl) {
        tmp = TextUtils.getReverse(text, start, end);
        tmpstart = 0;
        // XXX: assumes getReverse doesn't change the length of the text
        tmpend = end - start;
      } else {
        tmp = text;
        tmpstart = start;
        tmpend = end;
      }

      if (fmi != null) {
        workPaint.getFontMetricsInt(fmi);
      }

      if (canvas != null) {
        if (workPaint.bgColor != 0) {
          int c = workPaint.getColor();
          Paint.Style s = workPaint.getStyle();
          workPaint.setColor(workPaint.bgColor);
          workPaint.setStyle(Paint.Style.FILL);

          if (!haveWidth) {
            ret = workPaint.measureText(tmp, tmpstart, tmpend);
            haveWidth = true;
          }

          if (dir == Layout.DIR_RIGHT_TO_LEFT) {
            canvas.drawRect(x - ret, top, x, bottom, workPaint);
          } else {
            canvas.drawRect(x, top, x + ret, bottom, workPaint);
          }

          workPaint.setStyle(s);
          workPaint.setColor(c);
        }

        if (dir == Layout.DIR_RIGHT_TO_LEFT) {
          if (!haveWidth) {
            ret = workPaint.measureText(tmp, tmpstart, tmpend);
            haveWidth = true;
          }

          canvas.drawText(tmp, tmpstart, tmpend, x - ret, y + workPaint.baselineShift, workPaint);
        } else {
          if (needWidth) {
            if (!haveWidth) {
              ret = workPaint.measureText(tmp, tmpstart, tmpend);
              haveWidth = true;
            }
          }

          canvas.drawText(tmp, tmpstart, tmpend, x, y + workPaint.baselineShift, workPaint);
        }
      } else {
        if (needWidth && !haveWidth) {
          ret = workPaint.measureText(tmp, tmpstart, tmpend);
          haveWidth = true;
        }
      }
    } else {
      ret = replacement.getSize(workPaint, text, start, end, fmi);

      if (canvas != null) {
        if (dir == Layout.DIR_RIGHT_TO_LEFT) {
          replacement.draw(canvas, text, start, end, x - ret, top, y, bottom, workPaint);
        } else {
          replacement.draw(canvas, text, start, end, x, top, y, bottom, workPaint);
        }
      }
    }

    if (dir == Layout.DIR_RIGHT_TO_LEFT) {
      return -ret;
    } else {
      return ret;
    }
  }