List of usage examples for android.text SpannableStringBuilder subSequence
public CharSequence subSequence(int start, int end)
From source file:com.keylesspalace.tusky.util.LinkHelper.java
/** * Finds links, mentions, and hashtags in a piece of text and makes them clickable, associating * them with callbacks to notify when they're clicked. * * @param view the returned text will be put in * @param content containing text with mentions, links, or hashtags * @param mentions any '@' mentions which are known to be in the content * @param listener to notify about particular spans that are clicked *//* w w w . j a v a2 s . com*/ public static void setClickableText(TextView view, Spanned content, @Nullable Status.Mention[] mentions, final LinkListener listener) { SpannableStringBuilder builder = new SpannableStringBuilder(content); URLSpan[] urlSpans = content.getSpans(0, content.length(), URLSpan.class); for (URLSpan span : urlSpans) { int start = builder.getSpanStart(span); int end = builder.getSpanEnd(span); int flags = builder.getSpanFlags(span); CharSequence text = builder.subSequence(start, end); if (text.charAt(0) == '#') { final String tag = text.subSequence(1, text.length()).toString(); ClickableSpan newSpan = new ClickableSpan() { @Override public void onClick(View widget) { listener.onViewTag(tag); } @Override public void updateDrawState(TextPaint ds) { super.updateDrawState(ds); ds.setUnderlineText(false); } }; builder.removeSpan(span); builder.setSpan(newSpan, start, end, flags); } else if (text.charAt(0) == '@' && mentions != null && mentions.length > 0) { String accountUsername = text.subSequence(1, text.length()).toString(); /* There may be multiple matches for users on different instances with the same * username. If a match has the same domain we know it's for sure the same, but if * that can't be found then just go with whichever one matched last. */ String id = null; for (Status.Mention mention : mentions) { if (mention.localUsername.equalsIgnoreCase(accountUsername)) { id = mention.id; if (mention.url.contains(getDomain(span.getURL()))) { break; } } } if (id != null) { final String accountId = id; ClickableSpan newSpan = new ClickableSpan() { @Override public void onClick(View widget) { listener.onViewAccount(accountId); } @Override public void updateDrawState(TextPaint ds) { super.updateDrawState(ds); ds.setUnderlineText(false); } }; builder.removeSpan(span); builder.setSpan(newSpan, start, end, flags); } } else { ClickableSpan newSpan = new CustomURLSpan(span.getURL()); builder.removeSpan(span); builder.setSpan(newSpan, start, end, flags); } } view.setText(builder); view.setLinksClickable(true); view.setMovementMethod(LinkMovementMethod.getInstance()); }
From source file:com.xinheng.crash.CrashListAdapter.java
@Override public void onBindViewHolder(final ViewHolder holder, final int position) { String crash = mData[position]; holder.log = crash;/*from w ww . jav a 2s.c o m*/ if (crash != null) { setSpaceState(holder, /*show = */ crash.startsWith("at ")); if (crash.startsWith("Caused by")) { holder.title.setTypeface(null, Typeface.BOLD); holder.title.setTextColor(0xdeffffff); } else { holder.title.setTypeface(null, Typeface.NORMAL); holder.title.setTextColor(0xffef4545); } if (mPackageName != null && crash.contains(mPackageName) && crash.indexOf("(") > -1) { holder.itemView.setSelected(true); int indexOfC = crash.indexOf("("); String atPackage = crash.substring(0, indexOfC); SpannableStringBuilder builder = new SpannableStringBuilder(atPackage) .append(StringStyleUtils.format(holder.title.getContext(), " " + crash.substring(indexOfC), R.style.LineTextAppearance)); CharSequence title = builder.subSequence(0, builder.length()); holder.title.setText(title); } else { holder.itemView.setSelected(false); holder.title.setText(crash); } } }
From source file:me.drakeet.library.ui.CrashListAdapter.java
@Override public void onBindViewHolder(final ViewHolder holder, final int position) { String crash = mData[position]; holder.log = crash;/* w w w .j a v a 2s . com*/ if (crash != null) { setSpaceState(holder, /*show = */ crash.startsWith("at ")); if (crash.startsWith("Caused by")) { holder.title.setTypeface(null, Typeface.BOLD); holder.title.setTextColor(0xdeffffff); } else { holder.title.setTypeface(null, Typeface.NORMAL); holder.title.setTextColor(0xffef4545); } if (mPackageName != null && crash.contains(mPackageName)) { holder.itemView.setSelected(true); int indexOfC = crash.indexOf("("); if (indexOfC >= 0) { String atPackage = crash.substring(0, indexOfC); SpannableStringBuilder builder = new SpannableStringBuilder(atPackage) .append(StringStyleUtils.format(holder.title.getContext(), " " + crash.substring(indexOfC), R.style.LineTextAppearance)); CharSequence title = builder.subSequence(0, builder.length()); holder.title.setText(title); } else { holder.title.setText(crash); } } else { holder.itemView.setSelected(false); holder.title.setText(crash); } } }
From source file:com.juick.android.JuickMessagesAdapter.java
private static void setStyleSpans(SpannableStringBuilder ssb, int ssbOffset, Object what, String styleChar) { String txt;/*from ww w .j a va2 s . c om*/ txt = ssb.subSequence(ssbOffset, ssb.length()).toString(); txt = " " + txt + " "; ssbOffset -= 1; // int scan = 0; int cnt = 0; while (cnt++ < 20) { // don't need bugs in production. int ix = txt.indexOf(styleChar, scan); if (ix < 0) break; if (" \n".indexOf(txt.charAt(ix - 1)) != -1) { int ix2 = txt.indexOf(styleChar, ix + 1); if (ix2 < 0) break; if (" \n".indexOf(txt.charAt(ix2 + 1)) == -1 // not ends with space || txt.substring(ix, ix2).indexOf("\n") != -1) { // spans several lines scan = ix2; // not ok continue; } else { CharacterStyle span = null; CharacterStyle span2 = null; // found needed stuff if (what instanceof Integer && (((Integer) what) == Typeface.BOLD)) { span = new StyleSpan(Typeface.BOLD); if (helvNueFonts) { span2 = new CustomTypefaceSpan("", JuickAdvancedApplication.helvNueBold); } } if (what instanceof Integer && (((Integer) what) == Typeface.ITALIC)) { span = new StyleSpan(Typeface.ITALIC); } if (what == UnderlineSpan.class) { span = new UnderlineSpan(); } if (span != null && ix2 - ix > 1) { ssb.delete(ssbOffset + ix, ssbOffset + ix + 1); // delete styling char txt = stringDelete(txt, ix, ix + 1); ix2--; // moves, too ssb.delete(ssbOffset + ix2, ssbOffset + ix2 + 1); // second char deleted txt = stringDelete(txt, ix2, ix2 + 1); ssb.setSpan(span, ssbOffset + ix, ssbOffset + ix2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); if (span2 != null) { ssb.setSpan(span2, ssbOffset + ix, ssbOffset + ix2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } } scan = ix2; } } } }
From source file:com.juick.android.JuickMessagesAdapter.java
public static ParsedMessage formatMessageText(final Context ctx, final JuickMessage jmsg, boolean condensed) { if (jmsg.parsedText != null) { return (ParsedMessage) jmsg.parsedText; // was parsed before }/*from w ww. j av a2 s .c o m*/ getColorTheme(ctx); SpannableStringBuilder ssb = new SpannableStringBuilder(); int spanOffset = 0; final boolean isMainMessage = jmsg.getRID() == 0; final boolean doCompactComment = !isMainMessage && compactComments; if (jmsg.continuationInformation != null) { ssb.append(jmsg.continuationInformation + "\n"); ssb.setSpan(new StyleSpan(Typeface.ITALIC), spanOffset, ssb.length() - 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } // // NAME // spanOffset = ssb.length(); String name = '@' + jmsg.User.UName; int userNameStart = ssb.length(); ssb.append(name); int userNameEnd = ssb.length(); StyleSpan userNameBoldSpan; ForegroundColorSpan userNameColorSpan; ssb.setSpan(userNameBoldSpan = new StyleSpan(Typeface.BOLD), spanOffset, ssb.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); ssb.setSpan( userNameColorSpan = new ForegroundColorSpan( colorTheme.getColor(ColorsTheme.ColorKey.USERNAME, 0xFFC8934E)), spanOffset, ssb.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); if (helvNueFonts) { ssb.setSpan(new CustomTypefaceSpan("", JuickAdvancedApplication.helvNueBold), spanOffset, ssb.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } ssb.append(' '); spanOffset = ssb.length(); if (!condensed) { // // TAGS // String tags = jmsg.getTags(); if (feedlyFonts) tags = tags.toUpperCase(); ssb.append(tags + "\n"); if (tags.length() > 0) { ssb.setSpan(new ForegroundColorSpan(colorTheme.getColor(ColorsTheme.ColorKey.TAGS, 0xFF0000CC)), spanOffset, ssb.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); if (feedlyFonts) { ssb.setSpan(new CustomTypefaceSpan("", JuickAdvancedApplication.dinWebPro), spanOffset, ssb.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } else if (helvNueFonts) { ssb.setSpan(new CustomTypefaceSpan("", JuickAdvancedApplication.helvNue), spanOffset, ssb.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } } spanOffset = ssb.length(); } if (jmsg.translated) { // // 'translated' // ssb.append("translated: "); ssb.setSpan( new ForegroundColorSpan(colorTheme.getColor(ColorsTheme.ColorKey.TRANSLATED_LABEL, 0xFF4ec856)), spanOffset, ssb.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); ssb.setSpan(new StyleSpan(android.graphics.Typeface.BOLD), spanOffset, ssb.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); spanOffset = ssb.length(); } int bodyOffset = ssb.length(); int messageNumberStart = -1, messageNumberEnd = -1; if (showNumbers(ctx) && !condensed) { // // numbers // if (isMainMessage) { messageNumberStart = ssb.length(); ssb.append(jmsg.getDisplayMessageNo()); messageNumberEnd = ssb.length(); } else { ssb.append("/" + jmsg.getRID()); if (jmsg.getReplyTo() != 0) { ssb.append("->" + jmsg.getReplyTo()); } } ssb.append(" "); ssb.setSpan(new ForegroundColorSpan(colorTheme.getColor(ColorsTheme.ColorKey.MESSAGE_ID, 0xFFa0a5bd)), spanOffset, ssb.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); spanOffset = ssb.length(); } // // MESSAGE BODY // String txt = Censor.getCensoredText(jmsg.Text); if (!condensed) { if (jmsg.Photo != null) { txt = jmsg.Photo + "\n" + txt; } if (jmsg.Video != null) { txt = jmsg.Video + "\n" + txt; } } ssb.append(txt); boolean ssbChanged = false; // allocation optimization // Highlight links http://example.com/ ArrayList<ExtractURLFromMessage.FoundURL> foundURLs = ExtractURLFromMessage.extractUrls(txt, jmsg.getMID()); ArrayList<String> urls = new ArrayList<String>(); for (ExtractURLFromMessage.FoundURL foundURL : foundURLs) { setSSBSpan(ssb, new ForegroundColorSpan(colorTheme.getColor(ColorsTheme.ColorKey.URLS, 0xFF0000CC)), spanOffset + foundURL.start, spanOffset + foundURL.end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); urls.add(foundURL.url); if (foundURL.title != null) { ssb.replace(spanOffset + foundURL.title.start - 1, spanOffset + foundURL.title.start, " "); ssb.replace(spanOffset + foundURL.title.end, spanOffset + foundURL.title.end + 1, " "); ssb.replace(spanOffset + foundURL.start - 1, spanOffset + foundURL.start, "("); ssb.replace(spanOffset + foundURL.end, spanOffset + foundURL.end + 1, ")"); ssb.setSpan(new StyleSpan(Typeface.BOLD), spanOffset + foundURL.title.start, spanOffset + foundURL.title.end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } ssbChanged = true; } // bold italic underline if (jmsg.Text.indexOf("*") != -1) { setStyleSpans(ssb, spanOffset, Typeface.BOLD, "*"); ssbChanged = true; } if (jmsg.Text.indexOf("/") != 0) { setStyleSpans(ssb, spanOffset, Typeface.ITALIC, "/"); ssbChanged = true; } if (jmsg.Text.indexOf("_") != 0) { setStyleSpans(ssb, spanOffset, UnderlineSpan.class, "_"); ssbChanged = true; } if (ssbChanged) { txt = ssb.subSequence(spanOffset, ssb.length()).toString(); // ssb was modified in between } // Highlight nick String accountName = XMPPService.getMyAccountName(jmsg.getMID(), ctx); if (accountName != null) { int scan = spanOffset; String nickScanArea = (ssb + " ").toUpperCase(); while (true) { int myNick = nickScanArea.indexOf("@" + accountName.toUpperCase(), scan); if (myNick != -1) { if (!isNickPart(nickScanArea.charAt(myNick + accountName.length() + 1))) { setSSBSpan(ssb, new BackgroundColorSpan( colorTheme.getColor(ColorsTheme.ColorKey.USERNAME_ME, 0xFF938e00)), myNick - 1, myNick + accountName.length() + 2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } scan = myNick + 1; } else { break; } } } // Highlight messages #1234 int pos = 0; Matcher m = getCrossReferenceMsgPattern(jmsg.getMID()).matcher(txt); while (m.find(pos)) { ssb.setSpan(new ForegroundColorSpan(colorTheme.getColor(ColorsTheme.ColorKey.URLS, 0xFF0000CC)), spanOffset + m.start(), spanOffset + m.end(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); pos = m.end(); } SpannableStringBuilder compactDt = null; if (!condensed) { // found messages count (search results) if (jmsg.myFoundCount != 0 || jmsg.hisFoundCount != 0) { ssb.append("\n"); int where = ssb.length(); if (jmsg.myFoundCount != 0) { ssb.append(ctx.getString(R.string.MyReplies_) + jmsg.myFoundCount + " "); } if (jmsg.hisFoundCount != 0) { ssb.append(ctx.getString(R.string.UserReplies_) + jmsg.hisFoundCount); } ssb.setSpan( new ForegroundColorSpan( colorTheme.getColor(ColorsTheme.ColorKey.TRANSLATED_LABEL, 0xFF4ec856)), where, ssb.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } int rightPartOffset = spanOffset = ssb.length(); if (!doCompactComment) { // // TIME (bottom of message) // try { DateFormat df = new SimpleDateFormat("HH:mm dd/MMM/yy"); String date = jmsg.Timestamp != null ? df.format(jmsg.Timestamp) : "[bad date]"; if (date.endsWith("/76")) date = date.substring(0, date.length() - 3); // special case for no year in datasource ssb.append("\n" + date + " "); } catch (Exception e) { ssb.append("\n[fmt err] "); } ssb.setSpan(new ForegroundColorSpan(colorTheme.getColor(ColorsTheme.ColorKey.DATE, 0xFFAAAAAA)), spanOffset, ssb.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } else { compactDt = new SpannableStringBuilder(); try { if (true || jmsg.deltaTime != Long.MIN_VALUE) { compactDt.append(com.juickadvanced.Utils.toRelaviteDate(jmsg.Timestamp.getTime(), russian)); } else { DateFormat df = new SimpleDateFormat("HH:mm dd/MMM/yy"); String date = jmsg.Timestamp != null ? df.format(jmsg.Timestamp) : "[bad date]"; if (date.endsWith("/76")) date = date.substring(0, date.length() - 3); // special case for no year in datasource compactDt.append(date); } } catch (Exception e) { compactDt.append("\n[fmt err] "); } compactDt.setSpan( new ForegroundColorSpan(colorTheme.getColor(ColorsTheme.ColorKey.DATE, 0xFFAAAAAA)), 0, compactDt.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } spanOffset = ssb.length(); // // Number of REPLIES // if (jmsg.replies > 0) { String replies = Replies + jmsg.replies; ssb.append(" " + replies); ssb.setSpan( new ForegroundColorSpan( colorTheme.getColor(ColorsTheme.ColorKey.NUMBER_OF_COMMENTS, 0xFFC8934E)), spanOffset, ssb.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); ssb.setSpan(new WrapTogetherSpan() { }, spanOffset, ssb.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } if (!doCompactComment) { // right align ssb.setSpan(new AlignmentSpan.Standard(Alignment.ALIGN_OPPOSITE), rightPartOffset, ssb.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } } if (helvNueFonts) { ssb.setSpan(new CustomTypefaceSpan("", JuickAdvancedApplication.helvNue), bodyOffset, ssb.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } LeadingMarginSpan.LeadingMarginSpan2 userpicSpan = null; if (showUserpics(ctx) && !condensed) { userpicSpan = new LeadingMarginSpan.LeadingMarginSpan2() { @Override public int getLeadingMarginLineCount() { if (_wrapUserpics) { return 2; } else { return 22222; } } @Override public int getLeadingMargin(boolean first) { if (first) { return (int) (2 * getLineHeight(ctx, (double) textScale)); } else { return 0; } } @Override public void drawLeadingMargin(Canvas c, Paint p, int x, int dir, int top, int baseline, int bottom, CharSequence text, int start, int end, boolean first, Layout layout) { } }; ssb.setSpan(userpicSpan, 0, ssb.length(), 0); } ParsedMessage parsedMessage = new ParsedMessage(ssb, urls); parsedMessage.userpicSpan = userpicSpan; parsedMessage.userNameBoldSpan = userNameBoldSpan; parsedMessage.userNameColorSpan = userNameColorSpan; parsedMessage.userNameStart = userNameStart; parsedMessage.userNameEnd = userNameEnd; parsedMessage.messageNumberStart = messageNumberStart; parsedMessage.messageNumberEnd = messageNumberEnd; parsedMessage.compactDate = compactDt; return parsedMessage; }