1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.documentsui.dirlist;
18 
19 import android.os.Bundle;
20 import android.support.v4.view.AccessibilityDelegateCompat;
21 import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
22 import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat;
23 import android.support.v7.widget.RecyclerView;
24 import android.support.v7.widget.RecyclerViewAccessibilityDelegate;
25 import android.view.View;
26 
27 import java.util.function.Function;
28 
29 /**
30  * Custom Accessibility Delegate for RecyclerViews to route click events on its child views to
31  * proper handlers, and to surface selection state to a11y events.
32  * <p>
33  * The majority of event handling isdone using TouchDetector instead of View.OnCLickListener, which
34  * most a11y services use to understand whether a particular view is clickable or not. Thus, we need
35  * to use a custom accessibility delegate to manually add ACTION_CLICK to clickable child views'
36  * accessibility node, and then correctly route these clicks done by a11y services to responsible
37  * click callbacks.
38  * <p>
39  * DocumentsUI uses {@link View#setActivated(boolean)} instead of {@link View#setSelected(boolean)}
40  * for marking a view as selected. We will surface that selection state to a11y services in this
41  * class.
42  */
43 public class AccessibilityEventRouter extends RecyclerViewAccessibilityDelegate {
44 
45     private final ItemDelegate mItemDelegate;
46     private final Function<View, Boolean> mClickCallback;
47 
AccessibilityEventRouter( RecyclerView recyclerView, Function<View, Boolean> clickCallback)48     public AccessibilityEventRouter(
49             RecyclerView recyclerView, Function<View, Boolean> clickCallback) {
50         super(recyclerView);
51         mClickCallback = clickCallback;
52         mItemDelegate = new ItemDelegate(this) {
53             @Override
54             public void onInitializeAccessibilityNodeInfo(View host,
55                     AccessibilityNodeInfoCompat info) {
56                 super.onInitializeAccessibilityNodeInfo(host, info);
57                 info.addAction(AccessibilityActionCompat.ACTION_CLICK);
58                 info.setSelected(host.isActivated());
59             }
60 
61             @Override
62             public boolean performAccessibilityAction(View host, int action, Bundle args) {
63                 // We are only handling click events; route all other to default implementation
64                 if (action == AccessibilityNodeInfoCompat.ACTION_CLICK) {
65                     return mClickCallback.apply(host);
66                 }
67                 return super.performAccessibilityAction(host, action, args);
68             }
69         };
70     }
71 
72     @Override
getItemDelegate()73     public AccessibilityDelegateCompat getItemDelegate() {
74         return mItemDelegate;
75     }
76 }