1 /* 2 * Copyright 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 androidx.recyclerview.selection; 18 19 import static androidx.recyclerview.selection.testing.TestEvents.Mouse.ALT_CLICK; 20 import static androidx.recyclerview.selection.testing.TestEvents.Mouse.CLICK; 21 import static androidx.recyclerview.selection.testing.TestEvents.Mouse.CTRL_CLICK; 22 import static androidx.recyclerview.selection.testing.TestEvents.Mouse.SECONDARY_CLICK; 23 import static androidx.recyclerview.selection.testing.TestEvents.Mouse.SHIFT_CLICK; 24 import static androidx.recyclerview.selection.testing.TestEvents.Mouse.TERTIARY_CLICK; 25 26 import static org.junit.Assert.assertFalse; 27 import static org.junit.Assert.assertTrue; 28 29 import android.view.MotionEvent; 30 31 import androidx.recyclerview.selection.testing.SelectionProbe; 32 import androidx.recyclerview.selection.testing.SelectionTrackers; 33 import androidx.recyclerview.selection.testing.TestAdapter; 34 import androidx.recyclerview.selection.testing.TestData; 35 import androidx.recyclerview.selection.testing.TestEvents; 36 import androidx.recyclerview.selection.testing.TestFocusDelegate; 37 import androidx.recyclerview.selection.testing.TestItemDetails; 38 import androidx.recyclerview.selection.testing.TestItemDetailsLookup; 39 import androidx.recyclerview.selection.testing.TestItemKeyProvider; 40 import androidx.recyclerview.selection.testing.TestOnContextClickListener; 41 import androidx.recyclerview.selection.testing.TestOnItemActivatedListener; 42 import androidx.recyclerview.widget.RecyclerView; 43 import androidx.test.ext.junit.runners.AndroidJUnit4; 44 import androidx.test.filters.SmallTest; 45 46 import org.junit.Before; 47 import org.junit.Test; 48 import org.junit.runner.RunWith; 49 50 @RunWith(AndroidJUnit4.class) 51 @SmallTest 52 public final class MouseInputHandlerTest { 53 54 private MouseInputHandler<String> mInputDelegate; 55 56 private TestOnContextClickListener mMouseCallbacks; 57 private TestOnItemActivatedListener<String> mActivationCallbacks; 58 private TestFocusDelegate<String> mFocusCallbacks; 59 60 private TestItemDetailsLookup mDetailsLookup; 61 private SelectionProbe mSelection; 62 private SelectionTracker<String> mSelectionMgr; 63 64 private TestEvents.Builder mEvent; 65 66 @Before setUp()67 public void setUp() { 68 69 mSelectionMgr = SelectionTrackers.createStringTracker("mouse-input-test", 100); 70 mDetailsLookup = new TestItemDetailsLookup(); 71 mSelection = new SelectionProbe(mSelectionMgr); 72 73 mMouseCallbacks = new TestOnContextClickListener(); 74 mActivationCallbacks = new TestOnItemActivatedListener<>(); 75 mFocusCallbacks = new TestFocusDelegate<>(); 76 77 mInputDelegate = new MouseInputHandler<>( 78 mSelectionMgr, 79 new TestItemKeyProvider<>( 80 ItemKeyProvider.SCOPE_MAPPED, 81 new TestAdapter<>(TestData.createStringData(100))), 82 mDetailsLookup, 83 mMouseCallbacks, 84 mActivationCallbacks, 85 mFocusCallbacks); 86 87 mEvent = TestEvents.builder().mouse(); 88 mDetailsLookup.initAt(RecyclerView.NO_POSITION); 89 } 90 91 @Test testConfirmedClick_StartsSelection()92 public void testConfirmedClick_StartsSelection() { 93 mDetailsLookup.initAt(11).setInItemSelectRegion(true); 94 mInputDelegate.onSingleTapConfirmed(CLICK); 95 96 mSelection.assertSelection(11); 97 } 98 99 @Test testClickOnSelectRegion_AddsToSelection()100 public void testClickOnSelectRegion_AddsToSelection() { 101 mDetailsLookup.initAt(11).setInItemSelectRegion(true); 102 mInputDelegate.onSingleTapConfirmed(CLICK); 103 104 mDetailsLookup.initAt(10).setInItemSelectRegion(true); 105 mInputDelegate.onSingleTapUp(CLICK); 106 107 mSelection.assertSelected(10, 11); 108 } 109 110 @Test testClickOnIconOfSelectedItem_RemovesFromSelection()111 public void testClickOnIconOfSelectedItem_RemovesFromSelection() { 112 mDetailsLookup.initAt(8).setInItemSelectRegion(true); 113 mInputDelegate.onSingleTapConfirmed(CLICK); 114 115 mDetailsLookup.initAt(11); 116 mInputDelegate.onSingleTapUp(SHIFT_CLICK); 117 mSelection.assertSelected(8, 9, 10, 11); 118 119 mDetailsLookup.initAt(9); 120 mInputDelegate.onSingleTapUp(CLICK); 121 mSelection.assertSelected(8, 10, 11); 122 } 123 124 @Test testRightClickDown_StartsContextMenu()125 public void testRightClickDown_StartsContextMenu() { 126 mInputDelegate.onDown(SECONDARY_CLICK); 127 128 mMouseCallbacks.assertLastEvent(SECONDARY_CLICK); 129 } 130 131 @Test testAltClickDown_StartsContextMenu()132 public void testAltClickDown_StartsContextMenu() { 133 mInputDelegate.onDown(ALT_CLICK); 134 135 mMouseCallbacks.assertLastEvent(ALT_CLICK); 136 } 137 138 @Test testScroll_shouldTrap()139 public void testScroll_shouldTrap() { 140 mDetailsLookup.initAt(0); 141 assertTrue(mInputDelegate.onScroll( 142 null, 143 mEvent.action(MotionEvent.ACTION_MOVE).primary().build(), 144 -1, 145 -1)); 146 } 147 148 @Test testScroll_NoTrapForTwoFinger()149 public void testScroll_NoTrapForTwoFinger() { 150 mDetailsLookup.initAt(0); 151 assertFalse(mInputDelegate.onScroll( 152 null, 153 mEvent.action(MotionEvent.ACTION_MOVE).build(), 154 -1, 155 -1)); 156 } 157 158 @Test testUnconfirmedCtrlClick_AddsToExistingSelection()159 public void testUnconfirmedCtrlClick_AddsToExistingSelection() { 160 mDetailsLookup.initAt(7).setInItemSelectRegion(true); 161 mInputDelegate.onSingleTapConfirmed(CLICK); 162 163 mDetailsLookup.initAt(11); 164 mInputDelegate.onSingleTapUp(CTRL_CLICK); 165 166 mSelection.assertSelection(7, 11); 167 } 168 169 @Test testUnconfirmedShiftClick_ExtendsSelection()170 public void testUnconfirmedShiftClick_ExtendsSelection() { 171 mDetailsLookup.initAt(7).setInItemSelectRegion(true); 172 mInputDelegate.onSingleTapConfirmed(CLICK); 173 174 mDetailsLookup.initAt(11); 175 mInputDelegate.onSingleTapUp(SHIFT_CLICK); 176 177 mSelection.assertSelection(7, 8, 9, 10, 11); 178 } 179 180 @Test testConfirmedShiftClick_ExtendsSelectionFromFocus()181 public void testConfirmedShiftClick_ExtendsSelectionFromFocus() { 182 TestItemDetails item = mDetailsLookup.initAt(7); 183 mFocusCallbacks.focusItem(item); 184 185 // There should be no selected item at this point, just focus on "7". 186 mDetailsLookup.initAt(11); 187 mInputDelegate.onSingleTapConfirmed(SHIFT_CLICK); 188 mSelection.assertSelection(7, 8, 9, 10, 11); 189 } 190 191 @Test testUnconfirmedShiftClick_RotatesAroundOrigin()192 public void testUnconfirmedShiftClick_RotatesAroundOrigin() { 193 mDetailsLookup.initAt(7).setInItemSelectRegion(true); 194 mInputDelegate.onSingleTapConfirmed(CLICK); 195 196 mDetailsLookup.initAt(11); 197 mInputDelegate.onSingleTapUp(SHIFT_CLICK); 198 mSelection.assertSelection(7, 8, 9, 10, 11); 199 200 mDetailsLookup.initAt(5); 201 mInputDelegate.onSingleTapUp(SHIFT_CLICK); 202 203 mSelection.assertSelection(5, 6, 7); 204 mSelection.assertNotSelected(8, 9, 10, 11); 205 } 206 207 @Test testUnconfirmedShiftCtrlClick_Combination()208 public void testUnconfirmedShiftCtrlClick_Combination() { 209 mDetailsLookup.initAt(7).setInItemSelectRegion(true); 210 mInputDelegate.onSingleTapConfirmed(CLICK); 211 212 mDetailsLookup.initAt(11); 213 mInputDelegate.onSingleTapUp(SHIFT_CLICK); 214 mSelection.assertSelection(7, 8, 9, 10, 11); 215 216 mDetailsLookup.initAt(5); 217 mInputDelegate.onSingleTapUp(CTRL_CLICK); 218 219 mSelection.assertSelection(5, 7, 8, 9, 10, 11); 220 } 221 222 @Test testUnconfirmedShiftCtrlClick_ShiftTakesPriority()223 public void testUnconfirmedShiftCtrlClick_ShiftTakesPriority() { 224 mDetailsLookup.initAt(7).setInItemSelectRegion(true); 225 mInputDelegate.onSingleTapConfirmed(CLICK); 226 227 mDetailsLookup.initAt(11); 228 mInputDelegate.onSingleTapUp(mEvent.ctrl().shift().build()); 229 230 mSelection.assertSelection(7, 8, 9, 10, 11); 231 } 232 233 // TODO: Add testSpaceBar_Previews, but we need to set a system property 234 // to have a deterministic state. 235 236 @Test testDoubleClick_Opens()237 public void testDoubleClick_Opens() { 238 TestItemDetails doc = mDetailsLookup.initAt(11); 239 mInputDelegate.onDoubleTap(CLICK); 240 241 mActivationCallbacks.assertActivated(doc); 242 } 243 244 @Test testMiddleClick_DoesNothing()245 public void testMiddleClick_DoesNothing() { 246 mDetailsLookup.initAt(11).setInItemSelectRegion(true); 247 mInputDelegate.onSingleTapConfirmed(TERTIARY_CLICK); 248 249 mSelection.assertNoSelection(); 250 } 251 252 @Test testClickOff_ClearsSelection()253 public void testClickOff_ClearsSelection() { 254 mDetailsLookup.initAt(11).setInItemSelectRegion(true); 255 mInputDelegate.onSingleTapConfirmed(CLICK); 256 257 mDetailsLookup.initAt(RecyclerView.NO_POSITION); 258 mInputDelegate.onSingleTapUp(CLICK); 259 260 mSelection.assertNoSelection(); 261 } 262 263 @Test testClick_Focuses()264 public void testClick_Focuses() { 265 mDetailsLookup.initAt(11).setInItemSelectRegion(false); 266 mInputDelegate.onSingleTapConfirmed(CLICK); 267 268 mFocusCallbacks.assertHasFocus(true); 269 mFocusCallbacks.assertFocused("11"); 270 } 271 272 @Test testClickOff_ClearsFocus()273 public void testClickOff_ClearsFocus() { 274 mDetailsLookup.initAt(11).setInItemSelectRegion(false); 275 mInputDelegate.onSingleTapConfirmed(CLICK); 276 mFocusCallbacks.assertHasFocus(true); 277 278 mDetailsLookup.initAt(RecyclerView.NO_POSITION); 279 mInputDelegate.onSingleTapUp(CLICK); 280 mFocusCallbacks.assertHasFocus(false); 281 } 282 283 @Test testClickOffSelection_RemovesSelectionAndFocuses()284 public void testClickOffSelection_RemovesSelectionAndFocuses() { 285 mDetailsLookup.initAt(1).setInItemSelectRegion(true); 286 mInputDelegate.onSingleTapConfirmed(CLICK); 287 288 mDetailsLookup.initAt(5); 289 mInputDelegate.onSingleTapUp(SHIFT_CLICK); 290 291 mSelection.assertSelection(1, 2, 3, 4, 5); 292 293 mDetailsLookup.initAt(11); 294 mInputDelegate.onSingleTapUp(CLICK); 295 296 mFocusCallbacks.assertFocused("11"); 297 mSelection.assertNoSelection(); 298 } 299 } 300