Merge "Introduce new constructor for not copying NoCopySpan" into pi-dev
am: 54ff524a2b
Change-Id: I2e11e189c240df026c478bb9d5a4cd97c5e30e9e
This commit is contained in:
@@ -16,7 +16,6 @@
|
||||
|
||||
package android.text;
|
||||
|
||||
|
||||
/**
|
||||
* This is the class for text whose content is immutable but to which
|
||||
* markup objects can be attached and detached.
|
||||
@@ -26,12 +25,27 @@ public class SpannableString
|
||||
extends SpannableStringInternal
|
||||
implements CharSequence, GetChars, Spannable
|
||||
{
|
||||
/**
|
||||
* @param source source object to copy from
|
||||
* @param ignoreNoCopySpan whether to copy NoCopySpans in the {@code source}
|
||||
* @hide
|
||||
*/
|
||||
public SpannableString(CharSequence source, boolean ignoreNoCopySpan) {
|
||||
super(source, 0, source.length(), ignoreNoCopySpan);
|
||||
}
|
||||
|
||||
/**
|
||||
* For the backward compatibility reasons, this constructor copies all spans including {@link
|
||||
* android.text.NoCopySpan}.
|
||||
* @param source source text
|
||||
*/
|
||||
public SpannableString(CharSequence source) {
|
||||
super(source, 0, source.length());
|
||||
this(source, false /* ignoreNoCopySpan */); // preserve existing NoCopySpan behavior
|
||||
}
|
||||
|
||||
private SpannableString(CharSequence source, int start, int end) {
|
||||
super(source, start, end);
|
||||
// preserve existing NoCopySpan behavior
|
||||
super(source, start, end, false /* ignoreNoCopySpan */);
|
||||
}
|
||||
|
||||
public static SpannableString valueOf(CharSequence source) {
|
||||
|
||||
@@ -26,7 +26,7 @@ import java.lang.reflect.Array;
|
||||
/* package */ abstract class SpannableStringInternal
|
||||
{
|
||||
/* package */ SpannableStringInternal(CharSequence source,
|
||||
int start, int end) {
|
||||
int start, int end, boolean ignoreNoCopySpan) {
|
||||
if (start == 0 && end == source.length())
|
||||
mText = source.toString();
|
||||
else
|
||||
@@ -38,24 +38,37 @@ import java.lang.reflect.Array;
|
||||
|
||||
if (source instanceof Spanned) {
|
||||
if (source instanceof SpannableStringInternal) {
|
||||
copySpans((SpannableStringInternal) source, start, end);
|
||||
copySpans((SpannableStringInternal) source, start, end, ignoreNoCopySpan);
|
||||
} else {
|
||||
copySpans((Spanned) source, start, end);
|
||||
copySpans((Spanned) source, start, end, ignoreNoCopySpan);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This unused method is left since this is listed in hidden api list.
|
||||
*
|
||||
* Due to backward compatibility reasons, we copy even NoCopySpan by default
|
||||
*/
|
||||
/* package */ SpannableStringInternal(CharSequence source, int start, int end) {
|
||||
this(source, start, end, false /* ignoreNoCopySpan */);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies another {@link Spanned} object's spans between [start, end] into this object.
|
||||
*
|
||||
* @param src Source object to copy from.
|
||||
* @param start Start index in the source object.
|
||||
* @param end End index in the source object.
|
||||
* @param ignoreNoCopySpan whether to copy NoCopySpans in the {@code source}
|
||||
*/
|
||||
private final void copySpans(Spanned src, int start, int end) {
|
||||
private void copySpans(Spanned src, int start, int end, boolean ignoreNoCopySpan) {
|
||||
Object[] spans = src.getSpans(start, end, Object.class);
|
||||
|
||||
for (int i = 0; i < spans.length; i++) {
|
||||
if (ignoreNoCopySpan && spans[i] instanceof NoCopySpan) {
|
||||
continue;
|
||||
}
|
||||
int st = src.getSpanStart(spans[i]);
|
||||
int en = src.getSpanEnd(spans[i]);
|
||||
int fl = src.getSpanFlags(spans[i]);
|
||||
@@ -76,35 +89,48 @@ import java.lang.reflect.Array;
|
||||
* @param src Source object to copy from.
|
||||
* @param start Start index in the source object.
|
||||
* @param end End index in the source object.
|
||||
* @param ignoreNoCopySpan copy NoCopySpan for backward compatible reasons.
|
||||
*/
|
||||
private final void copySpans(SpannableStringInternal src, int start, int end) {
|
||||
if (start == 0 && end == src.length()) {
|
||||
private void copySpans(SpannableStringInternal src, int start, int end,
|
||||
boolean ignoreNoCopySpan) {
|
||||
int count = 0;
|
||||
final int[] srcData = src.mSpanData;
|
||||
final Object[] srcSpans = src.mSpans;
|
||||
final int limit = src.mSpanCount;
|
||||
boolean hasNoCopySpan = false;
|
||||
|
||||
for (int i = 0; i < limit; i++) {
|
||||
int spanStart = srcData[i * COLUMNS + START];
|
||||
int spanEnd = srcData[i * COLUMNS + END];
|
||||
if (isOutOfCopyRange(start, end, spanStart, spanEnd)) continue;
|
||||
if (srcSpans[i] instanceof NoCopySpan) {
|
||||
hasNoCopySpan = true;
|
||||
if (ignoreNoCopySpan) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
count++;
|
||||
}
|
||||
|
||||
if (count == 0) return;
|
||||
|
||||
if (!hasNoCopySpan && start == 0 && end == src.length()) {
|
||||
mSpans = ArrayUtils.newUnpaddedObjectArray(src.mSpans.length);
|
||||
mSpanData = new int[src.mSpanData.length];
|
||||
mSpanCount = src.mSpanCount;
|
||||
System.arraycopy(src.mSpans, 0, mSpans, 0, src.mSpans.length);
|
||||
System.arraycopy(src.mSpanData, 0, mSpanData, 0, mSpanData.length);
|
||||
} else {
|
||||
int count = 0;
|
||||
int[] srcData = src.mSpanData;
|
||||
int limit = src.mSpanCount;
|
||||
for (int i = 0; i < limit; i++) {
|
||||
int spanStart = srcData[i * COLUMNS + START];
|
||||
int spanEnd = srcData[i * COLUMNS + END];
|
||||
if (isOutOfCopyRange(start, end, spanStart, spanEnd)) continue;
|
||||
count++;
|
||||
}
|
||||
|
||||
if (count == 0) return;
|
||||
|
||||
Object[] srcSpans = src.mSpans;
|
||||
mSpanCount = count;
|
||||
mSpans = ArrayUtils.newUnpaddedObjectArray(mSpanCount);
|
||||
mSpanData = new int[mSpans.length * COLUMNS];
|
||||
for (int i = 0, j = 0; i < limit; i++) {
|
||||
int spanStart = srcData[i * COLUMNS + START];
|
||||
int spanEnd = srcData[i * COLUMNS + END];
|
||||
if (isOutOfCopyRange(start, end, spanStart, spanEnd)) continue;
|
||||
if (isOutOfCopyRange(start, end, spanStart, spanEnd)
|
||||
|| (ignoreNoCopySpan && srcSpans[i] instanceof NoCopySpan)) {
|
||||
continue;
|
||||
}
|
||||
if (spanStart < start) spanStart = start;
|
||||
if (spanEnd > end) spanEnd = end;
|
||||
|
||||
@@ -494,6 +520,21 @@ import java.lang.reflect.Array;
|
||||
return hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Following two unused methods are left since these are listed in hidden api list.
|
||||
*
|
||||
* Due to backward compatibility reasons, we copy even NoCopySpan by default
|
||||
*/
|
||||
private void copySpans(Spanned src, int start, int end) {
|
||||
copySpans(src, start, end, false);
|
||||
}
|
||||
|
||||
private void copySpans(SpannableStringInternal src, int start, int end) {
|
||||
copySpans(src, start, end, false);
|
||||
}
|
||||
|
||||
|
||||
|
||||
private String mText;
|
||||
private Object[] mSpans;
|
||||
private int[] mSpanData;
|
||||
|
||||
@@ -26,12 +26,27 @@ public final class SpannedString
|
||||
extends SpannableStringInternal
|
||||
implements CharSequence, GetChars, Spanned
|
||||
{
|
||||
/**
|
||||
* @param source source object to copy from
|
||||
* @param ignoreNoCopySpan whether to copy NoCopySpans in the {@code source}
|
||||
* @hide
|
||||
*/
|
||||
public SpannedString(CharSequence source, boolean ignoreNoCopySpan) {
|
||||
super(source, 0, source.length(), ignoreNoCopySpan);
|
||||
}
|
||||
|
||||
/**
|
||||
* For the backward compatibility reasons, this constructor copies all spans including {@link
|
||||
* android.text.NoCopySpan}.
|
||||
* @param source source text
|
||||
*/
|
||||
public SpannedString(CharSequence source) {
|
||||
super(source, 0, source.length());
|
||||
this(source, false /* ignoreNoCopySpan */); // preserve existing NoCopySpan behavior
|
||||
}
|
||||
|
||||
private SpannedString(CharSequence source, int start, int end) {
|
||||
super(source, start, end);
|
||||
// preserve existing NoCopySpan behavior
|
||||
super(source, start, end, false /* ignoreNoCopySpan */);
|
||||
}
|
||||
|
||||
public CharSequence subSequence(int start, int end) {
|
||||
|
||||
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* 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 android.text;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.support.test.filters.SmallTest;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.text.style.QuoteSpan;
|
||||
import android.text.style.UnderlineSpan;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class SpannableStringNoCopyTest {
|
||||
@Test
|
||||
public void testCopyConstructor_copyNoCopySpans_SpannableStringInternalImpl() {
|
||||
final SpannableString first = new SpannableString("t\nest data");
|
||||
first.setSpan(new QuoteSpan(), 0, 2, Spanned.SPAN_PARAGRAPH);
|
||||
first.setSpan(new NoCopySpan.Concrete(), 2, 4, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
first.setSpan(new UnderlineSpan(), 0, first.length(), Spanned.SPAN_PRIORITY);
|
||||
|
||||
// By default, copy NoCopySpans
|
||||
final SpannedString copied = new SpannedString(first);
|
||||
final Object[] spans = copied.getSpans(0, copied.length(), Object.class);
|
||||
assertNotNull(spans);
|
||||
assertEquals(3, spans.length);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCopyConstructor_doesNotCopyNoCopySpans_SpannableStringInternalImpl() {
|
||||
final SpannableString first = new SpannableString("t\nest data");
|
||||
first.setSpan(new QuoteSpan(), 0, 2, Spanned.SPAN_PARAGRAPH);
|
||||
first.setSpan(new NoCopySpan.Concrete(), 2, 4, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
first.setSpan(new UnderlineSpan(), 0, first.length(), Spanned.SPAN_PRIORITY);
|
||||
|
||||
// Do not copy NoCopySpan if specified so.
|
||||
final SpannedString copied = new SpannedString(first, false /* copyNoCopySpan */);
|
||||
final Object[] spans = copied.getSpans(0, copied.length(), Object.class);
|
||||
assertNotNull(spans);
|
||||
assertEquals(2, spans.length);
|
||||
|
||||
for (int i = 0; i < spans.length; i++) {
|
||||
assertFalse(spans[i] instanceof NoCopySpan);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCopyConstructor_copyNoCopySpans_OtherSpannableImpl() {
|
||||
final SpannableString first = new SpannableString("t\nest data");
|
||||
first.setSpan(new QuoteSpan(), 0, 2, Spanned.SPAN_PARAGRAPH);
|
||||
first.setSpan(new NoCopySpan.Concrete(), 2, 4, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
first.setSpan(new UnderlineSpan(), 0, first.length(), Spanned.SPAN_PRIORITY);
|
||||
|
||||
// By default, copy NoCopySpans
|
||||
final SpannedString copied = new SpannedString(new CustomSpannable(first));
|
||||
final Object[] spans = copied.getSpans(0, copied.length(), Object.class);
|
||||
assertNotNull(spans);
|
||||
assertEquals(3, spans.length);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCopyConstructor_doesNotCopyNoCopySpans_OtherSpannableImpl() {
|
||||
final SpannableString first = new SpannableString("t\nest data");
|
||||
first.setSpan(new QuoteSpan(), 0, 2, Spanned.SPAN_PARAGRAPH);
|
||||
first.setSpan(new NoCopySpan.Concrete(), 2, 4, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
first.setSpan(new UnderlineSpan(), 0, first.length(), Spanned.SPAN_PRIORITY);
|
||||
|
||||
// Do not copy NoCopySpan if specified so.
|
||||
final SpannedString copied = new SpannedString(
|
||||
new CustomSpannable(first), false /* copyNoCopySpan */);
|
||||
final Object[] spans = copied.getSpans(0, copied.length(), Object.class);
|
||||
assertNotNull(spans);
|
||||
assertEquals(2, spans.length);
|
||||
|
||||
for (int i = 0; i < spans.length; i++) {
|
||||
assertFalse(spans[i] instanceof NoCopySpan);
|
||||
}
|
||||
}
|
||||
|
||||
// A custom implementation of Spannable.
|
||||
private static class CustomSpannable implements Spannable {
|
||||
private final @NonNull Spannable mText;
|
||||
|
||||
CustomSpannable(@NonNull Spannable text) {
|
||||
mText = text;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSpan(Object what, int start, int end, int flags) {
|
||||
mText.setSpan(what, start, end, flags);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeSpan(Object what) {
|
||||
mText.removeSpan(what);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T[] getSpans(int start, int end, Class<T> type) {
|
||||
return mText.getSpans(start, end, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSpanStart(Object tag) {
|
||||
return mText.getSpanStart(tag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSpanEnd(Object tag) {
|
||||
return mText.getSpanEnd(tag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSpanFlags(Object tag) {
|
||||
return mText.getSpanFlags(tag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int nextSpanTransition(int start, int limit, Class type) {
|
||||
return mText.nextSpanTransition(start, limit, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int length() {
|
||||
return mText.length();
|
||||
}
|
||||
|
||||
@Override
|
||||
public char charAt(int index) {
|
||||
return mText.charAt(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence subSequence(int start, int end) {
|
||||
return mText.subSequence(start, end);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return mText.toString();
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* 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 android.text;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.support.test.filters.SmallTest;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.text.style.QuoteSpan;
|
||||
import android.text.style.UnderlineSpan;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class SpannedStringNoCopyTest {
|
||||
@Test
|
||||
public void testCopyConstructor_copyNoCopySpans_SpannableStringInternalImpl() {
|
||||
final SpannableString first = new SpannableString("t\nest data");
|
||||
first.setSpan(new QuoteSpan(), 0, 2, Spanned.SPAN_PARAGRAPH);
|
||||
first.setSpan(new NoCopySpan.Concrete(), 2, 4, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
first.setSpan(new UnderlineSpan(), 0, first.length(), Spanned.SPAN_PRIORITY);
|
||||
|
||||
// By default, copy NoCopySpans
|
||||
final SpannedString copied = new SpannedString(first);
|
||||
final Object[] spans = copied.getSpans(0, copied.length(), Object.class);
|
||||
assertNotNull(spans);
|
||||
assertEquals(3, spans.length);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCopyConstructor_doesNotCopyNoCopySpans_SpannableStringInternalImpl() {
|
||||
final SpannableString first = new SpannableString("t\nest data");
|
||||
first.setSpan(new QuoteSpan(), 0, 2, Spanned.SPAN_PARAGRAPH);
|
||||
first.setSpan(new NoCopySpan.Concrete(), 2, 4, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
first.setSpan(new UnderlineSpan(), 0, first.length(), Spanned.SPAN_PRIORITY);
|
||||
|
||||
// Do not copy NoCopySpan if specified so.
|
||||
final SpannedString copied = new SpannedString(first, false /* copyNoCopySpan */);
|
||||
final Object[] spans = copied.getSpans(0, copied.length(), Object.class);
|
||||
assertNotNull(spans);
|
||||
assertEquals(2, spans.length);
|
||||
|
||||
for (int i = 0; i < spans.length; i++) {
|
||||
assertFalse(spans[i] instanceof NoCopySpan);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCopyConstructor_copyNoCopySpans_OtherSpannedImpl() {
|
||||
final SpannableString first = new SpannableString("t\nest data");
|
||||
first.setSpan(new QuoteSpan(), 0, 2, Spanned.SPAN_PARAGRAPH);
|
||||
first.setSpan(new NoCopySpan.Concrete(), 2, 4, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
first.setSpan(new UnderlineSpan(), 0, first.length(), Spanned.SPAN_PRIORITY);
|
||||
|
||||
// By default, copy NoCopySpans
|
||||
final SpannedString copied = new SpannedString(new CustomSpanned(first));
|
||||
final Object[] spans = copied.getSpans(0, copied.length(), Object.class);
|
||||
assertNotNull(spans);
|
||||
assertEquals(3, spans.length);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCopyConstructor_doesNotCopyNoCopySpans_OtherSpannedImpl() {
|
||||
final SpannableString first = new SpannableString("t\nest data");
|
||||
first.setSpan(new QuoteSpan(), 0, 2, Spanned.SPAN_PARAGRAPH);
|
||||
first.setSpan(new NoCopySpan.Concrete(), 2, 4, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
first.setSpan(new UnderlineSpan(), 0, first.length(), Spanned.SPAN_PRIORITY);
|
||||
|
||||
// Do not copy NoCopySpan if specified so.
|
||||
final SpannedString copied = new SpannedString(
|
||||
new CustomSpanned(first), false /* copyNoCopySpan */);
|
||||
final Object[] spans = copied.getSpans(0, copied.length(), Object.class);
|
||||
assertNotNull(spans);
|
||||
assertEquals(2, spans.length);
|
||||
|
||||
for (int i = 0; i < spans.length; i++) {
|
||||
assertFalse(spans[i] instanceof NoCopySpan);
|
||||
}
|
||||
}
|
||||
|
||||
// A custom implementation of Spanned
|
||||
private static class CustomSpanned implements Spanned {
|
||||
private final @NonNull Spanned mText;
|
||||
|
||||
CustomSpanned(@NonNull Spannable text) {
|
||||
mText = text;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T[] getSpans(int start, int end, Class<T> type) {
|
||||
return mText.getSpans(start, end, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSpanStart(Object tag) {
|
||||
return mText.getSpanStart(tag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSpanEnd(Object tag) {
|
||||
return mText.getSpanEnd(tag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSpanFlags(Object tag) {
|
||||
return mText.getSpanFlags(tag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int nextSpanTransition(int start, int limit, Class type) {
|
||||
return mText.nextSpanTransition(start, limit, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int length() {
|
||||
return mText.length();
|
||||
}
|
||||
|
||||
@Override
|
||||
public char charAt(int index) {
|
||||
return mText.charAt(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence subSequence(int start, int end) {
|
||||
return mText.subSequence(start, end);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return mText.toString();
|
||||
}
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user