Loading core/java/android/text/method/AllCapsTransformationMethod.java +60 −2 Original line number Diff line number Diff line Loading @@ -17,6 +17,10 @@ package android.text.method; import android.content.Context; import android.graphics.Rect; import android.icu.text.CaseMap; import android.icu.text.Edits; import android.text.SpannableStringBuilder; import android.text.Spanned; import android.util.Log; import android.view.View; import android.widget.TextView; Loading @@ -35,7 +39,7 @@ public class AllCapsTransformationMethod implements TransformationMethod2 { private Locale mLocale; public AllCapsTransformationMethod(Context context) { mLocale = context.getResources().getConfiguration().locale; mLocale = context.getResources().getConfiguration().getLocales().get(0); } @Override Loading @@ -56,7 +60,61 @@ public class AllCapsTransformationMethod implements TransformationMethod2 { if (locale == null) { locale = mLocale; } return source.toString().toUpperCase(locale); if (!(source instanceof Spanned)) { // No spans return CaseMap.toUpper().apply( locale, source, new StringBuilder(), null /* we don't need the edits */); } final Edits edits = new Edits(); final SpannableStringBuilder result = CaseMap.toUpper().apply( locale, source, new SpannableStringBuilder(), edits); if (!edits.hasChanges()) { // No changes happened while capitalizing. We can return the source as it was. return source; } final Edits.Iterator iterator = edits.getFineIterator(); final Spanned spanned = (Spanned) source; final int sourceLength = source.length(); final Object[] spans = spanned.getSpans(0, sourceLength, Object.class); for (Object span : spans) { final int sourceStart = spanned.getSpanStart(span); final int sourceEnd = spanned.getSpanEnd(span); final int flags = spanned.getSpanFlags(span); // Make sure the indexes are not at the end of the string, since in that case // iterator.findSourceIndex() would fail. final int destStart = sourceStart == sourceLength ? result.length() : mapToDest(iterator, sourceStart); final int destEnd = sourceEnd == sourceLength ? result.length() : mapToDest(iterator, sourceEnd); result.setSpan(span, destStart, destEnd, flags); } return result; } private static int mapToDest(Edits.Iterator iterator, int sourceIndex) { // Guaranteed to succeed if sourceIndex < source.length(). iterator.findSourceIndex(sourceIndex); if (sourceIndex == iterator.sourceIndex()) { return iterator.destinationIndex(); } // We handle the situation differently depending on if we are in the changed slice or an // unchanged one: In an unchanged slice, we can find the exact location the span // boundary was before and map there. // // But in a changed slice, we need to treat the whole destination slice as an atomic unit. // We adjust the span boundary to the end of that slice to reduce of the chance of adjacent // spans in the source overlapping in the result. (The choice for the end vs the beginning // is somewhat arbitrary, but was taken because we except to see slightly more spans only // affecting a base character compared to spans only affecting a combining character.) if (iterator.hasChange()) { return iterator.destinationIndex() + iterator.newLength(); } else { // Move the index 1:1 along with this unchanged piece of text. return iterator.destinationIndex() + (sourceIndex - iterator.sourceIndex()); } } @Override Loading Loading
core/java/android/text/method/AllCapsTransformationMethod.java +60 −2 Original line number Diff line number Diff line Loading @@ -17,6 +17,10 @@ package android.text.method; import android.content.Context; import android.graphics.Rect; import android.icu.text.CaseMap; import android.icu.text.Edits; import android.text.SpannableStringBuilder; import android.text.Spanned; import android.util.Log; import android.view.View; import android.widget.TextView; Loading @@ -35,7 +39,7 @@ public class AllCapsTransformationMethod implements TransformationMethod2 { private Locale mLocale; public AllCapsTransformationMethod(Context context) { mLocale = context.getResources().getConfiguration().locale; mLocale = context.getResources().getConfiguration().getLocales().get(0); } @Override Loading @@ -56,7 +60,61 @@ public class AllCapsTransformationMethod implements TransformationMethod2 { if (locale == null) { locale = mLocale; } return source.toString().toUpperCase(locale); if (!(source instanceof Spanned)) { // No spans return CaseMap.toUpper().apply( locale, source, new StringBuilder(), null /* we don't need the edits */); } final Edits edits = new Edits(); final SpannableStringBuilder result = CaseMap.toUpper().apply( locale, source, new SpannableStringBuilder(), edits); if (!edits.hasChanges()) { // No changes happened while capitalizing. We can return the source as it was. return source; } final Edits.Iterator iterator = edits.getFineIterator(); final Spanned spanned = (Spanned) source; final int sourceLength = source.length(); final Object[] spans = spanned.getSpans(0, sourceLength, Object.class); for (Object span : spans) { final int sourceStart = spanned.getSpanStart(span); final int sourceEnd = spanned.getSpanEnd(span); final int flags = spanned.getSpanFlags(span); // Make sure the indexes are not at the end of the string, since in that case // iterator.findSourceIndex() would fail. final int destStart = sourceStart == sourceLength ? result.length() : mapToDest(iterator, sourceStart); final int destEnd = sourceEnd == sourceLength ? result.length() : mapToDest(iterator, sourceEnd); result.setSpan(span, destStart, destEnd, flags); } return result; } private static int mapToDest(Edits.Iterator iterator, int sourceIndex) { // Guaranteed to succeed if sourceIndex < source.length(). iterator.findSourceIndex(sourceIndex); if (sourceIndex == iterator.sourceIndex()) { return iterator.destinationIndex(); } // We handle the situation differently depending on if we are in the changed slice or an // unchanged one: In an unchanged slice, we can find the exact location the span // boundary was before and map there. // // But in a changed slice, we need to treat the whole destination slice as an atomic unit. // We adjust the span boundary to the end of that slice to reduce of the chance of adjacent // spans in the source overlapping in the result. (The choice for the end vs the beginning // is somewhat arbitrary, but was taken because we except to see slightly more spans only // affecting a base character compared to spans only affecting a combining character.) if (iterator.hasChange()) { return iterator.destinationIndex() + iterator.newLength(); } else { // Move the index 1:1 along with this unchanged piece of text. return iterator.destinationIndex() + (sourceIndex - iterator.sourceIndex()); } } @Override Loading