Merge "Support talkback d-pad scrolling in GridLayoutManager" into oc-mr1-jetpack-dev
diff --git a/leanback/src/android/support/v17/leanback/widget/GridLayoutManager.java b/leanback/src/android/support/v17/leanback/widget/GridLayoutManager.java
index 9d159ec..613198f 100644
--- a/leanback/src/android/support/v17/leanback/widget/GridLayoutManager.java
+++ b/leanback/src/android/support/v17/leanback/widget/GridLayoutManager.java
@@ -22,6 +22,7 @@
 import android.content.Context;
 import android.graphics.PointF;
 import android.graphics.Rect;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -3655,7 +3656,32 @@
     public boolean performAccessibilityAction(Recycler recycler, State state, int action,
             Bundle args) {
         saveContext(recycler, state);
-        switch (action) {
+        int translatedAction = action;
+        boolean reverseFlowPrimary = (mFlag & PF_REVERSE_FLOW_PRIMARY) != 0;
+        if (Build.VERSION.SDK_INT >= 23) {
+            if (mOrientation == HORIZONTAL) {
+                if (action == AccessibilityNodeInfoCompat.AccessibilityActionCompat
+                        .ACTION_SCROLL_LEFT.getId()) {
+                    translatedAction = reverseFlowPrimary
+                            ? AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD :
+                            AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD;
+                } else if (action == AccessibilityNodeInfoCompat.AccessibilityActionCompat
+                        .ACTION_SCROLL_RIGHT.getId()) {
+                    translatedAction = reverseFlowPrimary
+                            ? AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD :
+                            AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD;
+                }
+            } else { // VERTICAL layout
+                if (action == AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_UP
+                        .getId()) {
+                    translatedAction = AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD;
+                } else if (action == AccessibilityNodeInfoCompat.AccessibilityActionCompat
+                        .ACTION_SCROLL_DOWN.getId()) {
+                    translatedAction = AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD;
+                }
+            }
+        }
+        switch (translatedAction) {
             case AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD:
                 processSelectionMoves(false, -1);
                 break;
@@ -3726,12 +3752,39 @@
             AccessibilityNodeInfoCompat info) {
         saveContext(recycler, state);
         int count = state.getItemCount();
-        if ((mFlag & PF_SCROLL_ENABLED) != 0 && count > 1 && !isItemFullyVisible(0)) {
-            info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD);
+        boolean reverseFlowPrimary = (mFlag & PF_REVERSE_FLOW_PRIMARY) != 0;
+        if (count > 1 && !isItemFullyVisible(0)) {
+            if (Build.VERSION.SDK_INT >= 23) {
+                if (mOrientation == HORIZONTAL) {
+                    info.addAction(reverseFlowPrimary
+                            ? AccessibilityNodeInfoCompat.AccessibilityActionCompat
+                                    .ACTION_SCROLL_RIGHT :
+                            AccessibilityNodeInfoCompat.AccessibilityActionCompat
+                                    .ACTION_SCROLL_LEFT);
+                } else {
+                    info.addAction(
+                            AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_UP);
+                }
+            } else {
+                info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD);
+            }
             info.setScrollable(true);
         }
-        if ((mFlag & PF_SCROLL_ENABLED) != 0 && count > 1 && !isItemFullyVisible(count - 1)) {
-            info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD);
+        if (count > 1 && !isItemFullyVisible(count - 1)) {
+            if (Build.VERSION.SDK_INT >= 23) {
+                if (mOrientation == HORIZONTAL) {
+                    info.addAction(reverseFlowPrimary
+                            ? AccessibilityNodeInfoCompat.AccessibilityActionCompat
+                                    .ACTION_SCROLL_LEFT :
+                            AccessibilityNodeInfoCompat.AccessibilityActionCompat
+                                    .ACTION_SCROLL_RIGHT);
+                } else {
+                    info.addAction(AccessibilityNodeInfoCompat.AccessibilityActionCompat
+                                    .ACTION_SCROLL_DOWN);
+                }
+            } else {
+                info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD);
+            }
             info.setScrollable(true);
         }
         final AccessibilityNodeInfoCompat.CollectionInfoCompat collectionInfo =
diff --git a/leanback/tests/java/android/support/v17/leanback/widget/GridWidgetTest.java b/leanback/tests/java/android/support/v17/leanback/widget/GridWidgetTest.java
index 7bf96a5..4548494 100644
--- a/leanback/tests/java/android/support/v17/leanback/widget/GridWidgetTest.java
+++ b/leanback/tests/java/android/support/v17/leanback/widget/GridWidgetTest.java
@@ -4312,6 +4312,252 @@
         waitForScrollIdle(mVerifyLayout);
         assertEquals(1, mGridView.getSelectedPosition());
     }
