1 /* 2 * Copyright (C) 2013 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.inputmethod.latin.utils; 18 19 import android.text.Spannable; 20 import android.text.SpannableString; 21 import android.text.Spanned; 22 import android.text.SpannedString; 23 import android.text.TextUtils; 24 import android.text.style.SuggestionSpan; 25 26 public final class SpannableStringUtils { 27 /** 28 * Copies the spans from the region <code>start...end</code> in 29 * <code>source</code> to the region 30 * <code>destoff...destoff+end-start</code> in <code>dest</code>. 31 * Spans in <code>source</code> that begin before <code>start</code> 32 * or end after <code>end</code> but overlap this range are trimmed 33 * as if they began at <code>start</code> or ended at <code>end</code>. 34 * Only SuggestionSpans that don't have the SPAN_PARAGRAPH span are copied. 35 * 36 * This code is almost entirely taken from {@link TextUtils#copySpansFrom}, except for the 37 * kind of span that is copied. 38 * 39 * @throws IndexOutOfBoundsException if any of the copied spans 40 * are out of range in <code>dest</code>. 41 */ 42 public static void copyNonParagraphSuggestionSpansFrom(Spanned source, int start, int end, 43 Spannable dest, int destoff) { 44 Object[] spans = source.getSpans(start, end, SuggestionSpan.class); 45 46 for (int i = 0; i < spans.length; i++) { 47 int fl = source.getSpanFlags(spans[i]); 48 if (0 != (fl & Spannable.SPAN_PARAGRAPH)) continue; 49 50 int st = source.getSpanStart(spans[i]); 51 int en = source.getSpanEnd(spans[i]); 52 53 if (st < start) 54 st = start; 55 if (en > end) 56 en = end; 57 58 dest.setSpan(spans[i], st - start + destoff, en - start + destoff, 59 fl); 60 } 61 } 62 63 /** 64 * Returns a CharSequence concatenating the specified CharSequences, retaining their 65 * SuggestionSpans that don't have the PARAGRAPH flag, but not other spans. 66 * 67 * This code is almost entirely taken from {@link TextUtils#concat(CharSequence...)}, except 68 * it calls copyNonParagraphSuggestionSpansFrom instead of {@link TextUtils#copySpansFrom}. 69 */ 70 public static CharSequence concatWithNonParagraphSuggestionSpansOnly(CharSequence... text) { 71 if (text.length == 0) { 72 return ""; 73 } 74 75 if (text.length == 1) { 76 return text[0]; 77 } 78 79 boolean spanned = false; 80 for (int i = 0; i < text.length; i++) { 81 if (text[i] instanceof Spanned) { 82 spanned = true; 83 break; 84 } 85 } 86 87 StringBuilder sb = new StringBuilder(); 88 for (int i = 0; i < text.length; i++) { 89 sb.append(text[i]); 90 } 91 92 if (!spanned) { 93 return sb.toString(); 94 } 95 96 SpannableString ss = new SpannableString(sb); 97 int off = 0; 98 for (int i = 0; i < text.length; i++) { 99 int len = text[i].length(); 100 101 if (text[i] instanceof Spanned) { 102 copyNonParagraphSuggestionSpansFrom((Spanned) text[i], 0, len, ss, off); 103 } 104 105 off += len; 106 } 107 108 return new SpannedString(ss); 109 } 110 } 111