Add Alt + dpad hotkeys to ViewPager
Alt + right/left on a view within a ViewPager should now
switch pages.
Bug: 32673955
Test: v4.view.BaseViewPagerTest#testKeyboardNavigation
Change-Id: I811659be7acca4a6e1990844cfaeb95cb403e0cc
diff --git a/core-ui/java/android/support/v4/view/ViewPager.java b/core-ui/java/android/support/v4/view/ViewPager.java
index 60b1ee5..8cd973c 100644
--- a/core-ui/java/android/support/v4/view/ViewPager.java
+++ b/core-ui/java/android/support/v4/view/ViewPager.java
@@ -2749,10 +2749,18 @@
if (event.getAction() == KeyEvent.ACTION_DOWN) {
switch (event.getKeyCode()) {
case KeyEvent.KEYCODE_DPAD_LEFT:
- handled = arrowScroll(FOCUS_LEFT);
+ if (event.hasModifiers(KeyEvent.META_ALT_ON)) {
+ handled = pageLeft();
+ } else {
+ handled = arrowScroll(FOCUS_LEFT);
+ }
break;
case KeyEvent.KEYCODE_DPAD_RIGHT:
- handled = arrowScroll(FOCUS_RIGHT);
+ if (event.hasModifiers(KeyEvent.META_ALT_ON)) {
+ handled = pageRight();
+ } else {
+ handled = arrowScroll(FOCUS_RIGHT);
+ }
break;
case KeyEvent.KEYCODE_TAB:
if (event.hasNoModifiers()) {
diff --git a/core-ui/tests/java/android/support/v4/view/BaseViewPagerTest.java b/core-ui/tests/java/android/support/v4/view/BaseViewPagerTest.java
index a83a976..1544e76 100644
--- a/core-ui/tests/java/android/support/v4/view/BaseViewPagerTest.java
+++ b/core-ui/tests/java/android/support/v4/view/BaseViewPagerTest.java
@@ -16,6 +16,7 @@
package android.support.v4.view;
import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.action.ViewActions.pressKey;
import static android.support.test.espresso.action.ViewActions.swipeLeft;
import static android.support.test.espresso.action.ViewActions.swipeRight;
import static android.support.test.espresso.assertion.PositionAssertions.isBelow;
@@ -46,6 +47,7 @@
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.allOf;
+import static org.hamcrest.Matchers.is;
import static org.hamcrest.core.IsNot.not;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -61,14 +63,18 @@
import android.graphics.Color;
import android.support.coreui.test.R;
import android.support.test.espresso.ViewAction;
+import android.support.test.espresso.action.EspressoKey;
import android.support.test.filters.LargeTest;
import android.support.test.filters.MediumTest;
import android.support.v4.BaseInstrumentationTestCase;
import android.support.v4.testutils.TestUtilsMatchers;
import android.text.TextUtils;
import android.util.Pair;
+import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.LinearLayout;
import android.widget.TextView;
import org.junit.After;
@@ -202,6 +208,43 @@
}
}
+ protected static class ButtonPagerAdapter extends BasePagerAdapter<Integer> {
+ private ArrayList<Button[]> mButtons = new ArrayList<>();
+
+ @Override
+ public void add(String title, Integer content) {
+ super.add(title, content);
+ mButtons.add(new Button[3]);
+ }
+
+ @Override
+ public Object instantiateItem(ViewGroup container, int position) {
+ final LinearLayout view = new LinearLayout(container.getContext());
+ view.setBackgroundColor(mEntries.get(position).second);
+ view.setOrientation(LinearLayout.HORIZONTAL);
+ configureInstantiatedItem(view, position);
+
+ for (int i = 0; i < 3; ++i) {
+ Button but = new Button(container.getContext());
+ but.setText("" + i);
+ but.setFocusableInTouchMode(true);
+ view.addView(but, ViewGroup.LayoutParams.WRAP_CONTENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT);
+ mButtons.get(position)[i] = but;
+ }
+
+ // Unlike ListView adapters, the ViewPager adapter is responsible
+ // for adding the view to the container.
+ container.addView(view);
+
+ return new ViewHolder(view, position);
+ }
+
+ public View getButton(int page, int idx) {
+ return mButtons.get(page)[idx];
+ }
+ }
+
public BaseViewPagerTest(Class<T> activityClass) {
super(activityClass);
}
@@ -1031,4 +1074,33 @@
// Swipe one more page to the right
verifyScrollCallbacksToLowerPage(wrap(swipeRight()), 0);
}
+
+ @Test
+ @MediumTest
+ public void testKeyboardNavigation() {
+ ButtonPagerAdapter adapter = new ButtonPagerAdapter();
+ adapter.add("Red", Color.RED);
+ adapter.add("Green", Color.GREEN);
+ adapter.add("Blue", Color.BLUE);
+ onView(withId(R.id.pager)).perform(setAdapter(adapter), scrollToPage(0, false));
+ View firstButton = adapter.getButton(0, 0);
+ firstButton.requestFocus();
+ assertTrue(firstButton.isFocused());
+ assertEquals(0, mViewPager.getCurrentItem());
+
+ // Normal arrows should traverse contents first
+ onView(is(firstButton)).perform(pressKey(KeyEvent.KEYCODE_DPAD_RIGHT));
+ assertEquals(0, mViewPager.getCurrentItem());
+ assertTrue(adapter.getButton(0, 1).isFocused());
+
+ // Alt arrows should change page even if there are more focusables in that direction
+ onView(is(adapter.getButton(0, 1))).perform(pressKey(new EspressoKey.Builder()
+ .withAltPressed(true).withKeyCode(KeyEvent.KEYCODE_DPAD_RIGHT).build()));
+ assertEquals(1, mViewPager.getCurrentItem());
+ assertTrue(adapter.getButton(1, 0).isFocused());
+
+ // Normal arrows should change page if there are no more focusables in that direction
+ onView(is(adapter.getButton(1, 0))).perform(pressKey(KeyEvent.KEYCODE_DPAD_LEFT));
+ assertEquals(0, mViewPager.getCurrentItem());
+ }
}