Snap for 7510676 from 8b5e5b64825e42c4e5be831ecce93ebe6b50002e to sc-release

Change-Id: Iab6446c5b944c89a23912921882e13dd17130b57
diff --git a/car-ui-lib/car-ui-lib/src/androidTest/java/com/android/car/ui/recyclerview/DelegatingContentLimitingAdapterTest.java b/car-ui-lib/car-ui-lib/src/androidTest/java/com/android/car/ui/recyclerview/DelegatingContentLimitingAdapterTest.java
index 3908df8..b3ad0c1 100644
--- a/car-ui-lib/car-ui-lib/src/androidTest/java/com/android/car/ui/recyclerview/DelegatingContentLimitingAdapterTest.java
+++ b/car-ui-lib/car-ui-lib/src/androidTest/java/com/android/car/ui/recyclerview/DelegatingContentLimitingAdapterTest.java
@@ -21,12 +21,16 @@
 import static androidx.test.espresso.matcher.ViewMatchers.withId;
 import static androidx.test.espresso.matcher.ViewMatchers.withText;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
 
 import android.view.View;
 
 import androidx.recyclerview.widget.LinearLayoutManager;
 import androidx.recyclerview.widget.OrientationHelper;
+import androidx.recyclerview.widget.RecyclerView.AdapterDataObserver;
 import androidx.test.core.app.ActivityScenario;
 import androidx.test.ext.junit.rules.ActivityScenarioRule;
 
@@ -36,6 +40,7 @@
 import org.junit.Rule;
 import org.junit.Test;
 
+import java.util.ArrayList;
 import java.util.Objects;
 
 public class DelegatingContentLimitingAdapterTest {
@@ -127,4 +132,150 @@
 
         onView(withText(mDelegateAdapter.getItemText(15))).check(matches(isDisplayed()));
     }
