Add ability to specify the scrollbar container width.

The scrollbar is centered within this width.

Test: unit tests and test on sample app.
Bug: 72118903
Change-Id: I621448cc6c097b09485546880aed14f143f5fc0f
diff --git a/car/res/values/attrs.xml b/car/res/values/attrs.xml
index 4930e1b..6b6c44b 100644
--- a/car/res/values/attrs.xml
+++ b/car/res/values/attrs.xml
@@ -50,6 +50,10 @@
         <attr name="scrollBarEnabled" format="boolean" />
         <!-- The top margin before the scroll bar is drawn. -->
         <attr name="scrollBarTopMargin" format="dimension" />
+        <!-- The width of the container that will hold the scrollbar. The scrollbar is centered
+             within this value. If this value is not explicitly set, the scrollbar centers itself
+             within the car_margin value. -->
+        <attr name="scrollBarContainerWidth" format="dimension" />
         <!-- Whether or not to show a diving line between each item of the list. -->
         <attr name="showPagedListViewDivider" format="boolean" />
         <!-- An optional id that specifies a child View whose starting edge will be used to
diff --git a/car/src/main/java/androidx/car/widget/PagedListView.java b/car/src/main/java/androidx/car/widget/PagedListView.java
index cc4b4fa..2831fa2 100644
--- a/car/src/main/java/androidx/car/widget/PagedListView.java
+++ b/car/src/main/java/androidx/car/widget/PagedListView.java
@@ -40,6 +40,7 @@
 import android.util.SparseArray;
 import android.view.LayoutInflater;
 import android.view.View;
+import android.view.ViewGroup;
 import android.widget.FrameLayout;
 
 import androidx.car.R;
@@ -341,6 +342,13 @@
             params.setMarginStart(0);
         }
 
+        if (a.hasValue(R.styleable.PagedListView_scrollBarContainerWidth)) {
+            int carMargin = getResources().getDimensionPixelSize(R.dimen.car_margin);
+            int scrollBarContainerWidth = a.getDimensionPixelSize(
+                    R.styleable.PagedListView_scrollBarContainerWidth, carMargin);
+            setScrollBarContainerWidth(scrollBarContainerWidth);
+        }
+
         setDayNightStyle(DayNightStyle.AUTO);
         a.recycle();
     }
@@ -407,6 +415,18 @@
     }
 
     /**
+     * Sets the width of the container that holds the scrollbar. The scrollbar will be centered
+     * within this width.
+     *
+     * @param width The width of the scrollbar container.
+     */
+    public void setScrollBarContainerWidth(int width) {
+        ViewGroup.LayoutParams layoutParams = mScrollBarView.getLayoutParams();
+        layoutParams.width = width;
+        mScrollBarView.requestLayout();
+    }
+
+    /**
      * Sets the top margin above the scroll bar. By default, this margin is 0.
      *
      * @param topMargin The top margin.
@@ -414,7 +434,7 @@
     public void setScrollBarTopMargin(int topMargin) {
         MarginLayoutParams params = (MarginLayoutParams) mScrollBarView.getLayoutParams();
         params.topMargin = topMargin;
-        requestLayout();
+        mScrollBarView.requestLayout();
     }
 
     @NonNull
diff --git a/car/tests/src/androidx/car/widget/PagedListViewTest.java b/car/tests/src/androidx/car/widget/PagedListViewTest.java
index 8579fd5..fe56a71 100644
--- a/car/tests/src/androidx/car/widget/PagedListViewTest.java
+++ b/car/tests/src/androidx/car/widget/PagedListViewTest.java
@@ -520,6 +520,36 @@
         assertThat(mPagedListView.getRecyclerView().getPaddingEnd(), is(equalTo(gutterSize)));
     }
 
+    @Test
+    public void setDefaultScrollBarContainerWidth() {
+        if (!isAutoDevice()) {
+            return;
+        }
+
+        // Just need enough items to ensure the scroll bar is showing.
+        setUpPagedListView(ITEMS_PER_PAGE * 10);
+
+        Resources res = InstrumentationRegistry.getContext().getResources();
+        int defaultWidth = res.getDimensionPixelSize(R.dimen.car_margin);
+
+        onView(withId(R.id.paged_scroll_view)).check(matches(withWidth(defaultWidth)));
+    }
+
+    @Test
+    public void testSetScrollBarContainerWidth() {
+        if (!isAutoDevice()) {
+            return;
+        }
+
+        // Just need enough items to ensure the scroll bar is showing.
+        setUpPagedListView(ITEMS_PER_PAGE * 10);
+
+        int scrollBarContainerWidth = 120;
+        mPagedListView.setScrollBarContainerWidth(scrollBarContainerWidth);
+
+        onView(withId(R.id.paged_scroll_view)).check(matches(withWidth(scrollBarContainerWidth)));
+    }
+
     private static String itemText(int index) {
         return "Data " + index;
     }
@@ -652,4 +682,24 @@
             }
         };
     }
+
+    /**
+     * Returns a matcher that matches {@link View}s that have the given width.
+     *
+     * @param width The width to match to.
+     */
+    @NonNull
+    public static Matcher<View> withWidth(int width) {
+        return new TypeSafeMatcher<View>() {
+            @Override
+            public void describeTo(Description description) {
+                description.appendText("with width: " + width);
+            }
+
+            @Override
+            public boolean matchesSafely(View view) {
+                return width == view.getLayoutParams().width;
+            }
+        };
+    }
 }