1/*
2 * Copyright (C) 2014 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
17package android.support.v7.widget;
18
19import android.support.v4.view.AccessibilityDelegateCompat;
20import android.support.v4.view.accessibility.AccessibilityEventCompat;
21import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
22import android.support.v4.view.accessibility.AccessibilityRecordCompat;
23import android.view.View;
24import android.view.accessibility.AccessibilityEvent;
25
26import java.util.concurrent.atomic.AtomicBoolean;
27
28public class RecyclerViewAccessibilityTest extends BaseRecyclerViewInstrumentationTest {
29    public void testOnInitializeAccessibilityNodeInfo() throws Throwable {
30        for (boolean vBefore : new boolean[]{true, false}) {
31            for (boolean vAfter : new boolean[]{true, false}) {
32                for (boolean hBefore : new boolean[]{true, false}) {
33                    for (boolean hAfter : new boolean[]{true, false}) {
34                        onInitializeAccessibilityNodeInfoTest(vBefore, hBefore,
35                                vAfter, hAfter);
36                        removeRecyclerView();
37                    }
38                }
39            }
40        }
41    }
42    public void onInitializeAccessibilityNodeInfoTest(final boolean verticalScrollBefore,
43            final boolean horizontalScrollBefore, final boolean verticalScrollAfter,
44            final boolean horizontalScrollAfter) throws Throwable {
45        final RecyclerView recyclerView = new RecyclerView(getActivity()) {
46            //@Override
47            public boolean canScrollHorizontally(int direction) {
48                return direction < 0 && horizontalScrollBefore ||
49                        direction > 0 && horizontalScrollAfter;
50            }
51
52            //@Override
53            public boolean canScrollVertically(int direction) {
54                return direction < 0 && verticalScrollBefore ||
55                        direction > 0 && verticalScrollAfter;
56            }
57        };
58        final TestAdapter adapter = new TestAdapter(10);
59        final AtomicBoolean hScrolledBack = new AtomicBoolean(false);
60        final AtomicBoolean vScrolledBack = new AtomicBoolean(false);
61        final AtomicBoolean hScrolledFwd = new AtomicBoolean(false);
62        final AtomicBoolean vScrolledFwd = new AtomicBoolean(false);
63        recyclerView.setAdapter(adapter);
64        recyclerView.setLayoutManager(new TestLayoutManager() {
65
66            @Override
67            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
68                layoutRange(recycler, 0, 5);
69            }
70
71            @Override
72            public RecyclerView.LayoutParams generateDefaultLayoutParams() {
73                return new RecyclerView.LayoutParams(-1, -1);
74            }
75
76            @Override
77            public boolean canScrollVertically() {
78                return verticalScrollAfter || verticalScrollBefore;
79            }
80
81            @Override
82            public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler,
83                    RecyclerView.State state) {
84                if (dx > 0) {
85                    hScrolledFwd.set(true);
86                } else if (dx < 0) {
87                    hScrolledBack.set(true);
88                }
89                return 0;
90            }
91
92            @Override
93            public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler,
94                    RecyclerView.State state) {
95                if (dy > 0) {
96                    vScrolledFwd.set(true);
97                } else if (dy < 0) {
98                    vScrolledBack.set(true);
99                }
100                return 0;
101            }
102
103            @Override
104            public boolean canScrollHorizontally() {
105                return horizontalScrollAfter || horizontalScrollBefore;
106            }
107        });
108        setRecyclerView(recyclerView);
109        final RecyclerViewAccessibilityDelegate delegateCompat = recyclerView
110                .getCompatAccessibilityDelegate();
111        final AccessibilityNodeInfoCompat info = AccessibilityNodeInfoCompat.obtain();
112        runTestOnUiThread(new Runnable() {
113            @Override
114            public void run() {
115                delegateCompat.onInitializeAccessibilityNodeInfo(recyclerView, info);
116            }
117        });
118        assertEquals(horizontalScrollAfter || horizontalScrollBefore
119                || verticalScrollAfter || verticalScrollBefore, info.isScrollable());
120        assertEquals(horizontalScrollBefore || verticalScrollBefore,
121                (info.getActions() & AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD) != 0);
122        assertEquals(horizontalScrollAfter || verticalScrollAfter,
123                (info.getActions() & AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD) != 0);
124        final AccessibilityNodeInfoCompat.CollectionInfoCompat collectionInfo = info
125                .getCollectionInfo();
126        assertNotNull(collectionInfo);
127        if (recyclerView.getLayoutManager().canScrollVertically()) {
128            assertEquals(adapter.getItemCount(), collectionInfo.getRowCount());
129        }
130        if (recyclerView.getLayoutManager().canScrollHorizontally()) {
131            assertEquals(adapter.getItemCount(), collectionInfo.getColumnCount());
132        }
133
134        final AccessibilityEvent event = AccessibilityEvent.obtain();
135        runTestOnUiThread(new Runnable() {
136            @Override
137            public void run() {
138                delegateCompat.onInitializeAccessibilityEvent(recyclerView, event);
139            }
140        });
141        final AccessibilityRecordCompat record = AccessibilityEventCompat
142                .asRecord(event);
143        assertEquals(record.isScrollable(), verticalScrollAfter || horizontalScrollAfter ||
144        verticalScrollBefore || horizontalScrollBefore);
145        assertEquals(record.getItemCount(), adapter.getItemCount());
146
147        getInstrumentation().waitForIdleSync();
148        for (int i = 0; i < mRecyclerView.getChildCount(); i ++) {
149            final View view = mRecyclerView.getChildAt(i);
150            final AccessibilityNodeInfoCompat childInfo = AccessibilityNodeInfoCompat.obtain();
151            runTestOnUiThread(new Runnable() {
152                @Override
153                public void run() {
154                    delegateCompat.getItemDelegate().
155                            onInitializeAccessibilityNodeInfo(view, childInfo);
156                }
157            });
158            final AccessibilityNodeInfoCompat.CollectionItemInfoCompat collectionItemInfo
159                    = childInfo.getCollectionItemInfo();
160            assertNotNull(collectionItemInfo);
161            if (recyclerView.getLayoutManager().canScrollHorizontally()) {
162                assertEquals(i, collectionItemInfo.getColumnIndex());
163            } else {
164                assertEquals(0, collectionItemInfo.getColumnIndex());
165            }
166
167            if (recyclerView.getLayoutManager().canScrollVertically()) {
168                assertEquals(i, collectionItemInfo.getRowIndex());
169            } else {
170                assertEquals(0, collectionItemInfo.getRowIndex());
171            }
172        }
173
174        runTestOnUiThread(new Runnable() {
175            @Override
176            public void run() {
177
178            }
179        });
180        hScrolledBack.set(false);
181        vScrolledBack.set(false);
182        hScrolledFwd.set(false);
183        vScrolledBack.set(false);
184        performAccessibilityAction(delegateCompat, recyclerView,
185                AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD);
186        assertEquals(horizontalScrollBefore, hScrolledBack.get());
187        assertEquals(verticalScrollBefore, vScrolledBack.get());
188        assertEquals(false, hScrolledFwd.get());
189        assertEquals(false, vScrolledFwd.get());
190
191        hScrolledBack.set(false);
192        vScrolledBack.set(false);
193        hScrolledFwd.set(false);
194        vScrolledBack.set(false);
195        performAccessibilityAction(delegateCompat, recyclerView,
196                AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD);
197        assertEquals(false, hScrolledBack.get());
198        assertEquals(false, vScrolledBack.get());
199        assertEquals(horizontalScrollAfter, hScrolledFwd.get());
200        assertEquals(verticalScrollAfter, vScrolledFwd.get());
201    }
202
203    void performAccessibilityAction(final AccessibilityDelegateCompat delegate,
204            final RecyclerView recyclerView,  final int action) throws Throwable {
205        runTestOnUiThread(new Runnable() {
206            @Override
207            public void run() {
208                delegate.performAccessibilityAction(recyclerView, action, null);
209            }
210        });
211        getInstrumentation().waitForIdleSync();
212        Thread.sleep(250);
213    }
214}
215