1 /*
2  * Copyright (C) 2014 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.compat;
18 
19 import android.graphics.Typeface;
20 import android.os.Build;
21 import android.test.AndroidTestCase;
22 import android.test.suitebuilder.annotation.SmallTest;
23 import android.text.SpannableString;
24 import android.text.Spanned;
25 import android.text.style.StyleSpan;
26 
27 import java.util.Locale;
28 
29 @SmallTest
30 public class LocaleSpanCompatUtilsTests extends AndroidTestCase {
testInstantiatable()31     public void testInstantiatable() {
32         if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
33             // LocaleSpan isn't yet available.
34             return;
35         }
36         assertTrue(LocaleSpanCompatUtils.isLocaleSpanAvailable());
37         final Object japaneseLocaleSpan = LocaleSpanCompatUtils.newLocaleSpan(Locale.JAPANESE);
38         assertNotNull(japaneseLocaleSpan);
39         assertEquals(Locale.JAPANESE,
40                 LocaleSpanCompatUtils.getLocaleFromLocaleSpan(japaneseLocaleSpan));
41     }
42 
assertLocaleSpan(final Spanned spanned, final int index, final int expectedStart, final int expectedEnd, final Locale expectedLocale, final int expectedSpanFlags)43     private static void assertLocaleSpan(final Spanned spanned, final int index,
44             final int expectedStart, final int expectedEnd,
45             final Locale expectedLocale, final int expectedSpanFlags) {
46         final Object span = spanned.getSpans(0, spanned.length(), Object.class)[index];
47         assertEquals(expectedLocale, LocaleSpanCompatUtils.getLocaleFromLocaleSpan(span));
48         assertEquals(expectedStart, spanned.getSpanStart(span));
49         assertEquals(expectedEnd, spanned.getSpanEnd(span));
50         assertEquals(expectedSpanFlags, spanned.getSpanFlags(span));
51     }
52 
assertSpanEquals(final Object expectedSpan, final Spanned spanned, final int index)53     private static void assertSpanEquals(final Object expectedSpan, final Spanned spanned,
54             final int index) {
55         final Object[] spans = spanned.getSpans(0, spanned.length(), Object.class);
56         assertEquals(expectedSpan, spans[index]);
57     }
58 
assertSpanCount(final int expectedCount, final Spanned spanned)59     private static void assertSpanCount(final int expectedCount, final Spanned spanned) {
60         final Object[] spans = spanned.getSpans(0, spanned.length(), Object.class);
61         assertEquals(expectedCount, spans.length);
62     }
63 
testUpdateLocaleSpan()64     public void testUpdateLocaleSpan() {
65         if (!LocaleSpanCompatUtils.isLocaleSpanAvailable()) {
66             return;
67         }
68 
69         // Test if the simplest case works.
70         {
71             final SpannableString text = new SpannableString("0123456789");
72             LocaleSpanCompatUtils.updateLocaleSpan(text, 1, 5, Locale.JAPANESE);
73             assertSpanCount(1, text);
74             assertLocaleSpan(text, 0, 1, 5, Locale.JAPANESE, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
75         }
76 
77         // Test if only LocaleSpans are updated.
78         {
79             final SpannableString text = new SpannableString("0123456789");
80             final StyleSpan styleSpan = new StyleSpan(Typeface.BOLD);
81             text.setSpan(styleSpan, 0, 7, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
82             LocaleSpanCompatUtils.updateLocaleSpan(text, 1, 5, Locale.JAPANESE);
83             assertSpanCount(2, text);
84             assertSpanEquals(styleSpan, text, 0);
85             assertLocaleSpan(text, 1, 1, 5, Locale.JAPANESE, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
86         }
87 
88         // Test if two jointed spans are merged into one span.
89         {
90             final SpannableString text = new SpannableString("0123456789");
91             text.setSpan(LocaleSpanCompatUtils.newLocaleSpan(Locale.JAPANESE), 1, 3,
92                     Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
93             LocaleSpanCompatUtils.updateLocaleSpan(text, 3, 5, Locale.JAPANESE);
94             assertSpanCount(1, text);
95             assertLocaleSpan(text, 0, 1, 5, Locale.JAPANESE, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
96         }
97 
98         // Test if two overlapped spans are merged into one span.
99         {
100             final SpannableString text = new SpannableString("0123456789");
101             text.setSpan(LocaleSpanCompatUtils.newLocaleSpan(Locale.JAPANESE), 1, 4,
102                     Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
103             LocaleSpanCompatUtils.updateLocaleSpan(text, 3, 5, Locale.JAPANESE);
104             assertSpanCount(1, text);
105             assertLocaleSpan(text, 0, 1, 5, Locale.JAPANESE, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
106         }
107 
108         // Test if three overlapped spans are merged into one span.
109         {
110             final SpannableString text = new SpannableString("0123456789");
111             text.setSpan(LocaleSpanCompatUtils.newLocaleSpan(Locale.JAPANESE), 1, 4,
112                     Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
113             text.setSpan(LocaleSpanCompatUtils.newLocaleSpan(Locale.JAPANESE), 5, 6,
114                     Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
115             LocaleSpanCompatUtils.updateLocaleSpan(text, 2, 8, Locale.JAPANESE);
116             assertSpanCount(1, text);
117             assertLocaleSpan(text, 0, 1, 8, Locale.JAPANESE, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
118         }
119 
120         // Test if disjoint spans remain disjoint.
121         {
122             final SpannableString text = new SpannableString("0123456789");
123             text.setSpan(LocaleSpanCompatUtils.newLocaleSpan(Locale.JAPANESE), 1, 3,
124                     Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
125             text.setSpan(LocaleSpanCompatUtils.newLocaleSpan(Locale.JAPANESE), 5, 6,
126                     Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
127             LocaleSpanCompatUtils.updateLocaleSpan(text, 8, 9, Locale.JAPANESE);
128             assertSpanCount(3, text);
129             assertLocaleSpan(text, 0, 1, 3, Locale.JAPANESE, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
130             assertLocaleSpan(text, 1, 5, 6, Locale.JAPANESE, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
131             assertLocaleSpan(text, 2, 8, 9, Locale.JAPANESE, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
132         }
133 
134         // Test if existing span flags are preserved during merge.
135         {
136             final SpannableString text = new SpannableString("0123456789");
137             text.setSpan(LocaleSpanCompatUtils.newLocaleSpan(Locale.JAPANESE), 1, 5,
138                     Spanned.SPAN_INCLUSIVE_INCLUSIVE | Spanned.SPAN_INTERMEDIATE);
139             LocaleSpanCompatUtils.updateLocaleSpan(text, 3, 4, Locale.JAPANESE);
140             assertSpanCount(1, text);
141             assertLocaleSpan(text, 0, 1, 5, Locale.JAPANESE,
142                     Spanned.SPAN_INCLUSIVE_INCLUSIVE | Spanned.SPAN_INTERMEDIATE);
143         }
144 
145         // Test if existing span flags are preserved even when partially overlapped (leading edge).
146         {
147             final SpannableString text = new SpannableString("0123456789");
148             text.setSpan(LocaleSpanCompatUtils.newLocaleSpan(Locale.JAPANESE), 1, 5,
149                     Spanned.SPAN_INCLUSIVE_INCLUSIVE | Spanned.SPAN_INTERMEDIATE);
150             LocaleSpanCompatUtils.updateLocaleSpan(text, 3, 7, Locale.JAPANESE);
151             assertSpanCount(1, text);
152             assertLocaleSpan(text, 0, 1, 7, Locale.JAPANESE,
153                     Spanned.SPAN_INCLUSIVE_EXCLUSIVE | Spanned.SPAN_INTERMEDIATE);
154         }
155 
156         // Test if existing span flags are preserved even when partially overlapped (trailing edge).
157         {
158             final SpannableString text = new SpannableString("0123456789");
159             text.setSpan(LocaleSpanCompatUtils.newLocaleSpan(Locale.JAPANESE), 3, 7,
160                     Spanned.SPAN_INCLUSIVE_INCLUSIVE | Spanned.SPAN_INTERMEDIATE);
161             LocaleSpanCompatUtils.updateLocaleSpan(text, 1, 5, Locale.JAPANESE);
162             assertSpanCount(1, text);
163             assertLocaleSpan(text, 0, 1, 7, Locale.JAPANESE,
164                     Spanned.SPAN_EXCLUSIVE_INCLUSIVE | Spanned.SPAN_INTERMEDIATE);
165         }
166 
167         // Test if existing locale span will be removed when the locale doesn't match.
168         {
169             final SpannableString text = new SpannableString("0123456789");
170             text.setSpan(LocaleSpanCompatUtils.newLocaleSpan(Locale.ENGLISH), 3, 5,
171                     Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
172             LocaleSpanCompatUtils.updateLocaleSpan(text, 1, 7, Locale.JAPANESE);
173             assertSpanCount(1, text);
174             assertLocaleSpan(text, 0, 1, 7, Locale.JAPANESE, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
175         }
176 
177         // Test if existing locale span will be removed when the locale doesn't match. (case 2)
178         {
179             final SpannableString text = new SpannableString("0123456789");
180             text.setSpan(LocaleSpanCompatUtils.newLocaleSpan(Locale.ENGLISH), 3, 7,
181                     Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
182             LocaleSpanCompatUtils.updateLocaleSpan(text, 5, 6, Locale.JAPANESE);
183             assertSpanCount(3, text);
184             assertLocaleSpan(text, 0, 3, 5, Locale.ENGLISH, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
185             assertLocaleSpan(text, 1, 6, 7, Locale.ENGLISH, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
186             assertLocaleSpan(text, 2, 5, 6, Locale.JAPANESE, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
187         }
188 
189         // Test if existing locale span will be removed when the locale doesn't match. (case 3)
190         {
191             final SpannableString text = new SpannableString("0123456789");
192             text.setSpan(LocaleSpanCompatUtils.newLocaleSpan(Locale.ENGLISH), 3, 7,
193                     Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
194             LocaleSpanCompatUtils.updateLocaleSpan(text, 2, 5, Locale.JAPANESE);
195             assertSpanCount(2, text);
196             assertLocaleSpan(text, 0, 5, 7, Locale.ENGLISH, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
197             assertLocaleSpan(text, 1, 2, 5, Locale.JAPANESE, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
198         }
199 
200         // Test if existing locale span will be removed when the locale doesn't match. (case 3)
201         {
202             final SpannableString text = new SpannableString("0123456789");
203             text.setSpan(LocaleSpanCompatUtils.newLocaleSpan(Locale.ENGLISH), 3, 7,
204                     Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
205             LocaleSpanCompatUtils.updateLocaleSpan(text, 5, 8, Locale.JAPANESE);
206             assertSpanCount(2, text);
207             assertLocaleSpan(text, 0, 3, 5, Locale.ENGLISH, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
208             assertLocaleSpan(text, 1, 5, 8, Locale.JAPANESE, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
209         }
210     }
211 }
212