Merge "GridLayoutManager: Fix WindowAlignment algorithm"
diff --git a/v17/leanback/res/layout/lb_vertical_grid.xml b/v17/leanback/res/layout/lb_vertical_grid.xml
index 5dfe0c9..7154e48 100644
--- a/v17/leanback/res/layout/lb_vertical_grid.xml
+++ b/v17/leanback/res/layout/lb_vertical_grid.xml
@@ -18,6 +18,7 @@
<android.support.v17.leanback.widget.VerticalGridView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/browse_grid"
- android:layout_width="match_parent"
+ android:layout_width="wrap_content"
android:layout_height="match_parent"
+ android:layout_gravity="center"
style="?attr/itemsVerticalGridStyle" />
diff --git a/v17/leanback/res/values/dimens.xml b/v17/leanback/res/values/dimens.xml
index bf5e179..3330e11 100644
--- a/v17/leanback/res/values/dimens.xml
+++ b/v17/leanback/res/values/dimens.xml
@@ -24,6 +24,7 @@
<dimen name="lb_browse_rows_margin_start">238dp</dimen>
<dimen name="lb_browse_rows_margin_top">167dp</dimen>
<dimen name="lb_browse_rows_fading_edge">16dp</dimen>
+ <dimen name="lb_vertical_grid_padding_bottom">87dp</dimen>
<dimen name="lb_browse_title_height">60dp</dimen>
<dimen name="lb_browse_title_icon_height">52dp</dimen>
diff --git a/v17/leanback/res/values/styles.xml b/v17/leanback/res/values/styles.xml
index a3a0559..8e940b5 100644
--- a/v17/leanback/res/values/styles.xml
+++ b/v17/leanback/res/values/styles.xml
@@ -152,11 +152,12 @@
<item name="android:focusableInTouchMode">true</item>
<item name="android:paddingLeft">?attr/browsePaddingLeft</item>
<item name="android:paddingRight">?attr/browsePaddingRight</item>
- <item name="android:paddingBottom">@dimen/lb_browse_item_vertical_margin</item>
+ <item name="android:paddingBottom">@dimen/lb_vertical_grid_padding_bottom</item>
<item name="android:paddingTop">?attr/browseRowsMarginTop</item>
<item name="android:gravity">center_horizontal</item>
<item name="horizontalMargin">@dimen/lb_browse_item_horizontal_margin</item>
<item name="verticalMargin">@dimen/lb_browse_item_vertical_margin</item>
+ <item name="columnWidth">wrap_content</item>
<item name="focusOutFront">true</item>
</style>
diff --git a/v4/java/android/support/v4/app/NotificationCompat.java b/v4/java/android/support/v4/app/NotificationCompat.java
index caec297..58acacf 100644
--- a/v4/java/android/support/v4/app/NotificationCompat.java
+++ b/v4/java/android/support/v4/app/NotificationCompat.java
@@ -1334,7 +1334,7 @@
* <br>
* This class is a "rebuilder": It attaches to a Builder object and modifies its behavior, like so:
* <pre class="prettyprint">
- * Notification noti = new Notification.Builder()
+ * Notification notif = new Notification.Builder(mContext)
* .setContentTitle("New photo from " + sender.toString())
* .setContentText(subject)
* .setSmallIcon(R.drawable.new_post)
@@ -1403,7 +1403,7 @@
* <br>
* This class is a "rebuilder": It attaches to a Builder object and modifies its behavior, like so:
* <pre class="prettyprint">
- * Notification noti = new Notification.Builder()
+ * Notification notif = new Notification.Builder(mContext)
* .setContentTitle("New mail from " + sender.toString())
* .setContentText(subject)
* .setSmallIcon(R.drawable.new_mail)
diff --git a/v4/java/android/support/v4/app/NotificationCompatSideChannelService.java b/v4/java/android/support/v4/app/NotificationCompatSideChannelService.java
index 2917e11..b0127de 100644
--- a/v4/java/android/support/v4/app/NotificationCompatSideChannelService.java
+++ b/v4/java/android/support/v4/app/NotificationCompatSideChannelService.java
@@ -19,6 +19,7 @@
import android.app.Notification;
import android.app.Service;
import android.content.Intent;
+import android.os.Build;
import android.os.IBinder;
import android.os.RemoteException;
@@ -41,9 +42,17 @@
*
*/
public abstract class NotificationCompatSideChannelService extends Service {
+ // In support lib, we cannot reference version codes >= 4 from android.os.Build.
+ private static final int BUILD_VERSION_CODE_KITKAT_WATCH = 20;
+
@Override
public IBinder onBind(Intent intent) {
if (intent.getAction().equals(NotificationManagerCompat.ACTION_BIND_SIDE_CHANNEL)) {
+ // Group support is the only current reason to use side channel,
+ // so disallow clients to bind for side channel on devices past KITKAT_WATCH for now.
+ if (Build.VERSION.SDK_INT >= BUILD_VERSION_CODE_KITKAT_WATCH) {
+ return null;
+ }
return new NotificationSideChannelStub();
}
return null;
diff --git a/v4/java/android/support/v4/widget/ExploreByTouchHelper.java b/v4/java/android/support/v4/widget/ExploreByTouchHelper.java
index b894399..7adbc6f 100644
--- a/v4/java/android/support/v4/widget/ExploreByTouchHelper.java
+++ b/v4/java/android/support/v4/widget/ExploreByTouchHelper.java
@@ -577,7 +577,8 @@
* @param x The view-relative x coordinate
* @param y The view-relative y coordinate
* @return virtual view identifier for the logical item under
- * coordinates (x,y)
+ * coordinates (x,y) or {@link View#NO_ID} if there is no item at
+ * the given coordinates
*/
protected abstract int getVirtualViewAt(float x, float y);
diff --git a/v7/palette/src/android/support/v7/graphics/Palette.java b/v7/palette/src/android/support/v7/graphics/Palette.java
index 9e57760..cff1f0b 100644
--- a/v7/palette/src/android/support/v7/graphics/Palette.java
+++ b/v7/palette/src/android/support/v7/graphics/Palette.java
@@ -47,7 +47,6 @@
*
* <pre>
* Palette.generateAsync(bitmap, new Palette.PaletteAsyncListener() {
- * @Override
* public void onGenerated(Palette palette) {
* // Do something with colors...
* }
diff --git a/v7/recyclerview/src/android/support/v7/widget/RecyclerView.java b/v7/recyclerview/src/android/support/v7/widget/RecyclerView.java
index eda0583..f7946a3 100644
--- a/v7/recyclerview/src/android/support/v7/widget/RecyclerView.java
+++ b/v7/recyclerview/src/android/support/v7/widget/RecyclerView.java
@@ -110,9 +110,18 @@
if (!mAdapterHelper.hasPendingUpdates()) {
return;
}
- eatRequestLayout();
- mAdapterHelper.preProcess();
- resumeRequestLayout(true);
+ if (mDataSetHasChangedAfterLayout) {
+ dispatchLayout();
+ } else {
+ eatRequestLayout();
+ mAdapterHelper.preProcess();
+ if (!mEatRequestLayout) {
+ // We run this after pre-processing is complete so that ViewHolders have their
+ // final adapter positions. No need to run it if a layout is already requested.
+ rebindInvalidViewHolders();
+ }
+ resumeRequestLayout(true);
+ }
}
};
@@ -132,6 +141,12 @@
private boolean mAdapterUpdateDuringMeasure;
private final boolean mPostUpdatesOnAnimation;
+ /**
+ * Set to true when an adapter data set changed notification is received.
+ * In that case, we cannot run any animations since we don't know what happened.
+ */
+ private boolean mDataSetHasChangedAfterLayout = false;
+
private EdgeEffectCompat mLeftGlow, mTopGlow, mRightGlow, mBottomGlow;
ItemAnimator mItemAnimator = new DefaultItemAnimator();
@@ -1453,17 +1468,26 @@
}
eatRequestLayout();
- saveOldPositions();
// simple animations are a subset of advanced animations (which will cause a
// prelayout step)
boolean animateChangesSimple = mItemAnimator != null && mItemsAddedOrRemoved
- && !mItemsChanged;
+ && !mItemsChanged && !mDataSetHasChangedAfterLayout;
final boolean animateChangesAdvanced = animateChangesSimple &&
predictiveItemAnimationsEnabled();
mItemsAddedOrRemoved = mItemsChanged = false;
ArrayMap<View, Rect> appearingViewInitialBounds = null;
mState.mInPreLayout = animateChangesAdvanced;
mState.mItemCount = mAdapter.getItemCount();
+
+ if (mDataSetHasChangedAfterLayout) {
+ // Processing these items have no value since data set changed unexpectedly.
+ // Instead, we just reset it.
+ // TODO consider handling updates that arrived before notifyDataSetChanged is called.
+ mAdapterHelper.reset();
+ markKnownViewsInvalid();
+ mLayout.onItemsChanged(this);
+ }
+
if (animateChangesSimple) {
// Step 0: Find out where all non-removed items are, pre-layout
mState.mPreLayoutHolderMap.clear();
@@ -1482,7 +1506,9 @@
// items back to the container). This gives the pre-layout position of APPEARING views
// which come into existence as part of the real layout.
- // make sure any pending data updates are flushed before laying out
+ // Save old positions so that LayoutManager can run its mapping logic.
+ saveOldPositions();
+ // Make sure any pending data updates are flushed before laying out.
mAdapterHelper.preProcess();
mInPreLayout = true;
final boolean didStructureChange = mState.mStructureChanged;
@@ -1508,14 +1534,14 @@
child.getRight(), child.getBottom()));
}
}
- }
- clearOldPositions();
- if (animateChangesAdvanced) {
+ clearOldPositions();
mAdapterHelper.consumePostponedUpdates();
} else {
+ clearOldPositions();
mAdapterHelper.consumeUpdatesInOnePass();
}
mState.mItemCount = mAdapter.getItemCount();
+ mState.mDeletedInvisibleItemCountSincePreviousLayout = 0;
// Step 2: Run layout
mState.mInPreLayout = false;
@@ -1592,7 +1618,7 @@
resumeRequestLayout(false);
mLayout.removeAndRecycleScrapInt(mRecycler, !animateChangesAdvanced);
mState.mPreviousLayoutItemCount = mState.mItemCount;
- mState.mDeletedInvisibleItemCountSincePreviousLayout = 0;
+ mDataSetHasChangedAfterLayout = false;
}
private void animateAppearance(ViewHolder itemHolder, Rect beforeBounds, int afterLeft,
@@ -1842,14 +1868,33 @@
}
if (holder.mPosition >= positionStart && holder.mPosition < positionEnd) {
+ // We re-bind these view holders after pre-processing is complete so that
+ // ViewHolders have their final positions assigned.
holder.addFlags(ViewHolder.FLAG_UPDATE);
- // Binding an attached view will request a layout if needed.
- mAdapter.bindViewHolder(holder, holder.getPosition());
}
}
mRecycler.viewRangeUpdate(positionStart, itemCount);
}
+ void rebindInvalidViewHolders() {
+ final int childCount = getChildCount();
+ for (int i = 0; i < getChildCount(); i++) {
+ final ViewHolder holder = getChildViewHolderInt(getChildAt(i));
+ // validate type is correct
+ if (holder != null && !holder.isRemoved() && holder.isInvalid()) {
+ final int type = mAdapter.getItemViewType(holder.mPosition);
+ if (holder.getItemViewType() == type) {
+ // Binding an attached view will request a layout if needed.
+ mAdapter.bindViewHolder(holder, holder.mPosition);
+ } else {
+ // binding to a new view will need re-layout anyways. We can as well trigger
+ // it here so that it happens during layout
+ requestLayout();
+ }
+ }
+ }
+ }
+
/**
* Mark all known views as invalid. Used in response to a, "the whole world might have changed"
* data change event.
@@ -2246,13 +2291,16 @@
@Override
public void onChanged() {
if (mAdapter.hasStableIds()) {
- // TODO Determine what actually changed
- markKnownViewsInvalid();
+ // TODO Determine what actually changed.
+ // This is more important to implement now since this callback will disable all
+ // animations because we cannot rely on positions.
mState.mStructureChanged = true;
- requestLayout();
+ mDataSetHasChangedAfterLayout = true;
} else {
- markKnownViewsInvalid();
mState.mStructureChanged = true;
+ mDataSetHasChangedAfterLayout = true;
+ }
+ if (!mAdapterHelper.hasPendingUpdates()) {
requestLayout();
}
}
@@ -3666,7 +3714,12 @@
final int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
- if (getPosition(child) == position) {
+ ViewHolder vh = getChildViewHolderInt(child);
+ if (vh == null) {
+ continue;
+ }
+ if (vh.getPosition() == position &&
+ (mRecyclerView.mState.isPreLayout() || !vh.isRemoved())) {
return child;
}
}
@@ -4391,6 +4444,15 @@
}
/**
+ * Called when {@link Adapter#notifyDataSetChanged()} is triggered instead of giving
+ * detailed information on what has actually changed.
+ *
+ * @param recyclerView
+ */
+ public void onItemsChanged(RecyclerView recyclerView) {
+ }
+
+ /**
* Called when items have been added to the adapter. The LayoutManager may choose to
* requestLayout if the inserted items would require refreshing the currently visible set
* of child views. (e.g. currently empty space would be filled by appended items, etc.)
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/BaseRecyclerViewInstrumentationTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/BaseRecyclerViewInstrumentationTest.java
index 65d07f0..d2d8f8b 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/BaseRecyclerViewInstrumentationTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/BaseRecyclerViewInstrumentationTest.java
@@ -40,6 +40,8 @@
protected AdapterHelper mAdapterHelper;
+ Throwable mainThreadException;
+
public BaseRecyclerViewInstrumentationTest() {
this(false);
}
@@ -49,6 +51,27 @@
mDebug = debug;
}
+ void checkForMainThreadException() throws Throwable {
+ if (mainThreadException != null) {
+ throw mainThreadException;
+ }
+ }
+
+ void postExceptionToInstrumentation(Throwable t) {
+ if (mDebug) {
+ Log.e(TAG, "captured exception on main thread", t);
+ }
+ mainThreadException = t;
+ if (mRecyclerView != null && mRecyclerView
+ .getLayoutManager() instanceof TestLayoutManager) {
+ TestLayoutManager lm = (TestLayoutManager) mRecyclerView.getLayoutManager();
+ // finish all layouts so that we get the correct exception
+ while (lm.layoutLatch.getCount() > 0) {
+ lm.layoutLatch.countDown();
+ }
+ }
+ }
+
@Override
protected void tearDown() throws Exception {
if (mRecyclerView != null) {
@@ -208,7 +231,8 @@
if (mDebug) {
Log.d(TAG, "will layout items from " + start + " to " + end);
}
- for (int i = start; i < end; i++) {
+ int diff = end > start ? 1 : -1;
+ for (int i = start; i != end; i+=diff) {
if (mDebug) {
Log.d(TAG, "laying out item " + i);
}
@@ -222,7 +246,7 @@
}
measureChildWithMargins(view, 0, 0);
- layoutDecorated(view, 0, (i - start) * 10, getDecoratedMeasuredWidth(view)
+ layoutDecorated(view, 0, Math.abs(i - start) * 10, getDecoratedMeasuredWidth(view)
, getDecoratedMeasuredHeight(view));
}
return skippedAdd;
@@ -313,6 +337,15 @@
});
}
+ public void notifyItemChange(final int start, final int count) throws Throwable {
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ notifyItemRangeChanged(start, count);
+ }
+ });
+ }
+
/**
* Similar to other methods but negative count means delete and position count means add.
* <p>
@@ -383,4 +416,4 @@
super.runTestOnUiThread(r);
}
}
-}
\ No newline at end of file
+}
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewAnimationsTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewAnimationsTest.java
index 662371d..96c9202 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewAnimationsTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewAnimationsTest.java
@@ -38,8 +38,6 @@
private static final String TAG = "RecyclerViewAnimationsTest";
- Throwable mainThreadException;
-
AnimationLayoutManager mLayoutManager;
TestAdapter mTestAdapter;
@@ -53,12 +51,6 @@
super.setUp();
}
- void checkForMainThreadException() throws Throwable {
- if (mainThreadException != null) {
- throw mainThreadException;
- }
- }
-
RecyclerView setupBasic(int itemCount) throws Throwable {
return setupBasic(itemCount, 0, itemCount);
}
@@ -91,12 +83,38 @@
recyclerView.waitForDraw(1);
mLayoutManager.mOnLayoutCallbacks.reset();
getInstrumentation().waitForIdleSync();
- assertEquals("extra layouts should not happend", 1, mLayoutManager.getTotalLayoutCount());
+ assertEquals("extra layouts should not happen", 1, mLayoutManager.getTotalLayoutCount());
assertEquals("all expected children should be laid out", firstLayoutItemCount,
mLayoutManager.getChildCount());
return recyclerView;
}
+ public void testNotifyDataSetChanged() throws Throwable {
+ setupBasic(10, 3, 4);
+ int layoutCount = mLayoutManager.mTotalLayoutCount;
+ mLayoutManager.expectLayouts(1);
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ mTestAdapter.deleteAndNotify(4, 1);
+ mTestAdapter.notifyChange();
+ } catch (Throwable throwable) {
+ throwable.printStackTrace();
+ }
+
+ }
+ });
+ mLayoutManager.waitForLayout(2);
+ getInstrumentation().waitForIdleSync();
+ assertEquals("on notify data set changed, predictive animations should not run",
+ layoutCount + 1, mLayoutManager.mTotalLayoutCount);
+ mLayoutManager.expectLayouts(2);
+ mTestAdapter.addAndNotify(4, 2);
+ // make sure animations recover
+ mLayoutManager.waitForLayout(2);
+ }
+
public void testGetItemForDeletedView() throws Throwable {
getItemForDeletedViewTest(false);
@@ -820,20 +838,6 @@
}
- private void postExceptionToInstrumentation(Throwable t) {
- if (DEBUG) {
- Log.e(TAG, "captured exception on main thread", t);
- }
- mainThreadException = t;
- if (mLayoutManager instanceof TestLayoutManager) {
- TestLayoutManager lm = mLayoutManager;
- // finish all layouts so that we get the correct exception
- while (lm.layoutLatch.getCount() > 0) {
- lm.layoutLatch.countDown();
- }
- }
- }
-
abstract class AdapterOps {
final public void run(TestAdapter adapter) throws Throwable {
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewLayoutTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewLayoutTest.java
index c662e31..64985ed 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewLayoutTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewLayoutTest.java
@@ -17,6 +17,8 @@
package android.support.v7.widget;
+import android.view.View;
+
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
@@ -29,6 +31,88 @@
super(DEBUG);
}
+ public void testFindViewById() throws Throwable {
+ findViewByIdTest(false);
+ removeRecyclerView();
+ findViewByIdTest(true);
+ }
+
+ public void findViewByIdTest(final boolean supportPredictive) throws Throwable {
+ final RecyclerView recyclerView = new RecyclerView(getActivity());
+ final int initialAdapterSize = 20;
+ final TestAdapter adapter = new TestAdapter(initialAdapterSize);
+ final int deleteStart = 6;
+ final int deleteCount = 5;
+ recyclerView.setAdapter(adapter);
+ final AtomicBoolean assertPositions = new AtomicBoolean(false);
+ TestLayoutManager lm = new TestLayoutManager() {
+ @Override
+ public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
+ super.onLayoutChildren(recycler, state);
+ if (assertPositions.get()) {
+ if (state.isPreLayout()) {
+ for (int i = 0; i < deleteStart; i ++) {
+ View view = findViewByPosition(i);
+ assertNotNull("find view by position for existing items should work "
+ + "fine", view);
+ assertFalse("view should not be marked as removed",
+ ((RecyclerView.LayoutParams) view.getLayoutParams())
+ .isItemRemoved());
+ }
+ for (int i = 0; i < deleteCount; i++) {
+ View view = findViewByPosition(i + deleteStart);
+ assertNotNull("find view by position should work fine for removed "
+ + "views in pre-layout", view);
+ assertTrue("view should be marked as removed",
+ ((RecyclerView.LayoutParams) view.getLayoutParams())
+ .isItemRemoved());
+ }
+ for (int i = deleteStart + deleteCount; i < 20; i++) {
+ View view = findViewByPosition(i);
+ assertNotNull(view);
+ assertFalse("view should not be marked as removed",
+ ((RecyclerView.LayoutParams) view.getLayoutParams())
+ .isItemRemoved());
+ }
+ } else {
+ for (int i = 0; i < initialAdapterSize - deleteCount; i ++) {
+ View view = findViewByPosition(i);
+ assertNotNull("find view by position for existing items should work "
+ + "fine", view);
+ TestViewHolder viewHolder =
+ (TestViewHolder) mRecyclerView.getChildViewHolder(view);
+ assertSame("should be the correct item " + viewHolder
+ ,viewHolder.mBindedItem,
+ adapter.mItems.get(viewHolder.mPosition));
+ assertFalse("view should not be marked as removed",
+ ((RecyclerView.LayoutParams) view.getLayoutParams())
+ .isItemRemoved());
+ }
+ }
+
+ }
+ detachAndScrapAttachedViews(recycler);
+ layoutRange(recycler, state.getItemCount() - 1, -1);
+ layoutLatch.countDown();
+ }
+
+ @Override
+ public boolean supportsPredictiveItemAnimations() {
+ return supportPredictive;
+ }
+ };
+ recyclerView.setLayoutManager(lm);
+ lm.expectLayouts(1);
+ setRecyclerView(recyclerView);
+ lm.waitForLayout(2);
+ getInstrumentation().waitForIdleSync();
+
+ assertPositions.set(true);
+ lm.expectLayouts(supportPredictive ? 2 : 1);
+ adapter.deleteAndNotify(new int[]{deleteStart, deleteCount - 1}, new int[]{deleteStart, 1});
+ lm.waitForLayout(2);
+ }
+
public void testTypeForCache() throws Throwable {
final AtomicInteger viewType = new AtomicInteger(1);
final TestAdapter adapter = new TestAdapter(100) {
@@ -85,6 +169,62 @@
});
}
+ public void testTypeForExistingViews() throws Throwable {
+ final AtomicInteger viewType = new AtomicInteger(1);
+ final int invalidatedCount = 2;
+ final int layoutStart = 2;
+ final TestAdapter adapter = new TestAdapter(100) {
+ @Override
+ public int getItemViewType(int position) {
+ return viewType.get();
+ }
+
+ @Override
+ public void onBindViewHolder(TestViewHolder holder,
+ int position) {
+ super.onBindViewHolder(holder, position);
+ if (position >= layoutStart && position < invalidatedCount + layoutStart) {
+ try {
+ assertEquals("holder type should match current view type at position " +
+ position, viewType.get(), holder.getItemViewType());
+ } catch (Throwable t) {
+ postExceptionToInstrumentation(t);
+ }
+ }
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return mItems.get(position).mId;
+ }
+ };
+ adapter.setHasStableIds(true);
+
+ final int childCount = 10;
+ final TestLayoutManager lm = new TestLayoutManager() {
+ @Override
+ public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
+ super.onLayoutChildren(recycler, state);
+ detachAndScrapAttachedViews(recycler);
+ layoutRange(recycler, layoutStart, layoutStart + childCount);
+ layoutLatch.countDown();
+ }
+ };
+ final RecyclerView recyclerView = new RecyclerView(getActivity());
+ recyclerView.setAdapter(adapter);
+ recyclerView.setLayoutManager(lm);
+ lm.expectLayouts(1);
+ setRecyclerView(recyclerView);
+ lm.waitForLayout(2);
+ getInstrumentation().waitForIdleSync();
+ viewType.incrementAndGet();
+ lm.expectLayouts(1);
+ adapter.notifyItemChange(layoutStart, invalidatedCount);
+ lm.waitForLayout(2);
+ checkForMainThreadException();
+ }
+
+
public void testState() throws Throwable {
final TestAdapter adapter = new TestAdapter(10);
final RecyclerView recyclerView = new RecyclerView(getActivity());
@@ -147,4 +287,4 @@
}
-}
\ No newline at end of file
+}