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 package com.android.documentsui.dirlist; 17 18 import static android.support.v4.util.Preconditions.checkArgument; 19 20 import android.support.v7.widget.RecyclerView; 21 import android.support.v7.widget.RecyclerView.ViewHolder; 22 import android.view.HapticFeedbackConstants; 23 import android.view.KeyEvent; 24 import android.view.MotionEvent; 25 26 import com.android.documentsui.ActionHandler; 27 import com.android.documentsui.base.EventHandler; 28 import com.android.documentsui.base.State; 29 import com.android.documentsui.selection.GestureSelectionHelper; 30 import com.android.documentsui.selection.ItemDetailsLookup; 31 import com.android.documentsui.selection.MouseInputHandler; 32 import com.android.documentsui.selection.SelectionHelper; 33 import com.android.documentsui.selection.TouchInputHandler; 34 import com.android.documentsui.selection.ItemDetailsLookup.ItemDetails; 35 import com.android.documentsui.selection.SelectionHelper.SelectionPredicate; 36 37 /** 38 * Helper class dedicated to building gesture input handlers. The construction 39 * of the various input handlers is non trivial. To keep logic clear, 40 * code flexible, and DirectoryFragment small(er), the construction has been 41 * isolated here in a separate class. 42 */ 43 final class InputHandlers { 44 45 private ActionHandler mActions; 46 private SelectionHelper mSelectionHelper; 47 private SelectionPredicate mSelectionPredicate; 48 private ItemDetailsLookup mDetailsLookup; 49 private FocusHandler mFocusHandler; 50 private RecyclerView mRecView; 51 private State mState; 52 InputHandlers( ActionHandler actions, SelectionHelper selectionHelper, SelectionPredicate selectionPredicate, ItemDetailsLookup detailsLookup, FocusHandler focusHandler, RecyclerView recView, State state)53 InputHandlers( 54 ActionHandler actions, 55 SelectionHelper selectionHelper, 56 SelectionPredicate selectionPredicate, 57 ItemDetailsLookup detailsLookup, 58 FocusHandler focusHandler, 59 RecyclerView recView, 60 State state) { 61 62 checkArgument(actions != null); 63 checkArgument(selectionHelper != null); 64 checkArgument(selectionPredicate != null); 65 checkArgument(detailsLookup != null); 66 checkArgument(focusHandler != null); 67 checkArgument(recView != null); 68 checkArgument(state != null); 69 70 mActions = actions; 71 mSelectionHelper = selectionHelper; 72 mSelectionPredicate = selectionPredicate; 73 mDetailsLookup = detailsLookup; 74 mFocusHandler = focusHandler; 75 mRecView = recView; 76 mState = state; 77 } 78 createKeyHandler()79 KeyInputHandler createKeyHandler() { 80 KeyInputHandler.Callbacks callbacks = new KeyInputHandler.Callbacks() { 81 @Override 82 public boolean isInteractiveItem(ItemDetails item, KeyEvent e) { 83 switch (item.getItemViewType()) { 84 case DocumentsAdapter.ITEM_TYPE_HEADER_MESSAGE: 85 case DocumentsAdapter.ITEM_TYPE_INFLATED_MESSAGE: 86 case DocumentsAdapter.ITEM_TYPE_SECTION_BREAK: 87 return false; 88 case DocumentsAdapter.ITEM_TYPE_DOCUMENT: 89 case DocumentsAdapter.ITEM_TYPE_DIRECTORY: 90 return true; 91 default: 92 throw new RuntimeException( 93 "Unsupported item type: " + item.getItemViewType()); 94 } 95 } 96 97 @Override 98 public boolean onItemActivated(ItemDetails item, KeyEvent e) { 99 // Handle enter key events 100 switch (e.getKeyCode()) { 101 case KeyEvent.KEYCODE_ENTER: 102 case KeyEvent.KEYCODE_DPAD_CENTER: 103 case KeyEvent.KEYCODE_BUTTON_A: 104 return mActions.openItem( 105 item, 106 ActionHandler.VIEW_TYPE_REGULAR, 107 ActionHandler.VIEW_TYPE_PREVIEW); 108 case KeyEvent.KEYCODE_SPACE: 109 return mActions.openItem( 110 item, 111 ActionHandler.VIEW_TYPE_PREVIEW, 112 ActionHandler.VIEW_TYPE_NONE); 113 } 114 115 return false; 116 } 117 118 @Override 119 public boolean onFocusItem(ItemDetails details, int keyCode, KeyEvent event) { 120 ViewHolder holder = 121 mRecView.findViewHolderForAdapterPosition(details.getPosition()); 122 if (holder instanceof DocumentHolder) { 123 return mFocusHandler.handleKey((DocumentHolder) holder, keyCode, event); 124 } 125 return false; 126 } 127 128 @Override 129 public void onPerformHapticFeedback() { 130 mRecView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); 131 } 132 }; 133 134 return new KeyInputHandler(mSelectionHelper, mSelectionPredicate, callbacks); 135 } 136 createMouseHandler( EventHandler<MotionEvent> showContextMenuCallback)137 MouseInputHandler createMouseHandler( 138 EventHandler<MotionEvent> showContextMenuCallback) { 139 140 checkArgument(showContextMenuCallback != null); 141 142 MouseInputHandler.Callbacks callbacks = new MouseInputHandler.Callbacks() { 143 @Override 144 public boolean onItemActivated(ItemDetails item, MotionEvent e) { 145 return mActions.openItem( 146 item, 147 ActionHandler.VIEW_TYPE_REGULAR, 148 ActionHandler.VIEW_TYPE_PREVIEW); 149 } 150 151 @Override 152 public boolean onContextClick(MotionEvent e) { 153 return showContextMenuCallback.accept(e); 154 } 155 156 @Override 157 public void onPerformHapticFeedback() { 158 mRecView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); 159 } 160 161 @Override 162 public void focusItem(ItemDetails item) { 163 mFocusHandler.focusDocument(item.getStableId()); 164 } 165 166 @Override 167 public void clearFocus() { 168 mFocusHandler.clearFocus(); 169 } 170 171 @Override 172 public boolean hasFocusedItem() { 173 return mFocusHandler.hasFocusedItem(); 174 } 175 176 @Override 177 public int getFocusedPosition() { 178 return mFocusHandler.getFocusPosition(); 179 } 180 }; 181 182 return new MouseInputHandler(mSelectionHelper, mDetailsLookup, callbacks); 183 } 184 185 /** 186 * Factory method for input touch delegate. Exists to reduce complexity in the 187 * calling scope. 188 * @param gestureHelper 189 */ createTouchHandler( GestureSelectionHelper gestureHelper, DragStartListener dragStartListener)190 TouchInputHandler createTouchHandler( 191 GestureSelectionHelper gestureHelper, DragStartListener dragStartListener) { 192 checkArgument(dragStartListener != null); 193 194 TouchInputHandler.Callbacks callbacks = new TouchInputHandler.Callbacks() { 195 @Override 196 public boolean onItemActivated(ItemDetails item, MotionEvent e) { 197 return mActions.openItem( 198 item, 199 ActionHandler.VIEW_TYPE_PREVIEW, 200 ActionHandler.VIEW_TYPE_REGULAR); 201 } 202 203 @Override 204 public boolean onDragInitiated(MotionEvent e) { 205 return dragStartListener.onTouchDragEvent(e); 206 } 207 208 @Override 209 public void onPerformHapticFeedback() { 210 mRecView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); 211 } 212 213 @Override 214 public void focusItem(ItemDetails item) { 215 mFocusHandler.focusDocument(item.getStableId()); 216 } 217 218 @Override 219 public void clearFocus() { 220 mFocusHandler.clearFocus(); 221 } 222 }; 223 224 return new TouchInputHandler( 225 mSelectionHelper, mDetailsLookup, mSelectionPredicate, gestureHelper, callbacks); 226 } 227 } 228