1 /* 2 * Copyright (C) 2019 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 android.view.inputmethod.cts; 18 19 import static android.view.inputmethod.CursorAnchorInfo.FLAG_HAS_INVISIBLE_REGION; 20 import static android.view.inputmethod.CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION; 21 import static android.view.inputmethod.CursorAnchorInfo.FLAG_IS_RTL; 22 23 import static org.junit.Assert.assertEquals; 24 import static org.junit.Assert.assertNotEquals; 25 import static org.junit.Assert.assertNull; 26 import static org.junit.Assert.assertTrue; 27 import static org.junit.Assert.fail; 28 29 import android.graphics.Matrix; 30 import android.graphics.RectF; 31 import android.os.Parcel; 32 import android.text.TextUtils; 33 import android.view.inputmethod.CursorAnchorInfo; 34 import android.view.inputmethod.CursorAnchorInfo.Builder; 35 36 import androidx.test.filters.SmallTest; 37 import androidx.test.runner.AndroidJUnit4; 38 39 import org.junit.Test; 40 import org.junit.runner.RunWith; 41 42 @SmallTest 43 @RunWith(AndroidJUnit4.class) 44 public class CursorAnchorInfoTest { 45 private static final float EPSILON = 0.0000001f; 46 47 private static final RectF[] MANY_BOUNDS = new RectF[] { 48 new RectF(101.0f, 201.0f, 301.0f, 401.0f), 49 new RectF(102.0f, 202.0f, 302.0f, 402.0f), 50 new RectF(103.0f, 203.0f, 303.0f, 403.0f), 51 new RectF(104.0f, 204.0f, 304.0f, 404.0f), 52 new RectF(105.0f, 205.0f, 305.0f, 405.0f), 53 new RectF(106.0f, 206.0f, 306.0f, 406.0f), 54 new RectF(107.0f, 207.0f, 307.0f, 407.0f), 55 new RectF(108.0f, 208.0f, 308.0f, 408.0f), 56 new RectF(109.0f, 209.0f, 309.0f, 409.0f), 57 new RectF(110.0f, 210.0f, 310.0f, 410.0f), 58 new RectF(111.0f, 211.0f, 311.0f, 411.0f), 59 new RectF(112.0f, 212.0f, 312.0f, 412.0f), 60 new RectF(113.0f, 213.0f, 313.0f, 413.0f), 61 new RectF(114.0f, 214.0f, 314.0f, 414.0f), 62 new RectF(115.0f, 215.0f, 315.0f, 415.0f), 63 new RectF(116.0f, 216.0f, 316.0f, 416.0f), 64 new RectF(117.0f, 217.0f, 317.0f, 417.0f), 65 new RectF(118.0f, 218.0f, 318.0f, 418.0f), 66 new RectF(119.0f, 219.0f, 319.0f, 419.0f), 67 }; 68 private static final int[] MANY_FLAGS_ARRAY = new int[] { 69 FLAG_HAS_INVISIBLE_REGION, 70 FLAG_HAS_INVISIBLE_REGION | FLAG_HAS_VISIBLE_REGION, 71 FLAG_HAS_VISIBLE_REGION, 72 FLAG_HAS_VISIBLE_REGION, 73 FLAG_HAS_VISIBLE_REGION, 74 FLAG_HAS_VISIBLE_REGION, 75 FLAG_HAS_VISIBLE_REGION | FLAG_IS_RTL, 76 FLAG_HAS_INVISIBLE_REGION | FLAG_HAS_VISIBLE_REGION | FLAG_IS_RTL, 77 FLAG_HAS_INVISIBLE_REGION | FLAG_IS_RTL, 78 FLAG_HAS_VISIBLE_REGION | FLAG_IS_RTL, 79 FLAG_HAS_VISIBLE_REGION, 80 FLAG_HAS_VISIBLE_REGION | FLAG_IS_RTL, 81 FLAG_HAS_VISIBLE_REGION, 82 FLAG_HAS_VISIBLE_REGION | FLAG_IS_RTL, 83 FLAG_HAS_VISIBLE_REGION, 84 FLAG_HAS_VISIBLE_REGION | FLAG_IS_RTL, 85 FLAG_HAS_VISIBLE_REGION, 86 FLAG_HAS_INVISIBLE_REGION, 87 FLAG_HAS_INVISIBLE_REGION | FLAG_IS_RTL, 88 }; 89 90 @Test 91 public void testBuilder() { 92 final int selectionStart = 30; 93 final int selectionEnd = 40; 94 final int composingTextStart = 32; 95 final String composingText = "test"; 96 final int insertionMarkerFlags = 97 FLAG_HAS_VISIBLE_REGION | FLAG_HAS_INVISIBLE_REGION | FLAG_IS_RTL; 98 final float insertionMarkerHorizontal = 10.5f; 99 final float insertionMarkerTop = 100.1f; 100 final float insertionMarkerBaseline = 110.4f; 101 final float insertionMarkerBottom = 111.0f; 102 103 Matrix transformMatrix = new Matrix(); 104 transformMatrix.setScale(10.0f, 20.0f); 105 106 final Builder builder = new Builder(); 107 builder.setSelectionRange(selectionStart, selectionEnd) 108 .setComposingText(composingTextStart, composingText) 109 .setInsertionMarkerLocation(insertionMarkerHorizontal, insertionMarkerTop, 110 insertionMarkerBaseline, insertionMarkerBottom, insertionMarkerFlags) 111 .setMatrix(transformMatrix); 112 for (int i = 0; i < MANY_BOUNDS.length; i++) { 113 final RectF bounds = MANY_BOUNDS[i]; 114 final int flags = MANY_FLAGS_ARRAY[i]; 115 builder.addCharacterBounds(i, bounds.left, bounds.top, bounds.right, bounds.bottom, 116 flags); 117 } 118 119 final CursorAnchorInfo info = builder.build(); 120 assertEquals(selectionStart, info.getSelectionStart()); 121 assertEquals(selectionEnd, info.getSelectionEnd()); 122 assertEquals(composingTextStart, info.getComposingTextStart()); 123 assertTrue(TextUtils.equals(composingText, info.getComposingText())); 124 assertEquals(insertionMarkerFlags, info.getInsertionMarkerFlags()); 125 assertEquals(insertionMarkerHorizontal, info.getInsertionMarkerHorizontal(), EPSILON); 126 assertEquals(insertionMarkerTop, info.getInsertionMarkerTop(), EPSILON); 127 assertEquals(insertionMarkerBaseline, info.getInsertionMarkerBaseline(), EPSILON); 128 assertEquals(insertionMarkerBottom, info.getInsertionMarkerBottom(), EPSILON); 129 assertEquals(transformMatrix, info.getMatrix()); 130 for (int i = 0; i < MANY_BOUNDS.length; i++) { 131 final RectF expectedBounds = MANY_BOUNDS[i]; 132 assertEquals(expectedBounds, info.getCharacterBounds(i)); 133 } 134 assertNull(info.getCharacterBounds(-1)); 135 assertNull(info.getCharacterBounds(MANY_BOUNDS.length + 1)); 136 for (int i = 0; i < MANY_FLAGS_ARRAY.length; i++) { 137 final int expectedFlags = MANY_FLAGS_ARRAY[i]; 138 assertEquals(expectedFlags, info.getCharacterBoundsFlags(i)); 139 } 140 assertEquals(0, info.getCharacterBoundsFlags(-1)); 141 assertEquals(0, info.getCharacterBoundsFlags(MANY_BOUNDS.length + 1)); 142 143 // Make sure that the builder can reproduce the same object. 144 final CursorAnchorInfo info2 = builder.build(); 145 assertEquals(selectionStart, info2.getSelectionStart()); 146 assertEquals(selectionEnd, info2.getSelectionEnd()); 147 assertEquals(composingTextStart, info2.getComposingTextStart()); 148 assertTrue(TextUtils.equals(composingText, info2.getComposingText())); 149 assertEquals(insertionMarkerFlags, info2.getInsertionMarkerFlags()); 150 assertEquals(insertionMarkerHorizontal, info2.getInsertionMarkerHorizontal(), EPSILON); 151 assertEquals(insertionMarkerTop, info2.getInsertionMarkerTop(), EPSILON); 152 assertEquals(insertionMarkerBaseline, info2.getInsertionMarkerBaseline(), EPSILON); 153 assertEquals(insertionMarkerBottom, info2.getInsertionMarkerBottom(), EPSILON); 154 assertEquals(transformMatrix, info2.getMatrix()); 155 for (int i = 0; i < MANY_BOUNDS.length; i++) { 156 final RectF expectedBounds = MANY_BOUNDS[i]; 157 assertEquals(expectedBounds, info2.getCharacterBounds(i)); 158 } 159 assertNull(info2.getCharacterBounds(-1)); 160 assertNull(info2.getCharacterBounds(MANY_BOUNDS.length + 1)); 161 for (int i = 0; i < MANY_FLAGS_ARRAY.length; i++) { 162 final int expectedFlags = MANY_FLAGS_ARRAY[i]; 163 assertEquals(expectedFlags, info2.getCharacterBoundsFlags(i)); 164 } 165 assertEquals(0, info2.getCharacterBoundsFlags(-1)); 166 assertEquals(0, info2.getCharacterBoundsFlags(MANY_BOUNDS.length + 1)); 167 assertEquals(info, info2); 168 assertEquals(info.hashCode(), info2.hashCode()); 169 170 // Make sure that object can be marshaled via Parcel. 171 final CursorAnchorInfo info3 = cloneViaParcel(info2); 172 assertEquals(selectionStart, info3.getSelectionStart()); 173 assertEquals(selectionEnd, info3.getSelectionEnd()); 174 assertEquals(composingTextStart, info3.getComposingTextStart()); 175 assertTrue(TextUtils.equals(composingText, info3.getComposingText())); 176 assertEquals(insertionMarkerFlags, info3.getInsertionMarkerFlags()); 177 assertEquals(insertionMarkerHorizontal, info3.getInsertionMarkerHorizontal(), EPSILON); 178 assertEquals(insertionMarkerTop, info3.getInsertionMarkerTop(), EPSILON); 179 assertEquals(insertionMarkerBaseline, info3.getInsertionMarkerBaseline(), EPSILON); 180 assertEquals(insertionMarkerBottom, info3.getInsertionMarkerBottom(), EPSILON); 181 assertEquals(transformMatrix, info3.getMatrix()); 182 for (int i = 0; i < MANY_BOUNDS.length; i++) { 183 final RectF expectedBounds = MANY_BOUNDS[i]; 184 assertEquals(expectedBounds, info3.getCharacterBounds(i)); 185 } 186 assertNull(info3.getCharacterBounds(-1)); 187 assertNull(info3.getCharacterBounds(MANY_BOUNDS.length + 1)); 188 for (int i = 0; i < MANY_FLAGS_ARRAY.length; i++) { 189 final int expectedFlags = MANY_FLAGS_ARRAY[i]; 190 assertEquals(expectedFlags, info3.getCharacterBoundsFlags(i)); 191 } 192 assertEquals(0, info3.getCharacterBoundsFlags(-1)); 193 assertEquals(0, info3.getCharacterBoundsFlags(MANY_BOUNDS.length + 1)); 194 assertEquals(info.hashCode(), info3.hashCode()); 195 196 builder.reset(); 197 final CursorAnchorInfo uninitializedInfo = builder.build(); 198 assertEquals(-1, uninitializedInfo.getSelectionStart()); 199 assertEquals(-1, uninitializedInfo.getSelectionEnd()); 200 assertEquals(-1, uninitializedInfo.getComposingTextStart()); 201 assertNull(uninitializedInfo.getComposingText()); 202 assertEquals(0, uninitializedInfo.getInsertionMarkerFlags()); 203 assertEquals(Float.NaN, uninitializedInfo.getInsertionMarkerHorizontal(), EPSILON); 204 assertEquals(Float.NaN, uninitializedInfo.getInsertionMarkerTop(), EPSILON); 205 assertEquals(Float.NaN, uninitializedInfo.getInsertionMarkerBaseline(), EPSILON); 206 assertEquals(Float.NaN, uninitializedInfo.getInsertionMarkerBottom(), EPSILON); 207 assertEquals(new Matrix(), uninitializedInfo.getMatrix()); 208 } 209 210 @Test 211 public void testEquality() { 212 final Matrix matrix1 = new Matrix(); 213 matrix1.setTranslate(10.0f, 20.0f); 214 final Matrix matrix2 = new Matrix(); 215 matrix2.setTranslate(110.0f, 120.0f); 216 final Matrix nanMatrix = new Matrix(); 217 nanMatrix.setValues(new float[]{ 218 Float.NaN, Float.NaN, Float.NaN, 219 Float.NaN, Float.NaN, Float.NaN, 220 Float.NaN, Float.NaN, Float.NaN}); 221 final int selectionStart1 = 2; 222 final int selectionEnd1 = 7; 223 final String composingText1 = "0123456789"; 224 final int composingTextStart1 = 0; 225 final int insertionMarkerFlags1 = FLAG_HAS_VISIBLE_REGION; 226 final float insertionMarkerHorizontal1 = 10.5f; 227 final float insertionMarkerTop1 = 100.1f; 228 final float insertionMarkerBaseline1 = 110.4f; 229 final float insertionMarkerBottom1 = 111.0f; 230 final int selectionStart2 = 4; 231 final int selectionEnd2 = 8; 232 final String composingText2 = "9876543210"; 233 final int composingTextStart2 = 3; 234 final int insertionMarkerFlags2 = 235 FLAG_HAS_VISIBLE_REGION | FLAG_HAS_INVISIBLE_REGION | FLAG_IS_RTL; 236 final float insertionMarkerHorizontal2 = 14.5f; 237 final float insertionMarkerTop2 = 200.1f; 238 final float insertionMarkerBaseline2 = 210.4f; 239 final float insertionMarkerBottom2 = 211.0f; 240 241 // Default instance should be equal. 242 assertEquals(new Builder().build(), new Builder().build()); 243 244 assertEquals( 245 new Builder().setSelectionRange(selectionStart1, selectionEnd1).build(), 246 new Builder().setSelectionRange(selectionStart1, selectionEnd1).build()); 247 assertNotEquals( 248 new Builder().setSelectionRange(selectionStart1, selectionEnd1).build(), 249 new Builder().setSelectionRange(selectionStart1, selectionEnd2).build()); 250 assertNotEquals( 251 new Builder().setSelectionRange(selectionStart1, selectionEnd1).build(), 252 new Builder().setSelectionRange(selectionStart2, selectionEnd1).build()); 253 assertNotEquals( 254 new Builder().setSelectionRange(selectionStart1, selectionEnd1).build(), 255 new Builder().setSelectionRange(selectionStart2, selectionEnd2).build()); 256 assertEquals( 257 new Builder().setComposingText(composingTextStart1, composingText1).build(), 258 new Builder().setComposingText(composingTextStart1, composingText1).build()); 259 assertNotEquals( 260 new Builder().setComposingText(composingTextStart1, composingText1).build(), 261 new Builder().setComposingText(composingTextStart2, composingText1).build()); 262 assertNotEquals( 263 new Builder().setComposingText(composingTextStart1, composingText1).build(), 264 new Builder().setComposingText(composingTextStart1, composingText2).build()); 265 assertNotEquals( 266 new Builder().setComposingText(composingTextStart1, composingText1).build(), 267 new Builder().setComposingText(composingTextStart2, composingText2).build()); 268 269 // For insertion marker locations, Float#NaN is treated as if it was a number. 270 assertEquals( 271 new Builder().setMatrix(matrix1).setInsertionMarkerLocation( 272 Float.NaN, Float.NaN, Float.NaN, Float.NaN, 273 insertionMarkerFlags1).build(), 274 new Builder().setMatrix(matrix1).setInsertionMarkerLocation( 275 Float.NaN, Float.NaN, Float.NaN, Float.NaN, 276 insertionMarkerFlags1).build()); 277 278 // Check Matrix. 279 assertEquals( 280 new Builder().setMatrix(matrix1).build(), 281 new Builder().setMatrix(matrix1).build()); 282 assertNotEquals( 283 new Builder().setMatrix(matrix1).build(), 284 new Builder().setMatrix(matrix2).build()); 285 assertNotEquals( 286 new Builder().setMatrix(matrix1).build(), 287 new Builder().setMatrix(nanMatrix).build()); 288 // Unlike insertion marker locations, Float#NaN in the matrix is treated as just a NaN as 289 // usual (NaN == NaN -> false). 290 assertNotEquals( 291 new Builder().setMatrix(nanMatrix).build(), 292 new Builder().setMatrix(nanMatrix).build()); 293 294 assertEquals( 295 new Builder().setMatrix(matrix1).setInsertionMarkerLocation( 296 insertionMarkerHorizontal1, insertionMarkerTop1, 297 insertionMarkerBaseline1, insertionMarkerBottom1, 298 insertionMarkerFlags1).build(), 299 new Builder().setMatrix(matrix1).setInsertionMarkerLocation( 300 insertionMarkerHorizontal1, insertionMarkerTop1, 301 insertionMarkerBaseline1, insertionMarkerBottom1, 302 insertionMarkerFlags1).build()); 303 assertNotEquals( 304 new Builder().setMatrix(matrix1).setInsertionMarkerLocation( 305 Float.NaN, insertionMarkerTop1, 306 insertionMarkerBaseline1, insertionMarkerBottom1, 307 insertionMarkerFlags1).build(), 308 new Builder().setMatrix(matrix1).setInsertionMarkerLocation( 309 insertionMarkerHorizontal1, insertionMarkerTop1, 310 insertionMarkerBaseline1, insertionMarkerBottom1, 311 insertionMarkerFlags1).build()); 312 assertNotEquals( 313 new Builder().setMatrix(matrix1).setInsertionMarkerLocation( 314 insertionMarkerHorizontal1, insertionMarkerTop1, 315 insertionMarkerBaseline1, insertionMarkerBottom1, 316 insertionMarkerFlags1).build(), 317 new Builder().setMatrix(matrix1).setInsertionMarkerLocation( 318 insertionMarkerHorizontal2, insertionMarkerTop1, 319 insertionMarkerBaseline1, insertionMarkerBottom1, 320 insertionMarkerFlags1).build()); 321 assertNotEquals( 322 new Builder().setMatrix(matrix1).setInsertionMarkerLocation( 323 insertionMarkerHorizontal1, insertionMarkerTop1, 324 insertionMarkerBaseline1, insertionMarkerBottom1, 325 insertionMarkerFlags1).build(), 326 new Builder().setMatrix(matrix1).setInsertionMarkerLocation( 327 insertionMarkerHorizontal1, insertionMarkerTop2, 328 insertionMarkerBaseline1, insertionMarkerBottom1, 329 insertionMarkerFlags1).build()); 330 assertNotEquals( 331 new Builder().setMatrix(matrix1).setInsertionMarkerLocation( 332 insertionMarkerHorizontal1, insertionMarkerTop1, 333 insertionMarkerBaseline1, insertionMarkerBottom1, 334 insertionMarkerFlags1).build(), 335 new Builder().setMatrix(matrix1).setInsertionMarkerLocation( 336 insertionMarkerHorizontal1, insertionMarkerTop1, 337 insertionMarkerBaseline2, insertionMarkerBottom1, 338 insertionMarkerFlags1).build()); 339 assertNotEquals( 340 new Builder().setMatrix(matrix1).setInsertionMarkerLocation( 341 insertionMarkerHorizontal1, insertionMarkerTop1, 342 insertionMarkerBaseline1, insertionMarkerBottom1, 343 insertionMarkerFlags1).build(), 344 new Builder().setMatrix(matrix1).setInsertionMarkerLocation( 345 insertionMarkerHorizontal2, insertionMarkerTop1, 346 insertionMarkerBaseline1, insertionMarkerBottom1, 347 insertionMarkerFlags1).build()); 348 assertNotEquals( 349 new Builder().setMatrix(matrix1).setInsertionMarkerLocation( 350 insertionMarkerHorizontal1, insertionMarkerTop1, 351 insertionMarkerBaseline1, insertionMarkerBottom1, 352 insertionMarkerFlags1).build(), 353 new Builder().setMatrix(matrix1).setInsertionMarkerLocation( 354 insertionMarkerHorizontal1, insertionMarkerTop1, 355 insertionMarkerBaseline1, insertionMarkerBottom2, 356 insertionMarkerFlags1).build()); 357 assertNotEquals( 358 new Builder().setMatrix(matrix1).setInsertionMarkerLocation( 359 insertionMarkerHorizontal1, insertionMarkerTop1, 360 insertionMarkerBaseline1, insertionMarkerBottom1, 361 insertionMarkerFlags1).build(), 362 new Builder().setMatrix(matrix1).setInsertionMarkerLocation( 363 insertionMarkerHorizontal1, insertionMarkerTop1, 364 insertionMarkerBaseline1, insertionMarkerBottom1, 365 insertionMarkerFlags2).build()); 366 } 367 368 @Test 369 public void testMatrixIsCopied() { 370 final Matrix matrix1 = new Matrix(); 371 matrix1.setTranslate(10.0f, 20.0f); 372 final Matrix matrix2 = new Matrix(); 373 matrix2.setTranslate(110.0f, 120.0f); 374 final Matrix matrix3 = new Matrix(); 375 matrix3.setTranslate(210.0f, 220.0f); 376 final Matrix matrix = new Matrix(); 377 final Builder builder = new Builder(); 378 379 matrix.set(matrix1); 380 builder.setMatrix(matrix); 381 matrix.postRotate(90.0f); 382 383 final CursorAnchorInfo firstInstance = builder.build(); 384 assertEquals(matrix1, firstInstance.getMatrix()); 385 matrix.set(matrix2); 386 builder.setMatrix(matrix); 387 final CursorAnchorInfo secondInstance = builder.build(); 388 assertEquals(matrix1, firstInstance.getMatrix()); 389 assertEquals(matrix2, secondInstance.getMatrix()); 390 391 matrix.set(matrix3); 392 assertEquals(matrix1, firstInstance.getMatrix()); 393 assertEquals(matrix2, secondInstance.getMatrix()); 394 } 395 396 @Test 397 public void testMatrixIsRequired() { 398 final int selectionStart = 30; 399 final int selectionEnd = 40; 400 final int composingTextStart = 32; 401 final String composingText = "test"; 402 final int insertionMarkerFlags = FLAG_HAS_VISIBLE_REGION; 403 final float insertionMarkerHorizontal = 10.5f; 404 final float insertionMarkerTop = 100.1f; 405 final float insertionMarkerBaseline = 110.4f; 406 final float insertionMarkerBottom = 111.0f; 407 Matrix transformMatrix = new Matrix(); 408 transformMatrix.setScale(10.0f, 20.0f); 409 410 final Builder builder = new Builder(); 411 // Check twice to make sure if Builder#reset() works as expected. 412 for (int repeatCount = 0; repeatCount < 2; ++repeatCount) { 413 builder.setSelectionRange(selectionStart, selectionEnd) 414 .setComposingText(composingTextStart, composingText); 415 try { 416 // Should succeed as coordinate transformation matrix is not required if no 417 // positional information is specified. 418 builder.build(); 419 } catch (IllegalArgumentException ex) { 420 fail(); 421 } 422 423 builder.setInsertionMarkerLocation(insertionMarkerHorizontal, insertionMarkerTop, 424 insertionMarkerBaseline, insertionMarkerBottom, insertionMarkerFlags); 425 try { 426 // Coordinate transformation matrix is required if no positional information is 427 // specified. 428 builder.build(); 429 fail(); 430 } catch (IllegalArgumentException ex) { 431 } 432 433 builder.setMatrix(transformMatrix); 434 try { 435 // Should succeed as coordinate transformation matrix is required. 436 builder.build(); 437 } catch (IllegalArgumentException ex) { 438 fail(); 439 } 440 441 builder.reset(); 442 } 443 } 444 445 @Test 446 public void testBuilderAddCharacterBounds() { 447 // A negative index should be rejected. 448 try { 449 new Builder().addCharacterBounds(-1, 0.0f, 0.0f, 0.0f, 0.0f, FLAG_HAS_VISIBLE_REGION); 450 fail(); 451 } catch (IllegalArgumentException ex) { 452 } 453 } 454 455 private static CursorAnchorInfo cloneViaParcel(CursorAnchorInfo src) { 456 Parcel parcel = null; 457 try { 458 parcel = Parcel.obtain(); 459 src.writeToParcel(parcel, 0); 460 parcel.setDataPosition(0); 461 return new CursorAnchorInfo(parcel); 462 } finally { 463 if (parcel != null) { 464 parcel.recycle(); 465 } 466 } 467 } 468 } 469