+
+    @Test
+    public void testChangeItem_callsObservers() {
+        mDelegateAdapter = new TestDelegatingContentLimitingAdapter(50);
+        mContentLimitingAdapter = new DelegatingContentLimitingAdapter<>(mDelegateAdapter, 1);
+
+        AdapterDataObserver observer = mock(AdapterDataObserver.class);
+        mContentLimitingAdapter.registerAdapterDataObserver(observer);
+
+        onView(withId(R.id.list)).check(matches(isDisplayed()));
+
+        CarUiRecyclerView carUiRecyclerView = mActivity.requireViewById(R.id.list);
+        mActivity.runOnUiThread(() -> {
+            carUiRecyclerView.setAdapter(mContentLimitingAdapter);
+            carUiRecyclerView.setVisibility(View.VISIBLE);
+            mContentLimitingAdapter.setMaxItems(10);
+            mDelegateAdapter.changeItemRange(5, 3);
+        });
+
+        onView(withText(mDelegateAdapter.getItemText(0))).check(matches(isDisplayed()));
+
+        verify(observer).onItemRangeChanged(5, 3, null);
+    }
+
+    @Test
+    public void testInsertItem_callsObservers() {
+        mDelegateAdapter = new TestDelegatingContentLimitingAdapter(50);
+        mContentLimitingAdapter = new DelegatingContentLimitingAdapter<>(mDelegateAdapter, 1);
+
+        AdapterDataObserver observer = mock(AdapterDataObserver.class);
+        mContentLimitingAdapter.registerAdapterDataObserver(observer);
+
+        onView(withId(R.id.list)).check(matches(isDisplayed()));
+
+        CarUiRecyclerView carUiRecyclerView = mActivity.requireViewById(R.id.list);
+        mActivity.runOnUiThread(() -> {
+            carUiRecyclerView.setAdapter(mContentLimitingAdapter);
+            carUiRecyclerView.setVisibility(View.VISIBLE);
+            mContentLimitingAdapter.setMaxItems(10);
+            mDelegateAdapter.insertItemRange(5, "new item 1", "new item 2");
+        });
+
+        onView(withText(mDelegateAdapter.getItemText(0))).check(matches(isDisplayed()));
+
+        verify(observer).onItemRangeInserted(5, 2);
+    }
+
+    @Test
+    public void testRemoveItem_callsObservers() {
+        mDelegateAdapter = new TestDelegatingContentLimitingAdapter(50);
+        mContentLimitingAdapter = new DelegatingContentLimitingAdapter<>(mDelegateAdapter, 1);
+
+        AdapterDataObserver observer = mock(AdapterDataObserver.class);
+        mContentLimitingAdapter.registerAdapterDataObserver(observer);
+
+        onView(withId(R.id.list)).check(matches(isDisplayed()));
+
+        CarUiRecyclerView carUiRecyclerView = mActivity.requireViewById(R.id.list);
+        mActivity.runOnUiThread(() -> {
+            carUiRecyclerView.setAdapter(mContentLimitingAdapter);
+            carUiRecyclerView.setVisibility(View.VISIBLE);
+            mContentLimitingAdapter.setMaxItems(10);
+            mDelegateAdapter.removeItemRange(5, 2);
+        });
+
+        onView(withText(mDelegateAdapter.getItemText(0))).check(matches(isDisplayed()));
+
+        verify(observer).onItemRangeRemoved(5, 2);
+    }
+
+    @Test
+    public void testMoveItem_callsObservers() {
+        mDelegateAdapter = new TestDelegatingContentLimitingAdapter(50);
+        mContentLimitingAdapter = new DelegatingContentLimitingAdapter<>(mDelegateAdapter, 1);
+
+        AdapterDataObserver observer = mock(AdapterDataObserver.class);
+        mContentLimitingAdapter.registerAdapterDataObserver(observer);
+
+        onView(withId(R.id.list)).check(matches(isDisplayed()));
+
+        CarUiRecyclerView carUiRecyclerView = mActivity.requireViewById(R.id.list);
+        mActivity.runOnUiThread(() -> {
+            carUiRecyclerView.setAdapter(mContentLimitingAdapter);
+            carUiRecyclerView.setVisibility(View.VISIBLE);
+            mContentLimitingAdapter.setMaxItems(10);
+            mDelegateAdapter.moveItem(5, 2);
+        });
+
+        onView(withText(mDelegateAdapter.getItemText(0))).check(matches(isDisplayed()));
+
+        verify(observer).onChanged();
+    }
+
+    @Test
+    public void testChangeDataSet_callsObservers() {
+        mDelegateAdapter = new TestDelegatingContentLimitingAdapter(50);
+        mContentLimitingAdapter = new DelegatingContentLimitingAdapter<>(mDelegateAdapter, 1);
+
+        AdapterDataObserver observer = mock(AdapterDataObserver.class);
+        mContentLimitingAdapter.registerAdapterDataObserver(observer);
+
+        onView(withId(R.id.list)).check(matches(isDisplayed()));
+
+        CarUiRecyclerView carUiRecyclerView = mActivity.requireViewById(R.id.list);
+        mActivity.runOnUiThread(() -> {
+            carUiRecyclerView.setAdapter(mContentLimitingAdapter);
+            carUiRecyclerView.setVisibility(View.VISIBLE);
+            mContentLimitingAdapter.setMaxItems(10);
+        });
+
+        onView(withText(mDelegateAdapter.getItemText(0))).check(matches(isDisplayed()));
+
+        mActivity.runOnUiThread(() -> {
+            ArrayList<String> newItems = new ArrayList<>();
+            for (int i = 0; i < 40; i++) {
+                newItems.add("New Item " + i);
+            }
+            mDelegateAdapter.changeList(newItems);
+        });
+
+        onView(withText("New Item 0")).check(matches(isDisplayed()));
+
+        verify(observer).onChanged();
+    }
+
+    @Test
+    public void testSetHasStableId_setsDelegateToo() {
+        mDelegateAdapter = new TestDelegatingContentLimitingAdapter(50);
+        mDelegateAdapter.setHasStableIds(false);
+
+        mContentLimitingAdapter = new DelegatingContentLimitingAdapter<>(mDelegateAdapter, 1);
+
+        mContentLimitingAdapter.setHasStableIds(true);
+
+        assertTrue(mDelegateAdapter.hasStableIds());
+    }
+
+    @Test
+    public void testGetIds_callsDelegate() {
+        mDelegateAdapter = new TestDelegatingContentLimitingAdapter(50);
+        mContentLimitingAdapter = new DelegatingContentLimitingAdapter<>(mDelegateAdapter, 1);
+
+        for (int i = 0; i < 50; i++) {
+            assertEquals(mDelegateAdapter.getItemId(i), mContentLimitingAdapter.getItemId(i));
+        }
+    }
 }
