public static String annotateSpans(final CharSequence cs, @Nullable final Func1<Object, Pair<String, String>> annotator) {
        if (!(cs instanceof Spanned) || cs.length() == 0) {
            return cs.toString();
        }
        final Object[] spans = ((Spanned) cs).getSpans(0, cs.length(), Object.class);
        if (spans.length == 0) {
            return cs.toString();
        }

        final SortedMap<Integer, String> data = new TreeMap<>((i1, i2) -> -i1.compareTo(i2));
        for (Object span : spans) {
            final Pair<String, String> markers = annotator == null ? null : annotator.call(span);
            final int start = ((Spanned) cs).getSpanStart(span);
            data.put(start, (data.containsKey(start) ? data.get(start) : "") + (markers == null || markers.first == null ? "" : markers.first));
            final int end = ((Spanned) cs).getSpanEnd(span);
            data.put(end, (markers == null || markers.second == null ? "" : markers.second) + (data.containsKey(end) ? data.get(end) : ""));
        }

        String result = cs.toString();
        for (Map.Entry<Integer, String> entry : data.entrySet()) {
            result = result.substring(0, entry.getKey()) + entry.getValue() + result.substring(entry.getKey());
        }
        return result;
    }
Convenience method to apply a span to a CharSequence. See {@link Spannable#setSpan(Object, int, int, int)} for details.

@param cs       CharSequence to apply span to
@param span     the span to use. Usually a class implementing {@link android.text.style.CharacterStyle}, e.g. {@link ForegroundColorSpan}
@param start    start index to add span. if <0 then 0 is used
@param end      end index to add span. if <0 then cs.length() is used. if end<start, then no span is applied
@param priority span priority. Values > 255 will be set to 255
@return CharSequence with span applied
main/src/main/java/cgeo/geocaching/utils/TextUtils.java