Support cleaner rotary scroll declaration

Add a custom app:rotaryScrollEnabled attribute to CarUiRecyclerView.
When set to true, this is equivalent to calling
CarUiUtils.setRotaryScrollEnabled(true).

Test: Use Scroll tab in RotaryPlayground
Bug: 171339427
Change-Id: If8b00edba0728c5d49ab02e4a1d7549466224009
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 e9e5b3c..93b7736 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
@@ -52,6 +52,7 @@
 import com.android.car.ui.recyclerview.decorations.linear.LinearDividerItemDecoration;
 import com.android.car.ui.recyclerview.decorations.linear.LinearOffsetItemDecoration;
 import com.android.car.ui.recyclerview.decorations.linear.LinearOffsetItemDecoration.OffsetPosition;
+import com.android.car.ui.utils.CarUiUtils;
 import com.android.car.ui.utils.CarUxRestrictionsUtil;
 
 import java.lang.annotation.Retention;
@@ -187,13 +188,13 @@
     }
 
     private void init(Context context, AttributeSet attrs, int defStyleAttr) {
-        initRotaryScroll();
         setClipToPadding(false);
         TypedArray a = context.obtainStyledAttributes(
                 attrs,
                 R.styleable.CarUiRecyclerView,
                 defStyleAttr,
                 R.style.Widget_CarUi_CarUiRecyclerView);
+        initRotaryScroll(a);
 
         mScrollBarEnabled = context.getResources().getBoolean(R.bool.car_ui_scrollbar_enable);
 
@@ -293,14 +294,24 @@
     }
 
     /**
-     * If this view's content description is set to opt into scrolling via the rotary controller,
-     * initialize it accordingly.
+     * If this view's {@code rotaryScrollEnabled} attribute is set to true, sets the content
+     * description so that the {@code RotaryService} will treat it as a scrollable container and
+     * initializes this view accordingly.
      */
-    private void initRotaryScroll() {
-        CharSequence contentDescription = getContentDescription();
-        boolean rotaryScrollEnabled = contentDescription != null
-                && (ROTARY_HORIZONTALLY_SCROLLABLE.contentEquals(contentDescription)
-                || ROTARY_VERTICALLY_SCROLLABLE.contentEquals(contentDescription));
+    private void initRotaryScroll(@Nullable TypedArray styledAttributes) {
+        boolean rotaryScrollEnabled = styledAttributes != null && styledAttributes.getBoolean(
+                R.styleable.CarUiRecyclerView_rotaryScrollEnabled, /* defValue=*/ false);
+        if (rotaryScrollEnabled) {
+            int orientation = styledAttributes.getInt(R.styleable.RecyclerView_android_orientation,
+                    LinearLayout.VERTICAL);
+            CarUiUtils.setRotaryScrollEnabled(
+                    this, /* isVertical= */ orientation == LinearLayout.VERTICAL);
+        } else {
+            CharSequence contentDescription = getContentDescription();
+            rotaryScrollEnabled = contentDescription != null
+                    && (ROTARY_HORIZONTALLY_SCROLLABLE.contentEquals(contentDescription)
+                    || ROTARY_VERTICALLY_SCROLLABLE.contentEquals(contentDescription));
+        }
 
         // If rotary scrolling is enabled, set a generic motion event listener to convert
         // SOURCE_ROTARY_ENCODER scroll events into SOURCE_MOUSE scroll events that RecyclerView
@@ -555,7 +566,7 @@
     @Override
     public void setContentDescription(CharSequence contentDescription) {
         super.setContentDescription(contentDescription);
-        initRotaryScroll();
+        initRotaryScroll(/* styledAttributes= */ null);
     }
 
     private static RuntimeException andLog(String msg, Throwable t) {
diff --git a/car-ui-lib/car-ui-lib/src/main/res/values/attrs.xml b/car-ui-lib/car-ui-lib/src/main/res/values/attrs.xml
index f38522f..0ca5abe 100644
--- a/car-ui-lib/car-ui-lib/src/main/res/values/attrs.xml
+++ b/car-ui-lib/car-ui-lib/src/main/res/values/attrs.xml
@@ -113,6 +113,13 @@
         <!-- Bottom offset for car ui recycler view for linear layout. -->
         <attr name="bottomOffset" format="integer" />
 
+        <!-- Whether to enable rotary scrolling. Disabled by default. With rotary scrolling enabled,
+        rotating the rotary controller will scroll rather than moving the focus when moving the
+        focus would cause a lot of scrolling. Rotary scrolling should be enabled when the recycler
+        view contains content which the user may want to see but can't interact with, either alone
+        or along with interactive (focusable) content. -->
+        <attr name="rotaryScrollEnabled" format="boolean" />
+
         <!-- Number of columns in a grid layout. -->
         <attr name="numOfColumns" format="integer" />