Java tutorial
/* * Copyright (c) 2016. Matsuda, Akihit (akihito104) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.freshdigitable.udonroad; import android.support.annotation.Nullable; import android.text.SpannableStringBuilder; import android.text.Spanned; import android.text.TextUtils; import android.text.style.ClickableSpan; import android.text.style.URLSpan; import android.view.View; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import twitter4j.ExtendedMediaEntity; import twitter4j.Status; import twitter4j.URLEntity; import twitter4j.UserMentionEntity; /** * SpannableStringUtil creates clickable text. * * Created by akihit on 2016/10/08. */ class SpannableStringUtil { static CharSequence create(Status bindingStatus) { final List<SpanningInfo> spannableInfo = createSpanningInfo(bindingStatus); return createClickableSpan(bindingStatus.getText(), spannableInfo); } static CharSequence create(final String text, URLEntity[] urlEntities) { if (TextUtils.isEmpty(text)) { return ""; } final List<SpanningInfo> urlSpanningInfo = createURLSpanningInfo(text, urlEntities, null); return createClickableSpan(text, urlSpanningInfo); } private static List<SpanningInfo> createSpanningInfo(Status bindingStatus) { List<SpanningInfo> info = new ArrayList<>(); info.addAll(createURLSpanningInfo(bindingStatus)); info.addAll(createUserSpanningInfo(bindingStatus)); for (int i = info.size() - 1; i >= 0; i--) { final SpanningInfo spanningInfo = info.get(i); for (int j = 0; j < i; j++) { if (spanningInfo.start == info.get(j).start) { info.remove(spanningInfo); break; } } } return info; } private static List<SpanningInfo> createURLSpanningInfo(Status bindingStatus) { final String text = bindingStatus.getText(); final String quotedStatusIdStr = bindingStatus.getQuotedStatus() != null ? Long.toString(bindingStatus.getQuotedStatusId()) : ""; final List<SpanningInfo> info = new ArrayList<>(); final URLEntity[] urlEntities = bindingStatus.getURLEntities(); info.addAll(createURLSpanningInfo(text, urlEntities, quotedStatusIdStr)); final ExtendedMediaEntity[] eme = bindingStatus.getExtendedMediaEntities(); for (ExtendedMediaEntity e : eme) { int start = text.indexOf(e.getURL()); int end = start + e.getURL().length(); if (isInvalidRange(text, start, end)) { continue; } info.add(new SpanningInfo(null, start, end, "")); } return info; } private static List<SpanningInfo> createURLSpanningInfo(final String text, URLEntity[] urlEntities, @Nullable String quotedStatusIdStr) { List<SpanningInfo> info = new ArrayList<>(urlEntities.length); for (URLEntity u : urlEntities) { int start = text.indexOf(u.getURL()); int end = start + u.getURL().length(); if (isInvalidRange(text, start, end)) { if (TextUtils.isEmpty(u.getExpandedURL())) { continue; } start = text.indexOf(u.getExpandedURL()); end = start + u.getExpandedURL().length(); if (isInvalidRange(text, start, end)) { continue; } } if (!TextUtils.isEmpty(quotedStatusIdStr) && u.getExpandedURL().contains(quotedStatusIdStr)) { info.add(new SpanningInfo(null, start, end, "")); } info.add(new SpanningInfo(new URLSpan(u.getExpandedURL()), start, end, u.getDisplayURL())); } return info; } private static List<SpanningInfo> createUserSpanningInfo(Status bindingStatus) { final String text = bindingStatus.getText(); final UserMentionEntity[] userMentionEntities = bindingStatus.getUserMentionEntities(); final List<SpanningInfo> info = new ArrayList<>(); for (UserMentionEntity u : userMentionEntities) { final int start = text.indexOf("@" + u.getScreenName()); final int end = start + u.getScreenName().length() + 1; if (isInvalidRange(text, start, end)) { continue; } final long id = u.getId(); info.add(new SpanningInfo(new ClickableSpan() { @Override public void onClick(View view) { UserInfoActivity.start(view.getContext(), id); } }, start, end, null)); } return info; } private static boolean isInvalidRange(String text, int start, int end) { return start < 0 || end > text.length() || start > text.length(); } private static CharSequence createClickableSpan(String text, List<SpanningInfo> info) { // to manipulate changing tweet text length, sort descending SpannableStringBuilder ssb = new SpannableStringBuilder(text); Collections.sort(info, new Comparator<SpanningInfo>() { @Override public int compare(SpanningInfo l, SpanningInfo r) { return r.start - l.start; } }); for (SpanningInfo si : info) { if (si.isSpanning()) { ssb.setSpan(si.span, si.start, si.end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } if (si.isReplacing()) { ssb.replace(si.start, si.end, si.displayingText); } } return ssb; } /** * SpanningInfo is information to create SpannableStringBuilder. * * Created by akihit on 2016/08/20. */ private static class SpanningInfo { final ClickableSpan span; final int start; final int end; final String displayingText; SpanningInfo(@Nullable ClickableSpan span, int start, int end, @Nullable String displayingText) { this.span = span; this.start = start; this.end = end; this.displayingText = displayingText; } boolean isSpanning() { return span != null; } boolean isReplacing() { return displayingText != null; } } }