package org.wikipedia.richtext; import android.support.annotation.IntRange; import android.support.annotation.NonNull; import android.text.Spannable; import android.text.SpannableString; import android.text.Spanned; import android.text.SpannedString; import android.text.TextUtils; import android.text.style.URLSpan; import android.widget.TextView; import org.wikipedia.util.StringUtil; import java.util.ArrayList; import java.util.Collection; import java.util.List; public final class RichTextUtil { @NonNull public static Spannable setSpans(@NonNull Spannable spannable, int start, int end, int flags, @NonNull Object... spans) { for (Object span : spans) { spannable.setSpan(span, start, end, flags); } return spannable; } /** * Apply only the spans from src to dst specific by spans. * * @see {@link android.text.TextUtils#copySpansFrom} */ public static void copySpans(@NonNull Spanned src, @NonNull Spannable dst, @NonNull Collection<Object> spans) { for (Object span : spans) { int start = src.getSpanStart(span); int end = src.getSpanEnd(span); int flags = src.getSpanFlags(span); dst.setSpan(span, start, end, flags); } } /** Strips all rich text except spans used to provide compositional hints. */ public static CharSequence stripRichText(CharSequence str, int start, int end) { String plainText = str.toString(); SpannableString ret = new SpannableString(plainText); if (str instanceof Spanned) { List<Object> keyboardHintSpans = getComposingSpans((Spanned) str, start, end); copySpans((Spanned) str, ret, keyboardHintSpans); } return ret; } /** * @return Temporary spans, often applied by the keyboard to provide hints such as typos. * * @see {@link android.view.inputmethod.BaseInputConnection#removeComposingSpans} * @see {@link android.inputmethod.latin.inputlogic.InputLogic#setComposingTextInternalWithBackgroundColor} */ @NonNull public static List<Object> getComposingSpans(@NonNull Spanned spanned, int start, int end) { // TODO: replace with Apache CollectionUtils.filter(). List<Object> ret = new ArrayList<>(); for (Object span : getSpans(spanned, start, end)) { if (isComposingSpan(spanned, span)) { ret.add(span); } } return ret; } public static Object[] getSpans(@NonNull Spanned spanned, int start, int end) { Class<Object> anyType = Object.class; return spanned.getSpans(start, end, anyType); } public static boolean isComposingSpan(@NonNull Spanned spanned, Object span) { return isFlaggedSpan(spanned, span, Spanned.SPAN_COMPOSING); } public static boolean isFlaggedSpan(@NonNull Spanned spanned, Object span, int flags) { return (spanned.getSpanFlags(span) & flags) == flags; } public static void removeUnderlinesFromLinks(@NonNull TextView textView) { CharSequence text = textView.getText(); if (text instanceof Spanned) { Spannable spannable = new SpannableString(text); removeUnderlinesFromLinks(spannable, spannable.getSpans(0, spannable.length(), URLSpan.class)); textView.setText(spannable); } } public static void removeUnderlinesFromLinks(@NonNull Spannable spannable, @NonNull URLSpan[] spans) { for (URLSpan span: spans) { int start = spannable.getSpanStart(span); int end = spannable.getSpanEnd(span); spannable.removeSpan(span); span = new URLSpanNoUnderline(span.getURL()); spannable.setSpan(span, start, end, 0); } } public static void removeUnderlinesFromLinksAndMakeBold(@NonNull TextView textView) { CharSequence text = textView.getText(); if (text instanceof Spanned) { Spannable spannable = new SpannableString(text); removeUnderlinesFromLinksAndMakeBold(spannable, spannable.getSpans(0, spannable.length(), URLSpan.class)); textView.setText(spannable); } } public static void removeUnderlinesFromLinksAndMakeBold(@NonNull Spannable spannable, @NonNull URLSpan[] spans) { for (URLSpan span: spans) { int start = spannable.getSpanStart(span); int end = spannable.getSpanEnd(span); spannable.removeSpan(span); span = new URLSpanBoldNoUnderline(span.getURL()); spannable.setSpan(span, start, end, 0); } } public static String stripHtml(@NonNull String html) { return StringUtil.fromHtml(html).toString(); } public static CharSequence remove(@NonNull CharSequence text, @IntRange(from = 1) int start, int end) { return new SpannedString(TextUtils.concat(text.subSequence(0, start - 1), text.subSequence(end, text.length()))); } private RichTextUtil() { } }