1/*
2 * Copyright (C) 2016 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 com.android.documentsui.dirlist;
18
19import static org.junit.Assert.assertFalse;
20import static org.junit.Assert.assertTrue;
21
22import android.support.test.filters.SmallTest;
23import android.support.test.runner.AndroidJUnit4;
24import android.support.v7.widget.RecyclerView;
25import android.view.MotionEvent;
26
27import com.android.documentsui.base.Events.InputEvent;
28import com.android.documentsui.selection.SelectionManager;
29import com.android.documentsui.selection.SelectionProbe;
30import com.android.documentsui.testing.SelectionManagers;
31import com.android.documentsui.testing.TestActionHandler;
32import com.android.documentsui.testing.TestEvent;
33import com.android.documentsui.testing.TestEvent.Builder;
34import com.android.documentsui.testing.TestEventHandler;
35import com.android.documentsui.testing.TestPredicate;
36
37import org.junit.Before;
38import org.junit.Test;
39import org.junit.runner.RunWith;
40
41import java.util.List;
42
43@RunWith(AndroidJUnit4.class)
44@SmallTest
45public final class UserInputHandler_MouseTest {
46
47    private static final List<String> ITEMS = TestData.create(100);
48
49    private UserInputHandler<TestEvent> mInputHandler;
50    private TestActionHandler mActionHandler;
51    private TestFocusHandler mFocusHandler;
52    private SelectionProbe mSelection;
53    private SelectionManager mSelectionMgr;
54    private TestPredicate<DocumentDetails> mCanSelect;
55    private TestEventHandler<InputEvent> mContextMenuClickHandler;
56    private TestEventHandler<InputEvent> mDragAndDropHandler;
57    private TestEventHandler<InputEvent> mGestureSelectHandler;
58    private TestEventHandler<Void> mPerformHapticFeedback;
59
60    private Builder mEvent;
61
62    @Before
63    public void setUp() {
64
65        mSelectionMgr = SelectionManagers.createTestInstance(ITEMS);
66        mActionHandler = new TestActionHandler();
67
68        mSelection = new SelectionProbe(mSelectionMgr);
69        mCanSelect = new TestPredicate<>();
70        mContextMenuClickHandler = new TestEventHandler<>();
71        mDragAndDropHandler = new TestEventHandler<>();
72        mGestureSelectHandler = new TestEventHandler<>();
73        mFocusHandler = new TestFocusHandler();
74
75        mInputHandler = new UserInputHandler<>(
76                mActionHandler,
77                mFocusHandler,
78                mSelectionMgr,
79                (MotionEvent event) -> {
80                    throw new UnsupportedOperationException("Not exercised in tests.");
81                },
82                mCanSelect,
83                mContextMenuClickHandler::accept,
84                mDragAndDropHandler::accept,
85                mGestureSelectHandler::accept,
86                () -> mPerformHapticFeedback.accept(null));
87
88        mEvent = TestEvent.builder().mouse().overDocIcon();
89    }
90
91    @Test
92    public void testConfirmedClick_StartsSelection() {
93        mInputHandler.onSingleTapConfirmed(mEvent.at(11).build());
94        mSelection.assertSelection(11);
95    }
96
97    @Test
98    public void testClickOnIconWithExistingSelection_AddsToSelection() {
99        mInputHandler.onSingleTapConfirmed(mEvent.at(11).build());
100        mInputHandler.onSingleTapUp(mEvent.at(10).build());
101        mSelection.assertSelected(10, 11);
102    }
103
104    @Test
105    public void testClickOnIconOfSelectedItem_RemovesFromSelection() {
106        mInputHandler.onSingleTapConfirmed(mEvent.at(8).build());
107        mInputHandler.onSingleTapUp(mEvent.at(11).shift().build());
108        mSelection.assertSelected(8, 9, 10, 11);
109
110        mInputHandler.onSingleTapUp(mEvent.at(9).unshift().build());
111        mSelection.assertSelected(8, 10, 11);
112    }
113
114    @Test
115    public void testRightClickDown_StartsContextMenu() {
116        mInputHandler.onDown(mEvent.secondary().build());
117        mContextMenuClickHandler.assertLastArgument(mEvent.secondary().build());
118    }
119
120    @Test
121    public void testAltClickDown_StartsContextMenu() {
122        mInputHandler.onDown(mEvent.primary().alt().build());
123        mContextMenuClickHandler.assertLastArgument(mEvent.primary().alt().build());
124    }
125
126    @Test
127    public void testScroll_shouldTrap() {
128        assertTrue(mInputHandler.onScroll(mEvent.at(0).action(MotionEvent.ACTION_MOVE).primary().build()));
129    }
130
131    @Test
132    public void testScroll_NoTrapForTwoFinger() {
133        assertFalse(mInputHandler.onScroll(mEvent.at(0).action(MotionEvent.ACTION_MOVE).build()));
134    }
135
136    @Test
137    public void testUnconfirmedCtrlClick_AddsToExistingSelection() {
138        mInputHandler.onSingleTapConfirmed(mEvent.at(7).build());
139
140        mInputHandler.onSingleTapUp(mEvent.at(11).ctrl().build());
141        mSelection.assertSelection(7, 11);
142    }
143
144    @Test
145    public void testUnconfirmedShiftClick_ExtendsSelection() {
146        mInputHandler.onSingleTapConfirmed(mEvent.at(7).build());
147
148        mInputHandler.onSingleTapUp(mEvent.at(11).shift().build());
149        mSelection.assertSelection(7, 8, 9, 10, 11);
150    }
151
152    @Test
153    public void testConfirmedShiftClick_ExtendsSelectionFromOriginFocus() {
154        mFocusHandler.focusPos = 7;
155        mFocusHandler.focusModelId = "7";
156        // This is a hack-y test, since the real FocusManager would've set range begin itself.
157        mSelectionMgr.setSelectionRangeBegin(7);
158        mSelection.assertNoSelection();
159
160        mInputHandler.onSingleTapConfirmed(mEvent.at(11).shift().build());
161        mSelection.assertSelection(7, 8, 9, 10, 11);
162    }
163
164    @Test
165    public void testUnconfirmedShiftClick_RotatesAroundOrigin() {
166        mInputHandler.onSingleTapConfirmed(mEvent.at(7).build());
167
168        mInputHandler.onSingleTapUp(mEvent.at(11).shift().build());
169        mSelection.assertSelection(7, 8, 9, 10, 11);
170
171        mInputHandler.onSingleTapUp(mEvent.at(5).shift().build());
172        mSelection.assertSelection(5, 6, 7);
173        mSelection.assertNotSelected(8, 9, 10, 11);
174    }
175
176    @Test
177    public void testUnconfirmedShiftCtrlClick_Combination() {
178        mInputHandler.onSingleTapConfirmed(mEvent.at(7).build());
179
180        mInputHandler.onSingleTapUp(mEvent.at(11).shift().build());
181        mSelection.assertSelection(7, 8, 9, 10, 11);
182
183        mInputHandler.onSingleTapUp(mEvent.at(5).unshift().ctrl().build());
184
185        mSelection.assertSelection(5, 7, 8, 9, 10, 11);
186    }
187
188    @Test
189    public void testUnconfirmedShiftCtrlClick_ShiftTakesPriority() {
190        mInputHandler.onSingleTapConfirmed(mEvent.at(7).build());
191
192        mInputHandler.onSingleTapUp(mEvent.at(11).ctrl().shift().build());
193        mSelection.assertSelection(7, 8, 9, 10, 11);
194    }
195
196    // TODO: Add testSpaceBar_Previews, but we need to set a system property
197    // to have a deterministic state.
198
199    @Test
200    public void testDoubleClick_Opens() {
201        mInputHandler.onDoubleTap(mEvent.at(11).build());
202        mActionHandler.open.assertLastArgument(mEvent.build().getDocumentDetails());
203    }
204
205    @Test
206    public void testMiddleClick_DoesNothing() {
207        mInputHandler.onSingleTapConfirmed(mEvent.at(11).tertiary().build());
208        mSelection.assertNoSelection();
209    }
210
211    @Test
212    public void testClickOff_ClearsSelection() {
213        mInputHandler.onSingleTapConfirmed(mEvent.at(11).build());
214        mInputHandler.onSingleTapUp(mEvent.at(RecyclerView.NO_POSITION).build());
215        mSelection.assertNoSelection();
216    }
217
218    @Test
219    public void testClick_Focuses() {
220        int id = 11;
221        mInputHandler.onSingleTapConfirmed(mEvent.at(id).notOverDocIcon().build());
222        assertTrue(mFocusHandler.getFocusModelId().equals(Integer.toString(id)));
223    }
224
225    @Test
226    public void testClickOff_ClearsFocus() {
227        int id = 11;
228        mInputHandler.onSingleTapConfirmed(mEvent.at(id).notOverDocIcon().build());
229        assertTrue(mFocusHandler.hasFocusedItem());
230        mInputHandler.onSingleTapUp(mEvent.at(RecyclerView.NO_POSITION).build());
231        assertFalse(mFocusHandler.hasFocusedItem());
232    }
233
234    @Test
235    public void testClickOffSelection_RemovesSelectionAndFocuses() {
236        mInputHandler.onSingleTapConfirmed(mEvent.at(1).build());
237        mInputHandler.onSingleTapUp(mEvent.at(5).shift().build());
238        mSelection.assertSelection(1, 2, 3, 4, 5);
239
240        int id = 11;
241        mInputHandler.onSingleTapUp(mEvent.at(id).unshift().notOverDocIcon().build());
242        assertTrue(mFocusHandler.getFocusModelId().equals(Integer.toString(id)));
243        mSelection.assertNoSelection();
244    }
245}
246