AdapterHelperTest.java revision a910619e83d0052e1d81aa5fe532821a2f99d76c
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 17package android.support.v7.widget; 18 19import junit.framework.AssertionFailedError; 20import junit.framework.TestResult; 21 22import android.test.AndroidTestCase; 23import android.util.Log; 24import android.widget.TextView; 25 26import java.util.ArrayList; 27import java.util.LinkedList; 28import java.util.List; 29import java.util.Queue; 30import java.util.Random; 31import java.util.concurrent.atomic.AtomicInteger; 32 33import static android.support.v7.widget.RecyclerView.*; 34 35public class AdapterHelperTest extends AndroidTestCase { 36 37 private static final boolean DEBUG = false; 38 39 private boolean mCollectLogs = false; 40 41 private static final String TAG = "AHT"; 42 43 List<ViewHolder> mViewHolders; 44 45 AdapterHelper mAdapterHelper; 46 47 List<AdapterHelper.UpdateOp> mFirstPassUpdates, mSecondPassUpdates; 48 49 TestAdapter mTestAdapter; 50 51 TestAdapter mPreProcessClone; // we clone adapter pre-process to run operations to see result 52 53 private List<TestAdapter.Item> mPreLayoutItems; 54 55 private StringBuilder mLog = new StringBuilder(); 56 57 @Override 58 protected void setUp() throws Exception { 59 cleanState(); 60 } 61 62 @Override 63 public void run(TestResult result) { 64 super.run(result); 65 if (!result.wasSuccessful()) { 66 result.addFailure(this, new AssertionFailedError(mLog.toString())); 67 } 68 } 69 70 private void cleanState() { 71 mLog.setLength(0); 72 mPreLayoutItems = new ArrayList<TestAdapter.Item>(); 73 mViewHolders = new ArrayList<ViewHolder>(); 74 mFirstPassUpdates = new ArrayList<AdapterHelper.UpdateOp>(); 75 mSecondPassUpdates = new ArrayList<AdapterHelper.UpdateOp>(); 76 mAdapterHelper = new AdapterHelper(new AdapterHelper.Callback() { 77 @Override 78 public RecyclerView.ViewHolder findViewHolder(int position) { 79 for (ViewHolder vh : mViewHolders) { 80 if (vh.mPosition == position && !vh.isRemoved()) { 81 return vh; 82 } 83 } 84 return null; 85 } 86 87 @Override 88 public void offsetPositionsForRemovingInvisible(int positionStart, int itemCount) { 89 final int positionEnd = positionStart + itemCount; 90 for (ViewHolder holder : mViewHolders) { 91 if (holder.mPosition >= positionEnd) { 92 holder.offsetPosition(-itemCount, true); 93 } else if (holder.mPosition >= positionStart) { 94 holder.addFlags(ViewHolder.FLAG_REMOVED); 95 holder.offsetPosition(-itemCount, true); 96 } 97 } 98 } 99 100 @Override 101 public void offsetPositionsForRemovingLaidOutOrNewView(int positionStart, 102 int itemCount) { 103 final int positionEnd = positionStart + itemCount; 104 for (ViewHolder holder : mViewHolders) { 105 if (holder.mPosition >= positionEnd) { 106 holder.offsetPosition(-itemCount, false); 107 } else if (holder.mPosition >= positionStart) { 108 holder.addFlags(ViewHolder.FLAG_REMOVED); 109 holder.offsetPosition(-itemCount, false); 110 } 111 } 112 } 113 114 @Override 115 public void markViewHoldersUpdated(int positionStart, int itemCount) { 116 final int positionEnd = positionStart + itemCount; 117 for (ViewHolder holder : mViewHolders) { 118 if (holder.mPosition >= positionStart && holder.mPosition < positionEnd) { 119 holder.addFlags(ViewHolder.FLAG_UPDATE); 120 } 121 } 122 } 123 124 @Override 125 public void onDispatchFirstPass(AdapterHelper.UpdateOp updateOp) { 126 if (DEBUG) { 127 log("first pass:" + updateOp.toString()); 128 } 129 for (ViewHolder viewHolder : mViewHolders) { 130 for (int i = 0; i < updateOp.itemCount; i ++) { 131 assertFalse("update op should not match any existing view holders", 132 viewHolder.getPosition() == updateOp.positionStart + i); 133 } 134 } 135 136 mFirstPassUpdates.add(updateOp); 137 } 138 139 @Override 140 public void onDispatchSecondPass(AdapterHelper.UpdateOp updateOp) { 141 if (DEBUG) { 142 log("second pass:" + updateOp.toString()); 143 } 144 mSecondPassUpdates.add(updateOp); 145 } 146 147 @Override 148 public void offsetPositionsForAdd(int positionStart, int itemCount) { 149 for (ViewHolder holder : mViewHolders) { 150 if (holder != null && holder.mPosition >= positionStart) { 151 holder.offsetPosition(itemCount, false); 152 } 153 } 154 } 155 156 @Override 157 public void offsetPositionsForMove(int from, int to) { 158 final int start, end, inBetweenOffset; 159 if (from < to) { 160 start = from; 161 end = to; 162 inBetweenOffset = -1; 163 } else { 164 start = to; 165 end = from; 166 inBetweenOffset = 1; 167 } 168 for (ViewHolder holder : mViewHolders) { 169 if (holder == null || holder.mPosition < start || holder.mPosition > end) { 170 continue; 171 } 172 holder.offsetPosition(inBetweenOffset, false); 173 } 174 } 175 }, true) { 176 @Override 177 void createFakeAddForRemovedMove(int adapterIndex, int pendingUpdateIndex) { 178 int addsBefore = 0; 179 for (int i = 0; i < pendingUpdateIndex; i ++) { 180 final UpdateOp updateOp = mPendingUpdates.get(i); 181 if (updateOp.cmd == UpdateOp.ADD) { 182 addsBefore += updateOp.itemCount; 183 } 184 } 185 mTestAdapter.createFakeItemAt(addsBefore); 186 super.createFakeAddForRemovedMove(adapterIndex, pendingUpdateIndex); 187 } 188 }; 189 } 190 191 void log(String msg) { 192 if (mCollectLogs) { 193 mLog.append(msg).append("\n"); 194 } else { 195 Log.d(TAG, msg); 196 } 197 } 198 199 void setupBasic(int count, int visibleStart, int visibleCount) { 200 if (DEBUG) { 201 log("setupBasic(" + count + "," + visibleStart + "," + visibleCount + ");"); 202 } 203 mTestAdapter = new TestAdapter(count, mAdapterHelper); 204 for (int i = 0; i < visibleCount; i++) { 205 addViewHolder(visibleStart + i); 206 } 207 mPreProcessClone = mTestAdapter.createCopy(); 208 } 209 210 private void addViewHolder(int posiiton) { 211 ViewHolder viewHolder = new RecyclerViewBasicTest.MockViewHolder( 212 new TextView(getContext())); 213 viewHolder.mPosition = posiiton; 214 mViewHolders.add(viewHolder); 215 } 216 217 public void testFindPositionOffsetInPreLayout() { 218 setupBasic(50, 25, 10); 219 rm(24, 5); 220 mAdapterHelper.preProcess(); 221 // since 25 is invisible, we offset by one while checking 222 assertEquals("find position for view 23", 223 23, mAdapterHelper.findPositionOffset(23)); 224 assertEquals("find position for view 24", 225 -1, mAdapterHelper.findPositionOffset(24)); 226 assertEquals("find position for view 25", 227 -1, mAdapterHelper.findPositionOffset(25)); 228 assertEquals("find position for view 26", 229 -1, mAdapterHelper.findPositionOffset(26)); 230 assertEquals("find position for view 27", 231 -1, mAdapterHelper.findPositionOffset(27)); 232 assertEquals("find position for view 28", 233 24, mAdapterHelper.findPositionOffset(28)); 234 assertEquals("find position for view 29", 235 25, mAdapterHelper.findPositionOffset(29)); 236 } 237 238 public void testSinglePass() { 239 setupBasic(10, 2, 3); 240 add(2, 1); 241 rm(1, 2); 242 add(1, 5); 243 mAdapterHelper.consumeUpdatesInOnePass(); 244 assertDispatch(0, 3); 245 } 246 247 public void testDeleteVisible() { 248 setupBasic(10, 2, 3); 249 rm(2, 1); 250 preProcess(); 251 assertDispatch(0, 1); 252 } 253 254 public void testDeleteInvisible() { 255 setupBasic(10, 3, 4); 256 rm(2, 1); 257 preProcess(); 258 assertDispatch(1, 0); 259 } 260 261 public void testAddCount() { 262 setupBasic(0, 0, 0); 263 add(0, 1); 264 assertEquals(1, mAdapterHelper.mPendingUpdates.size()); 265 } 266 267 public void testDeleteCount() { 268 setupBasic(1, 0, 0); 269 rm(0, 1); 270 assertEquals(1, mAdapterHelper.mPendingUpdates.size()); 271 } 272 273 public void testAddProcess() { 274 setupBasic(0, 0, 0); 275 add(0, 1); 276 preProcess(); 277 assertEquals(0, mAdapterHelper.mPendingUpdates.size()); 278 } 279 280 public void testAddRemoveSeparate() { 281 setupBasic(10, 2, 2); 282 add(6, 1); 283 rm(5, 1); 284 preProcess(); 285 assertDispatch(1, 1); 286 } 287 288 public void testScenario1() { 289 setupBasic(10, 3, 2); 290 rm(4, 1); 291 rm(3, 1); 292 rm(3, 1); 293 preProcess(); 294 assertDispatch(1, 2); 295 } 296 297 public void testDivideDelete() { 298 setupBasic(10, 3, 4); 299 rm(2, 2); 300 preProcess(); 301 assertDispatch(1, 1); 302 } 303 304 public void testScenario2() { 305 setupBasic(10, 3, 3); // 3-4-5 306 add(4, 2); // 3 a b 4 5 307 rm(0, 1); // (0) 3(2) a(3) b(4) 4(3) 5(4) 308 rm(1, 3); // (1,2) (x) a(1) b(2) 4(3) 309 preProcess(); 310 assertDispatch(2, 2); 311 } 312 313 public void testScenario3() { 314 setupBasic(10, 2, 2); 315 rm(0, 5); 316 preProcess(); 317 assertDispatch(2, 1); 318 assertOps(mFirstPassUpdates, rmOp(0, 2), rmOp(2, 1)); 319 assertOps(mSecondPassUpdates, rmOp(0, 2)); 320 } 321 // TODO test MOVE then remove items in between. 322 // TODO test MOVE then remove it, make sure it is not dispatched 323 324 public void testScenario4() { 325 setupBasic(5, 0, 5); 326 // 0 1 2 3 4 327 // 0 1 2 a b 3 4 328 // 0 2 a b 3 4 329 // 0 c d 2 a b 3 4 330 // 0 c d 2 a 4 331 // c d 2 a 4 332 // pre: 0 1 2 3 4 333 add(3, 2); 334 rm(1, 1); 335 add(1, 2); 336 rm(5, 2); 337 rm(0, 1); 338 preProcess(); 339 } 340 341 public void testScenario5() { 342 setupBasic(5, 0, 5); 343 // 0 1 2 3 4 344 // 0 1 2 a b 3 4 345 // 0 1 b 3 4 346 // pre: 0 1 2 3 4 347 // pre w/ adap: 0 1 2 b 3 4 348 add(3, 2); 349 rm(2, 2); 350 preProcess(); 351 } 352 353 public void testScenario6() { 354// setupBasic(47, 19, 24); 355// mv(11, 12); 356// add(24, 16); 357// rm(9, 3); 358 setupBasic(10, 5, 3); 359 mv(2, 3); 360 add(6, 4); 361 rm(4, 1); 362 preProcess(); 363 } 364 365 public void testScenario8() { 366 setupBasic(68, 51, 13); 367 mv(22, 11); 368 mv(22, 52); 369 rm(37, 19); 370 add(12, 38); 371 preProcess(); 372 } 373 374 public void testScenario9() { 375 setupBasic(44, 3, 7); 376 add(7, 21); 377 rm(31, 3); 378 rm(32, 11); 379 mv(29, 5); 380 mv(30, 32); 381 add(25, 32); 382 rm(15, 66); 383 preProcess(); 384 } 385 386 public void testScenario10() { 387 setupBasic(14, 10, 3); 388 rm(4, 4); 389 add(5, 11); 390 mv(5, 18); 391 rm(2, 9); 392 preProcess(); 393 } 394 395 public void testScenario11() { 396 setupBasic(78, 3, 64); 397 mv(34, 28); 398 add(1, 11); 399 rm(9, 74); 400 preProcess(); 401 } 402 403 public void testScenario12() { 404 setupBasic(38, 9, 7); 405 rm(26, 3); 406 mv(29, 15); 407 rm(30, 1); 408 preProcess(); 409 } 410 411 public void testScenario13() { 412 setupBasic(49, 41, 3); 413 rm(30, 13); 414 add(4, 10); 415 mv(3, 38); 416 mv(20, 17); 417 rm(18, 23); 418 preProcess(); 419 } 420 421 public void testScenario14() { 422 setupBasic(24, 3, 11); 423 rm(2, 15); 424 mv(2, 1); 425 add(2, 34); 426 add(11, 3); 427 rm(10, 25); 428 rm(13, 6); 429 rm(4, 4); 430 rm(6, 4); 431 preProcess(); 432 } 433 434 public void testScenario15() { 435 setupBasic(10, 8, 1); 436 mv(6, 1); 437 mv(1, 4); 438 rm(3, 1); 439 preProcess(); 440 } 441 442 public void testScenario16() { 443 setupBasic(10, 3, 3); 444 rm(2, 1); 445 rm(1, 7); 446 rm(0, 1); 447 preProcess(); 448 } 449 450 public void testScenario17() { 451 setupBasic(10, 8, 1); 452 mv(1, 0); 453 mv(5, 1); 454 rm(1, 7); 455 preProcess(); 456 } 457 458 public void testScenario18() throws InterruptedException { 459 setupBasic(10, 1, 4); 460 add(2, 11); 461 rm(16, 1); 462 add(3, 1); 463 rm(9, 10); 464 preProcess(); 465 } 466 467 public void testScenario19() { 468 setupBasic(10,8,1); 469 mv(9,7); 470 mv(9,3); 471 rm(5,4); 472 preProcess(); 473 } 474 475 public void testScenario20() { 476 setupBasic(10,7,1); 477 mv(9,1); 478 mv(3,9); 479 rm(7,2); 480 preProcess(); 481 } 482 483 public void testScenario21() { 484 setupBasic(10,5,2); 485 mv(1,0); 486 mv(9,1); 487 rm(2,3); 488 preProcess(); 489 } 490 491 public void testScenario22() { 492 setupBasic(10,7,2); 493 add(2,16); 494 mv(20,9); 495 rm(17,6); 496 preProcess(); 497 } 498 499 public void testScenario23() { 500 setupBasic(10,5,3); 501 mv(9,6); 502 add(4,15); 503 rm(21,3); 504 preProcess(); 505 } 506 507 public void testScenario24() { 508 setupBasic(10,1,6); 509 add(6,5); 510 mv(14,6); 511 rm(7,6); 512 preProcess(); 513 } 514 515 public void testScenario25() { 516 setupBasic(10,3,4); 517 mv(3,9); 518 mv(2,9); 519 rm(5,4); 520 preProcess(); 521 } 522 523 public void testScenario26() { 524 setupBasic(10,4,4); 525 rm(3,5); 526 mv(2,0); 527 mv(1,0); 528 rm(1,1); 529 mv(0,2); 530 preProcess(); 531 } 532 533 public void testScenario27() { 534 setupBasic(10,0,3); 535 mv(9,4); 536 mv(8,4); 537 add(7,6); 538 rm(5,5); 539 preProcess(); 540 } 541 542 public void testScenerio28() { 543 setupBasic(10,4,1); 544 mv(8,6); 545 rm(8,1); 546 mv(7,5); 547 rm(3,3); 548 rm(1,4); 549 preProcess(); 550 } 551 552 public void testMoveAdded() { 553 setupBasic(10, 2, 2); 554 add(3, 5); 555 mv(4, 2); 556 preProcess(); 557 } 558 559 public void testRandom() throws Throwable { 560 mCollectLogs = true; 561 Random random = new Random(System.nanoTime()); 562 for (int i = 0; i < 1000; i++) { 563 try { 564 randomTest(random, i + 10); 565 } catch (Throwable t) { 566 throw new Throwable(t.getMessage() + "\n" + mLog.toString(), t); 567 } 568 } 569 } 570 571 public void randomTest(Random random, int opCount) { 572 cleanState(); 573 if (DEBUG) { 574 log("randomTest"); 575 } 576 final int count = 10;// + random.nextInt(100); 577 final int start = random.nextInt(count - 1); 578 final int layoutCount = Math.max(1, random.nextInt(count - start)); 579 setupBasic(count, start, layoutCount); 580 581 while (opCount-- > 0) { 582 final int op = random.nextInt(3); 583 switch (op) { 584 case 0: 585 if (mTestAdapter.mItems.size() > 1) { 586 int s = random.nextInt(mTestAdapter.mItems.size() - 1); 587 int len = Math.max(1, random.nextInt(mTestAdapter.mItems.size() - s)); 588 rm(s, len); 589 } 590 break; 591 case 1: 592 int s = mTestAdapter.mItems.size() == 0 ? 0 : 593 random.nextInt(mTestAdapter.mItems.size()); 594 add(s, random.nextInt(50)); 595 break; 596 case 2: 597 if (mTestAdapter.mItems.size() >= 2) { 598 int from = random.nextInt(mTestAdapter.mItems.size()); 599 int to; 600 do { 601 to = random.nextInt(mTestAdapter.mItems.size()); 602 } while (to == from); 603 mv(from, to); 604 } 605 } 606 } 607 preProcess(); 608 } 609 610 public void assertOps(List<AdapterHelper.UpdateOp> actual, 611 AdapterHelper.UpdateOp... expected) { 612 assertEquals(expected.length, actual.size()); 613 for (int i = 0; i < expected.length; i++) { 614 assertEquals(expected[i], actual.get(i)); 615 } 616 } 617 618 void assertDispatch(int firstPass, int secondPass) { 619 assertEquals(firstPass, mFirstPassUpdates.size()); 620 assertEquals(secondPass, mSecondPassUpdates.size()); 621 } 622 623 void preProcess() { 624 mAdapterHelper.preProcess(); 625 for (int i = 0; i < mPreProcessClone.mItems.size(); i++) { 626 TestAdapter.Item item = mPreProcessClone.mItems.get(i); 627 final int preLayoutIndex = mPreLayoutItems.indexOf(item); 628 final int endIndex = mTestAdapter.mItems.indexOf(item); 629 if (preLayoutIndex != -1) { 630 assertEquals("find position offset should work properly for existing elements" + i 631 + " at pre layout position " + preLayoutIndex + " and post layout position " 632 + endIndex, endIndex, mAdapterHelper.findPositionOffset(preLayoutIndex)); 633 } 634 } 635 mAdapterHelper.consumePostponedUpdates(); 636 // now assert these two adapters have identical data. 637 mPreProcessClone.applyOps(mFirstPassUpdates, mTestAdapter); 638 mPreProcessClone.applyOps(mSecondPassUpdates, mTestAdapter); 639 assertAdaptersEqual(mTestAdapter, mPreProcessClone); 640 } 641 642 private void assertAdaptersEqual(TestAdapter a1, TestAdapter a2) { 643 assertEquals(a1.mItems.size(), a2.mItems.size()); 644 for (int i = 0; i < a1.mItems.size(); i++) { 645 TestAdapter.Item item = a1.mItems.get(i); 646 assertSame(item, a2.mItems.get(i)); 647 assertEquals(0, item.getUpdateCount()); 648 } 649 assertEquals(0, a1.mPendingAdded.size()); 650 assertEquals(0, a2.mPendingAdded.size()); 651 } 652 653 AdapterHelper.UpdateOp op(int cmd, int start, int count) { 654 return new AdapterHelper.UpdateOp(cmd, start, count); 655 } 656 657 AdapterHelper.UpdateOp addOp(int start, int count) { 658 return op(AdapterHelper.UpdateOp.ADD, start, count); 659 } 660 661 AdapterHelper.UpdateOp rmOp(int start, int count) { 662 return op(AdapterHelper.UpdateOp.REMOVE, start, count); 663 } 664 665 AdapterHelper.UpdateOp upOp(int start, int count) { 666 return op(AdapterHelper.UpdateOp.UPDATE, start, count); 667 } 668 669 void add(int start, int count) { 670 if (DEBUG) { 671 log("add(" + start + "," + count + ");"); 672 } 673 mTestAdapter.add(start, count); 674 } 675 676 boolean isItemLaidOut(int pos) { 677 for (ViewHolder viewHolder : mViewHolders) { 678 if (viewHolder.mOldPosition == pos) { 679 return true; 680 } 681 } 682 return false; 683 } 684 685 private void mv(int from, int to) { 686 if (DEBUG) { 687 log("mv(" + from + "," + to + ");"); 688 } 689 mTestAdapter.move(from, to); 690 } 691 692 void rm(int start, int count) { 693 if (DEBUG) { 694 log("rm(" + start + "," + count + ");"); 695 } 696 for (int i = start; i < start + count; i++) { 697 if (!isItemLaidOut(i)) { 698 TestAdapter.Item item = mTestAdapter.mItems.get(i); 699 mPreLayoutItems.remove(item); 700 } 701 } 702 mTestAdapter.remove(start, count); 703 } 704 705 void up(int start, int count) { 706 mTestAdapter.update(start, count); 707 } 708 709 static class TestAdapter { 710 711 List<Item> mItems; 712 713 final AdapterHelper mAdapterHelper; 714 715 Queue<Item> mPendingAdded; 716 717 public TestAdapter(int initialCount, AdapterHelper container) { 718 mItems = new ArrayList<Item>(); 719 mAdapterHelper = container; 720 mPendingAdded = new LinkedList<Item>(); 721 for (int i = 0; i < initialCount; i++) { 722 mItems.add(new Item()); 723 } 724 } 725 726 public void add(int index, int count) { 727 for (int i = 0; i < count; i++) { 728 Item item = new Item(); 729 mPendingAdded.add(item); 730 mItems.add(index + i, item); 731 } 732 mAdapterHelper.addUpdateOp(new AdapterHelper.UpdateOp( 733 AdapterHelper.UpdateOp.ADD, index, count 734 )); 735 } 736 737 public void move(int from, int to) { 738 mItems.add(to, mItems.remove(from)); 739 mAdapterHelper.addUpdateOp(new AdapterHelper.UpdateOp( 740 AdapterHelper.UpdateOp.MOVE, from, to 741 )); 742 } 743 public void remove(int index, int count) { 744 for (int i = 0; i < count; i++) { 745 mItems.remove(index); 746 } 747 mAdapterHelper.addUpdateOp(new AdapterHelper.UpdateOp( 748 AdapterHelper.UpdateOp.REMOVE, index, count 749 )); 750 } 751 752 public void update(int index, int count) { 753 for (int i = 0; i < count; i++) { 754 mItems.get(index + i).update(); 755 } 756 mAdapterHelper.addUpdateOp(new AdapterHelper.UpdateOp( 757 AdapterHelper.UpdateOp.UPDATE, index, count 758 )); 759 } 760 761 protected TestAdapter createCopy() { 762 TestAdapter adapter = new TestAdapter(0, mAdapterHelper); 763 for (Item item : mItems) { 764 adapter.mItems.add(item); 765 } 766 return adapter; 767 } 768 769 public void applyOps(List<AdapterHelper.UpdateOp> updates, 770 TestAdapter dataSource) { 771 for (AdapterHelper.UpdateOp op : updates) { 772 switch (op.cmd) { 773 case AdapterHelper.UpdateOp.ADD: 774 for (int i = 0; i < op.itemCount; i++) { 775 mItems.add(op.positionStart + i, dataSource.consumeNextAdded()); 776 } 777 break; 778 case AdapterHelper.UpdateOp.REMOVE: 779 for (int i = 0; i < op.itemCount; i++) { 780 mItems.remove(op.positionStart); 781 } 782 break; 783 case AdapterHelper.UpdateOp.UPDATE: 784 for (int i = 0; i < op.itemCount; i++) { 785 mItems.get(i).handleUpdate(); 786 } 787 break; 788 case AdapterHelper.UpdateOp.MOVE: 789 mItems.add(op.itemCount, mItems.remove(op.positionStart)); 790 break; 791 } 792 } 793 } 794 795 private Item consumeNextAdded() { 796 return mPendingAdded.remove(); 797 } 798 799 public void createFakeItemAt(int fakeAddedItemIndex) { 800 Item fakeItem = new Item(); 801 ((LinkedList<Item>)mPendingAdded).add(fakeAddedItemIndex, fakeItem); 802 } 803 804 public static class Item { 805 806 private static AtomicInteger itemCounter = new AtomicInteger(); 807 808 private final int id; 809 810 private int mVersionCount = 0; 811 812 private int mUpdateCount; 813 814 public Item() { 815 id = itemCounter.incrementAndGet(); 816 } 817 818 public void update() { 819 mVersionCount++; 820 } 821 822 public void handleUpdate() { 823 mVersionCount--; 824 } 825 826 public int getUpdateCount() { 827 return mUpdateCount; 828 } 829 } 830 } 831} 832