diff --git a/car-ui-lib/car-ui-lib/src/androidTest/java/com/android/car/ui/recyclerview/TestDelegatingContentLimitingAdapter.java b/car-ui-lib/car-ui-lib/src/androidTest/java/com/android/car/ui/recyclerview/TestDelegatingContentLimitingAdapter.java
index d3e9791..4a59c04 100644
--- a/car-ui-lib/car-ui-lib/src/androidTest/java/com/android/car/ui/recyclerview/TestDelegatingContentLimitingAdapter.java
+++ b/car-ui-lib/car-ui-lib/src/androidTest/java/com/android/car/ui/recyclerview/TestDelegatingContentLimitingAdapter.java
@@ -62,6 +62,45 @@
         return "Item " + i;
     }
 
+    public void changeItemRange(int positionStart, int itemCount) {
+        for (int i = 0; i < itemCount; i++) {
+            mItems.set(i + positionStart, mItems.get(i + positionStart) + "-changed");
+        }
+        notifyItemRangeChanged(positionStart, itemCount);
+    }
+
+    public void insertItemRange(int position, String... items) {
+        for (int i = 0; i < items.length; i++) {
+            mItems.add(position, items[i]);
+        }
+        notifyItemRangeInserted(position, items.length);
+    }
+
+    public void removeItemRange(int positionStart, int itemCount) {
+        for (int i = 0; i < itemCount; i++) {
+            mItems.remove(positionStart + i);
+        }
+        notifyItemRangeRemoved(positionStart, itemCount);
+    }
+
+    public void moveItem(int fromPosition, int toPosition) {
+        String item = mItems.get(fromPosition);
+        if (fromPosition > toPosition) {
+            mItems.remove(fromPosition);
+            mItems.add(toPosition, item);
+        } else {
+            mItems.add(toPosition, item);
+            mItems.remove(fromPosition);
+        }
+        notifyItemMoved(fromPosition, toPosition);
+    }
+
+    public void changeList(List<String> newItems) {
+        mItems.clear();
+        mItems.addAll(newItems);
+        notifyDataSetChanged();
+    }
+
     static class WithContentLimiting extends TestDelegatingContentLimitingAdapter
             implements DelegatingContentLimitingAdapter.ContentLimiting {
 
diff --git a/car-ui-lib/car-ui-lib/src/main/java/com/android/car/ui/recyclerview/DelegatingContentLimitingAdapter.java b/car-ui-lib/car-ui-lib/src/main/java/com/android/car/ui/recyclerview/DelegatingContentLimitingAdapter.java
index c865b75..62d9feb 100644
--- a/car-ui-lib/car-ui-lib/src/main/java/com/android/car/ui/recyclerview/DelegatingContentLimitingAdapter.java
+++ b/car-ui-lib/car-ui-lib/src/main/java/com/android/car/ui/recyclerview/DelegatingContentLimitingAdapter.java
@@ -41,6 +41,8 @@
     private final int mScrollingLimitedMessagePositionOffset;
     @IdRes
     private final int mConfigId;
+    @NonNull
+    private final Observer mAdapterDataObserver;
 
     /**
      * Provides the abilities to delegate {@link ContentLimitingAdapter} callback functions.
@@ -102,7 +104,24 @@
         mConfigId = configId;
         mScrollingLimitedMessageViewType = viewType;
         mScrollingLimitedMessagePositionOffset = offset;
-        mDelegate.registerAdapterDataObserver(new Observer());
+        mAdapterDataObserver = new Observer();
+    }
+
+    @Override
+    public void registerAdapterDataObserver(@NonNull RecyclerView.AdapterDataObserver observer) {
+        if (!hasObservers()) {
+            mDelegate.registerAdapterDataObserver(mAdapterDataObserver);
+        }
+        super.registerAdapterDataObserver(observer);
+    }
+
+    @Override
+    public void unregisterAdapterDataObserver(@NonNull RecyclerView.AdapterDataObserver observer) {
+        super.unregisterAdapterDataObserver(observer);
+
+        if (!hasObservers()) {
+            mDelegate.unregisterAdapterDataObserver(mAdapterDataObserver);
+        }
     }
 
     private class Observer extends RecyclerView.AdapterDataObserver {