+    @Test
+    public void testAccessibilityRespondToLeftRight() throws Throwable {
+        Intent intent = new Intent();
+        intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.horizontal_linear);
+        intent.putExtra(GridActivity.EXTRA_CHILD_LAYOUT_ID, R.layout.item_button_at_bottom);
+        intent.putExtra(GridActivity.EXTRA_ITEMS,  new int[]{});
+        intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
+        initActivity(intent);
+        mOrientation = BaseGridView.HORIZONTAL;
+        mNumRows = 1;
+
+        int width = mGridView.getWidth() - mGridView.getPaddingLeft()
+                - mGridView.getPaddingRight();
+        final int childWidth = width - mGridView.getHorizontalSpacing() - 500;
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mGridView.setWindowAlignment(BaseGridView.WINDOW_ALIGN_NO_EDGE);
+                mGridView.setWindowAlignmentOffset(100);
+                mGridView.setWindowAlignmentOffsetPercent(BaseGridView
+                        .WINDOW_ALIGN_OFFSET_PERCENT_DISABLED);
+                mGridView.setItemAlignmentOffset(0);
+                mGridView.setItemAlignmentOffsetPercent(BaseGridView
+                        .ITEM_ALIGN_OFFSET_PERCENT_DISABLED);
+            }
+        });
+        mActivity.addItems(0, new int[]{childWidth, childWidth});
+        waitForItemAnimation();
+        setSelectedPosition(1);
+        final RecyclerViewAccessibilityDelegate delegateCompat = mGridView
+                .getCompatAccessibilityDelegate();
+        final AccessibilityNodeInfoCompat info = AccessibilityNodeInfoCompat.obtain();
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                delegateCompat.onInitializeAccessibilityNodeInfo(mGridView, info);
+            }
+        });
+        assertTrue("test sanity", info.isScrollable());
+        if (Build.VERSION.SDK_INT >= 23) {
+            assertTrue("test sanity", info.removeAction(AccessibilityNodeInfoCompat
+                    .AccessibilityActionCompat.ACTION_SCROLL_LEFT));
+        } else {
+            assertTrue("test sanity", info.removeAction(AccessibilityNodeInfoCompat
+                    .AccessibilityActionCompat.ACTION_SCROLL_BACKWARD));
+        }
+
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                if (Build.VERSION.SDK_INT >= 23) {
+                    delegateCompat.performAccessibilityAction(mGridView, AccessibilityNodeInfoCompat
+                            .AccessibilityActionCompat.ACTION_SCROLL_LEFT.getId(), null);
+                } else {
+                    delegateCompat.performAccessibilityAction(mGridView, AccessibilityNodeInfoCompat
+                            .ACTION_SCROLL_BACKWARD, null);
+                }
+            }
+        });
+        waitForScrollIdle(mVerifyLayout);
+        assertEquals(0, mGridView.getSelectedPosition());
+        setSelectedPosition(0);
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                delegateCompat.onInitializeAccessibilityNodeInfo(mGridView, info);
+            }
+        });
+        if (Build.VERSION.SDK_INT >= 23) {
+            assertTrue("test sanity", info.removeAction(AccessibilityNodeInfoCompat
+                    .AccessibilityActionCompat.ACTION_SCROLL_RIGHT));
+        } else {
+            assertTrue("test sanity", info.removeAction(AccessibilityNodeInfoCompat
+                    .AccessibilityActionCompat.ACTION_SCROLL_FORWARD));
+        }
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                if (Build.VERSION.SDK_INT >= 23) {
+                    delegateCompat.performAccessibilityAction(mGridView, AccessibilityNodeInfoCompat
+                            .AccessibilityActionCompat.ACTION_SCROLL_RIGHT.getId(), null);
+                } else {
+                    delegateCompat.performAccessibilityAction(mGridView, AccessibilityNodeInfoCompat
+                            .ACTION_SCROLL_FORWARD, null);
+                }
+            }
+        });
+        waitForScrollIdle(mVerifyLayout);
+        assertEquals(1, mGridView.getSelectedPosition());
+    }
+
+    @Test
+    public void testAccessibilityRespondToLeftRightRtl() throws Throwable {
+        Intent intent = new Intent();
+        intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.horizontal_linear_rtl);
+        intent.putExtra(GridActivity.EXTRA_CHILD_LAYOUT_ID, R.layout.item_button_at_bottom);
+        intent.putExtra(GridActivity.EXTRA_ITEMS,  new int[]{});
+        intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
+        initActivity(intent);
+        mOrientation = BaseGridView.HORIZONTAL;
+        mNumRows = 1;
+
+        int width = mGridView.getWidth() - mGridView.getPaddingLeft()
+                - mGridView.getPaddingRight();
+        final int childWidth = width - mGridView.getHorizontalSpacing() - 500;
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mGridView.setLayoutDirection(View.LAYOUT_DIRECTION_RTL);
+                mGridView.setWindowAlignment(BaseGridView.WINDOW_ALIGN_NO_EDGE);
+                mGridView.setWindowAlignmentOffset(100);
+                mGridView.setWindowAlignmentOffsetPercent(BaseGridView
+                        .WINDOW_ALIGN_OFFSET_PERCENT_DISABLED);
+                mGridView.setItemAlignmentOffset(0);
+                mGridView.setItemAlignmentOffsetPercent(BaseGridView
+                        .ITEM_ALIGN_OFFSET_PERCENT_DISABLED);
+            }
+        });
+        mActivity.addItems(0, new int[]{childWidth, childWidth});
+        waitForItemAnimation();
+        setSelectedPosition(1);
+        final RecyclerViewAccessibilityDelegate delegateCompat = mGridView
+                .getCompatAccessibilityDelegate();
+        final AccessibilityNodeInfoCompat info = AccessibilityNodeInfoCompat.obtain();
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                delegateCompat.onInitializeAccessibilityNodeInfo(mGridView, info);
+            }
+        });
+        assertTrue("test sanity", info.isScrollable());
+        if (Build.VERSION.SDK_INT >= 23) {
+            assertTrue("test sanity", info.removeAction(AccessibilityNodeInfoCompat
+                    .AccessibilityActionCompat.ACTION_SCROLL_RIGHT));
+        } else {
+            assertTrue("test sanity", info.removeAction(AccessibilityNodeInfoCompat
+                    .AccessibilityActionCompat.ACTION_SCROLL_BACKWARD));
+        }
+
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                if (Build.VERSION.SDK_INT >= 23) {
+                    delegateCompat.performAccessibilityAction(mGridView, AccessibilityNodeInfoCompat
+                            .AccessibilityActionCompat.ACTION_SCROLL_RIGHT.getId(), null);
+                } else {
+                    delegateCompat.performAccessibilityAction(mGridView, AccessibilityNodeInfoCompat
+                            .ACTION_SCROLL_BACKWARD, null);
+                }
+            }
+        });
+        waitForScrollIdle(mVerifyLayout);
+        assertEquals(0, mGridView.getSelectedPosition());
+        setSelectedPosition(0);
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                delegateCompat.onInitializeAccessibilityNodeInfo(mGridView, info);
+            }
+        });
+        if (Build.VERSION.SDK_INT >= 23) {
+            assertTrue("test sanity", info.removeAction(AccessibilityNodeInfoCompat
+                    .AccessibilityActionCompat.ACTION_SCROLL_LEFT));
+        } else {
+            assertTrue("test sanity", info.removeAction(AccessibilityNodeInfoCompat
+                    .AccessibilityActionCompat.ACTION_SCROLL_FORWARD));
+        }
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                if (Build.VERSION.SDK_INT >= 23) {
+                    delegateCompat.performAccessibilityAction(mGridView, AccessibilityNodeInfoCompat
+                            .AccessibilityActionCompat.ACTION_SCROLL_LEFT.getId(), null);
+                } else {
+                    delegateCompat.performAccessibilityAction(mGridView, AccessibilityNodeInfoCompat
+                            .ACTION_SCROLL_FORWARD, null);
+                }
+            }
+        });
+        waitForScrollIdle(mVerifyLayout);
+        assertEquals(1, mGridView.getSelectedPosition());
+    }
+
+    @Test
+    public void testAccessibilityRespondToScrollUpAction() throws Throwable {
+        Intent intent = new Intent();
+        intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.vertical_linear);
+        intent.putExtra(GridActivity.EXTRA_CHILD_LAYOUT_ID, R.layout.item_button_at_bottom);
+        intent.putExtra(GridActivity.EXTRA_ITEMS,  new int[]{});
+        intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
+        initActivity(intent);
+        mOrientation = BaseGridView.VERTICAL;
+        mNumRows = 1;
+
+        int height = mGridView.getHeight() - mGridView.getPaddingTop()
+                - mGridView.getPaddingBottom();
+        final int childHeight = height - mGridView.getVerticalSpacing() - 100;
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mGridView.setWindowAlignment(BaseGridView.WINDOW_ALIGN_NO_EDGE);
+                mGridView.setWindowAlignmentOffset(100);
+                mGridView.setWindowAlignmentOffsetPercent(BaseGridView
+                        .WINDOW_ALIGN_OFFSET_PERCENT_DISABLED);
+                mGridView.setItemAlignmentOffset(0);
+                mGridView.setItemAlignmentOffsetPercent(BaseGridView
+                        .ITEM_ALIGN_OFFSET_PERCENT_DISABLED);
+            }
+        });
+        mActivity.addItems(0, new int[]{childHeight, childHeight});
+        waitForItemAnimation();
+        setSelectedPosition(1);
+
+        final RecyclerViewAccessibilityDelegate delegateCompat = mGridView
+                .getCompatAccessibilityDelegate();
+        final AccessibilityNodeInfoCompat info = AccessibilityNodeInfoCompat.obtain();
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                delegateCompat.onInitializeAccessibilityNodeInfo(mGridView, info);
+            }
+        });
+        assertTrue("test sanity", info.isScrollable());
+        if (Build.VERSION.SDK_INT >= 23) {
+            assertTrue("test sanity", info.removeAction(AccessibilityNodeInfoCompat
+                    .AccessibilityActionCompat.ACTION_SCROLL_UP));
+        } else {
+            assertTrue("test sanity", info.removeAction(AccessibilityNodeInfoCompat
+                    .AccessibilityActionCompat.ACTION_SCROLL_BACKWARD));
+        }
+
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                if (Build.VERSION.SDK_INT >= 23) {
+                    delegateCompat.performAccessibilityAction(mGridView, AccessibilityNodeInfoCompat
+                            .AccessibilityActionCompat.ACTION_SCROLL_UP.getId(), null);
+                } else {
+                    delegateCompat.performAccessibilityAction(mGridView, AccessibilityNodeInfoCompat
+                            .ACTION_SCROLL_BACKWARD, null);
+                }
+            }
+        });
+        waitForScrollIdle(mVerifyLayout);
+        assertEquals(0, mGridView.getSelectedPosition());
+    }
 
     @Test
     public void testAccessibilityScrollBackwardHalfVisible() throws Throwable {