Fix scrollbar keeping a reference to the adapter

There was a NPE, and a memory leak because of an observer to adapter
data changes was not unregistered.

Bug: 174846915
Test: manual

Change-Id: I4c649c59236bf406ae721092de3984b84cb8f1ac
diff --git a/car-ui-lib/car-ui-lib/src/main/java/com/android/car/ui/recyclerview/CarUiRecyclerView.java b/car-ui-lib/car-ui-lib/src/main/java/com/android/car/ui/recyclerview/CarUiRecyclerView.java
index 93b7736..d94fbf3 100644
--- a/car-ui-lib/car-ui-lib/src/main/java/com/android/car/ui/recyclerview/CarUiRecyclerView.java
+++ b/car-ui-lib/car-ui-lib/src/main/java/com/android/car/ui/recyclerview/CarUiRecyclerView.java
@@ -569,6 +569,16 @@
         initRotaryScroll(/* styledAttributes= */ null);
     }
 
+    @Override
+    public void setAdapter(@Nullable Adapter adapter) {
+        if (mScrollBar != null) {
+            // Make sure this is called before super so that scrollbar can get a reference to
+            // the adapter using RecyclerView#getAdapter()
+            mScrollBar.adapterChanged(adapter);
+        }
+        super.setAdapter(adapter);
+    }
+
     private static RuntimeException andLog(String msg, Throwable t) {
         Log.e(TAG, msg, t);
         throw new RuntimeException(msg, t);
diff --git a/car-ui-lib/car-ui-lib/src/main/java/com/android/car/ui/recyclerview/DefaultScrollBar.java b/car-ui-lib/car-ui-lib/src/main/java/com/android/car/ui/recyclerview/DefaultScrollBar.java
index 2bed61c..42554bf 100644
--- a/car-ui-lib/car-ui-lib/src/main/java/com/android/car/ui/recyclerview/DefaultScrollBar.java
+++ b/car-ui-lib/car-ui-lib/src/main/java/com/android/car/ui/recyclerview/DefaultScrollBar.java
@@ -133,6 +133,22 @@
                 mScrollView.getPaddingRight(), paddingEnd);
     }
 
+    @Override
+    public void adapterChanged(@Nullable RecyclerView.Adapter adapter) {
+        try {
+            if (mRecyclerView.getAdapter() != null) {
+                mRecyclerView.getAdapter().unregisterAdapterDataObserver(mAdapterChangeObserver);
+            }
+            if (adapter != null) {
+                adapter.registerAdapterDataObserver(mAdapterChangeObserver);
+            }
+        } catch (IllegalStateException e) {
+            // adapter is already registered. and we're trying to register again.
+            // or adapter was not registered and we're trying to unregister again.
+            // ignore.
+        }
+    }
+
     /**
      * Sets whether or not the up button on the scroll bar is clickable.
      *
@@ -262,27 +278,12 @@
                 .start();
     }
 
-    private boolean mIsAdapterChangeObserverRegistered = false;
-    @Nullable
-    private RecyclerView.Adapter mCurrentAdapter;
     private final RecyclerView.OnScrollListener mRecyclerViewOnScrollListener =
             new RecyclerView.OnScrollListener() {
                 @Override
                 public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
                     updatePaginationButtons();
                     cacheChildrenHeight(recyclerView.getLayoutManager());
-                    if (mCurrentAdapter != recyclerView.getAdapter()) {
-                        mIsAdapterChangeObserverRegistered = false;
-                        if (mCurrentAdapter != null) {
-                            mCurrentAdapter.unregisterAdapterDataObserver(mAdapterChangeObserver);
-                        }
-                    }
-                    if (!mIsAdapterChangeObserverRegistered
-                            && recyclerView.getAdapter() != null) {
-                        mIsAdapterChangeObserverRegistered = true;
-                        mCurrentAdapter = recyclerView.getAdapter();
-                        mCurrentAdapter.registerAdapterDataObserver(mAdapterChangeObserver);
-                    }
                 }
             };
     private final SparseArray<Integer> mChildHeightByAdapterPosition = new SparseArray();
@@ -320,7 +321,10 @@
         cacheChildrenHeight(mRecyclerView.getLayoutManager());
     }
 
-    private void cacheChildrenHeight(RecyclerView.LayoutManager layoutManager) {
+    private void cacheChildrenHeight(@Nullable RecyclerView.LayoutManager layoutManager) {
+        if (layoutManager == null) {
+            return;
+        }
         for (int i = 0; i < layoutManager.getChildCount(); i++) {
             View child = layoutManager.getChildAt(i);
             int childPosition = layoutManager.getPosition(child);
diff --git a/car-ui-lib/car-ui-lib/src/main/java/com/android/car/ui/recyclerview/ScrollBar.java b/car-ui-lib/car-ui-lib/src/main/java/com/android/car/ui/recyclerview/ScrollBar.java
index 8e127f0..9f4ee97 100644
--- a/car-ui-lib/car-ui-lib/src/main/java/com/android/car/ui/recyclerview/ScrollBar.java
+++ b/car-ui-lib/car-ui-lib/src/main/java/com/android/car/ui/recyclerview/ScrollBar.java
@@ -39,4 +39,9 @@
 
     /** Sets the padding of the scrollbar, relative to the padding of the RecyclerView. */
     void setPadding(int paddingStart, int paddingEnd);
+
+    /**
+     * Called when recyclerview's setAdapter is called.
+     */
+    void adapterChanged(RecyclerView.Adapter adapter);
 }