Loading src/com/android/documentsui/HorizontalBreadcrumb.java +2 −2 Original line number Diff line number Diff line Loading @@ -32,7 +32,7 @@ import com.android.documentsui.NavigationViewManager.Breadcrumb; import com.android.documentsui.NavigationViewManager.Environment; import com.android.documentsui.base.DocumentInfo; import com.android.documentsui.base.RootInfo; import com.android.documentsui.dirlist.AccessibilityClickEventRouter; import com.android.documentsui.dirlist.AccessibilityEventRouter; import java.util.function.Consumer; import java.util.function.IntConsumer; Loading Loading @@ -76,7 +76,7 @@ public final class HorizontalBreadcrumb extends RecyclerView // accessibility delegate to route click events correctly. See AccessibilityClickEventRouter // for more details on how we are routing these a11y events. setAccessibilityDelegateCompat( new AccessibilityClickEventRouter(this, new AccessibilityEventRouter(this, (View child) -> onAccessibilityClick(child))); setLayoutManager(mLayoutManager); Loading src/com/android/documentsui/dirlist/AccessibilityClickEventRouter.java→src/com/android/documentsui/dirlist/AccessibilityEventRouter.java +13 −8 Original line number Diff line number Diff line Loading @@ -28,20 +28,24 @@ import java.util.function.Function; /** * Custom Accessibility Delegate for RecyclerViews to route click events on its child views to * proper handlers. * * The majority of event handling is done using TouchDetector instead of View.OnCLickListener, * which most a11y services use to understand whether a particular view is clickable or not. * Thus, we need to use a custom accessibility delegate to manually add ACTION_CLICK to clickable child * views' accessibility node, and then correctly route these clicks done by a11y services to responsible * proper handlers, and to surface selection state to a11y events. * <p> * The majority of event handling isdone using TouchDetector instead of View.OnCLickListener, which * most a11y services use to understand whether a particular view is clickable or not. Thus, we need * to use a custom accessibility delegate to manually add ACTION_CLICK to clickable child views' * accessibility node, and then correctly route these clicks done by a11y services to responsible * click callbacks. * <p> * DocumentsUI uses {@link View#setActivated(boolean)} instead of {@link View#setSelected(boolean)} * for marking a view as selected. We will surface that selection state to a11y services in this * class. */ public class AccessibilityClickEventRouter extends RecyclerViewAccessibilityDelegate { public class AccessibilityEventRouter extends RecyclerViewAccessibilityDelegate { private final ItemDelegate mItemDelegate; private final Function<View, Boolean> mClickCallback; public AccessibilityClickEventRouter( public AccessibilityEventRouter( RecyclerView recyclerView, Function<View, Boolean> clickCallback) { super(recyclerView); mClickCallback = clickCallback; Loading @@ -51,6 +55,7 @@ public class AccessibilityClickEventRouter extends RecyclerViewAccessibilityDele AccessibilityNodeInfoCompat info) { super.onInitializeAccessibilityNodeInfo(host, info); info.addAction(AccessibilityActionCompat.ACTION_CLICK); info.setSelected(host.isActivated()); } @Override Loading src/com/android/documentsui/dirlist/DirectoryFragment.java +1 −1 Original line number Diff line number Diff line Loading @@ -315,7 +315,7 @@ public class DirectoryFragment extends Fragment mActions = mInjector.getActionHandler(mModel); mRecView.setAccessibilityDelegateCompat( new AccessibilityClickEventRouter(mRecView, new AccessibilityEventRouter(mRecView, (View child) -> onAccessibilityClick(child))); mSelectionMetadata = new SelectionMetadata(mModel::getItem); mSelectionMgr.addItemCallback(mSelectionMetadata); Loading tests/common/com/android/documentsui/testing/Views.java +7 −0 Original line number Diff line number Diff line Loading @@ -44,6 +44,13 @@ public final class Views { return view; } public static View createTestView(boolean activated) { View view = createTestView(); Mockito.when(view.isActivated()).thenReturn(activated); return view; } public static void setBackground(View testView, Drawable background) { Mockito.when(testView.getBackground()).thenReturn(background); } Loading tests/unit/com/android/documentsui/dirlist/AccessibilityTest.java 0 → 100644 +63 −0 Original line number Diff line number Diff line /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.documentsui.dirlist; import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.SmallTest; import android.view.View; import android.view.accessibility.AccessibilityNodeInfo; import com.android.documentsui.testing.TestRecyclerView; import com.android.documentsui.testing.Views; import java.util.List; @SmallTest public class AccessibilityTest extends AndroidTestCase { private static final List<String> ITEMS = TestData.create(10); private TestRecyclerView mView; private AccessibilityEventRouter mAccessibilityDelegate; private boolean mCallbackCalled = false; @Override public void setUp() throws Exception { mView = TestRecyclerView.create(ITEMS); mAccessibilityDelegate = new AccessibilityEventRouter(mView, (View v) -> { mCallbackCalled = true; return true; }); mView.setAccessibilityDelegateCompat(mAccessibilityDelegate); } public void test_announceSelected() throws Exception { View item = Views.createTestView(true); AccessibilityNodeInfoCompat info = new AccessibilityNodeInfoCompat(AccessibilityNodeInfo.obtain()); mAccessibilityDelegate.getItemDelegate().onInitializeAccessibilityNodeInfo(item, info); assertTrue(info.isSelected()); } public void test_routesAccessibilityClicks() throws Exception { View item = Views.createTestView(true); AccessibilityNodeInfoCompat info = new AccessibilityNodeInfoCompat(AccessibilityNodeInfo.obtain()); mAccessibilityDelegate.getItemDelegate().onInitializeAccessibilityNodeInfo(item, info); mAccessibilityDelegate.getItemDelegate().performAccessibilityAction(item, AccessibilityNodeInfoCompat.ACTION_CLICK, null); assertTrue(mCallbackCalled); } } Loading
src/com/android/documentsui/HorizontalBreadcrumb.java +2 −2 Original line number Diff line number Diff line Loading @@ -32,7 +32,7 @@ import com.android.documentsui.NavigationViewManager.Breadcrumb; import com.android.documentsui.NavigationViewManager.Environment; import com.android.documentsui.base.DocumentInfo; import com.android.documentsui.base.RootInfo; import com.android.documentsui.dirlist.AccessibilityClickEventRouter; import com.android.documentsui.dirlist.AccessibilityEventRouter; import java.util.function.Consumer; import java.util.function.IntConsumer; Loading Loading @@ -76,7 +76,7 @@ public final class HorizontalBreadcrumb extends RecyclerView // accessibility delegate to route click events correctly. See AccessibilityClickEventRouter // for more details on how we are routing these a11y events. setAccessibilityDelegateCompat( new AccessibilityClickEventRouter(this, new AccessibilityEventRouter(this, (View child) -> onAccessibilityClick(child))); setLayoutManager(mLayoutManager); Loading
src/com/android/documentsui/dirlist/AccessibilityClickEventRouter.java→src/com/android/documentsui/dirlist/AccessibilityEventRouter.java +13 −8 Original line number Diff line number Diff line Loading @@ -28,20 +28,24 @@ import java.util.function.Function; /** * Custom Accessibility Delegate for RecyclerViews to route click events on its child views to * proper handlers. * * The majority of event handling is done using TouchDetector instead of View.OnCLickListener, * which most a11y services use to understand whether a particular view is clickable or not. * Thus, we need to use a custom accessibility delegate to manually add ACTION_CLICK to clickable child * views' accessibility node, and then correctly route these clicks done by a11y services to responsible * proper handlers, and to surface selection state to a11y events. * <p> * The majority of event handling isdone using TouchDetector instead of View.OnCLickListener, which * most a11y services use to understand whether a particular view is clickable or not. Thus, we need * to use a custom accessibility delegate to manually add ACTION_CLICK to clickable child views' * accessibility node, and then correctly route these clicks done by a11y services to responsible * click callbacks. * <p> * DocumentsUI uses {@link View#setActivated(boolean)} instead of {@link View#setSelected(boolean)} * for marking a view as selected. We will surface that selection state to a11y services in this * class. */ public class AccessibilityClickEventRouter extends RecyclerViewAccessibilityDelegate { public class AccessibilityEventRouter extends RecyclerViewAccessibilityDelegate { private final ItemDelegate mItemDelegate; private final Function<View, Boolean> mClickCallback; public AccessibilityClickEventRouter( public AccessibilityEventRouter( RecyclerView recyclerView, Function<View, Boolean> clickCallback) { super(recyclerView); mClickCallback = clickCallback; Loading @@ -51,6 +55,7 @@ public class AccessibilityClickEventRouter extends RecyclerViewAccessibilityDele AccessibilityNodeInfoCompat info) { super.onInitializeAccessibilityNodeInfo(host, info); info.addAction(AccessibilityActionCompat.ACTION_CLICK); info.setSelected(host.isActivated()); } @Override Loading
src/com/android/documentsui/dirlist/DirectoryFragment.java +1 −1 Original line number Diff line number Diff line Loading @@ -315,7 +315,7 @@ public class DirectoryFragment extends Fragment mActions = mInjector.getActionHandler(mModel); mRecView.setAccessibilityDelegateCompat( new AccessibilityClickEventRouter(mRecView, new AccessibilityEventRouter(mRecView, (View child) -> onAccessibilityClick(child))); mSelectionMetadata = new SelectionMetadata(mModel::getItem); mSelectionMgr.addItemCallback(mSelectionMetadata); Loading
tests/common/com/android/documentsui/testing/Views.java +7 −0 Original line number Diff line number Diff line Loading @@ -44,6 +44,13 @@ public final class Views { return view; } public static View createTestView(boolean activated) { View view = createTestView(); Mockito.when(view.isActivated()).thenReturn(activated); return view; } public static void setBackground(View testView, Drawable background) { Mockito.when(testView.getBackground()).thenReturn(background); } Loading
tests/unit/com/android/documentsui/dirlist/AccessibilityTest.java 0 → 100644 +63 −0 Original line number Diff line number Diff line /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.documentsui.dirlist; import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.SmallTest; import android.view.View; import android.view.accessibility.AccessibilityNodeInfo; import com.android.documentsui.testing.TestRecyclerView; import com.android.documentsui.testing.Views; import java.util.List; @SmallTest public class AccessibilityTest extends AndroidTestCase { private static final List<String> ITEMS = TestData.create(10); private TestRecyclerView mView; private AccessibilityEventRouter mAccessibilityDelegate; private boolean mCallbackCalled = false; @Override public void setUp() throws Exception { mView = TestRecyclerView.create(ITEMS); mAccessibilityDelegate = new AccessibilityEventRouter(mView, (View v) -> { mCallbackCalled = true; return true; }); mView.setAccessibilityDelegateCompat(mAccessibilityDelegate); } public void test_announceSelected() throws Exception { View item = Views.createTestView(true); AccessibilityNodeInfoCompat info = new AccessibilityNodeInfoCompat(AccessibilityNodeInfo.obtain()); mAccessibilityDelegate.getItemDelegate().onInitializeAccessibilityNodeInfo(item, info); assertTrue(info.isSelected()); } public void test_routesAccessibilityClicks() throws Exception { View item = Views.createTestView(true); AccessibilityNodeInfoCompat info = new AccessibilityNodeInfoCompat(AccessibilityNodeInfo.obtain()); mAccessibilityDelegate.getItemDelegate().onInitializeAccessibilityNodeInfo(item, info); mAccessibilityDelegate.getItemDelegate().performAccessibilityAction(item, AccessibilityNodeInfoCompat.ACTION_CLICK, null); assertTrue(mCallbackCalled); } }