• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 android.view.KeyEvent;
19 
20 import com.android.documentsui.base.Events;
21 import com.android.documentsui.selection.ItemDetailsLookup;
22 import com.android.documentsui.selection.MotionInputHandler;
23 import com.android.documentsui.selection.SelectionHelper;
24 import com.android.documentsui.selection.ItemDetailsLookup.ItemDetails;
25 import com.android.documentsui.selection.MotionInputHandler.Callbacks;
26 import com.android.documentsui.selection.SelectionHelper.SelectionPredicate;
27 
28 import javax.annotation.Nullable;
29 
30 /**
31  * Class that handles keyboard events on RecyclerView items. The input handler
32  * must be attached directly to a RecyclerView item since, unlike DOM, events
33  * don't appear bubble up.
34  */
35 public final class KeyInputHandler extends KeyboardEventListener {
36 
37     private final SelectionHelper mSelectionHelper;
38     private final SelectionPredicate mSelectionPredicate;
39     private final Callbacks mCallbacks;
40 
KeyInputHandler( SelectionHelper selectionHelper, SelectionPredicate selectionPredicate, Callbacks callbacks)41     public KeyInputHandler(
42             SelectionHelper selectionHelper,
43             SelectionPredicate selectionPredicate,
44             Callbacks callbacks) {
45 
46         mSelectionHelper = selectionHelper;
47         mSelectionPredicate = selectionPredicate;
48         mCallbacks = callbacks;
49     }
50 
51     @Override
onKey(@ullable ItemDetails details, int keyCode, KeyEvent event)52     public boolean onKey(@Nullable ItemDetails details, int keyCode, KeyEvent event) {
53         // Only handle key-down events. This is simpler, consistent with most other UIs, and
54         // enables the handling of repeated key events from holding down a key.
55         if (event.getAction() != KeyEvent.ACTION_DOWN) {
56             return false;
57         }
58 
59         // Ignore tab key events.  Those should be handled by the top-level key handler.
60         if (keyCode == KeyEvent.KEYCODE_TAB) {
61             return false;
62         }
63 
64         // Ignore events sent to Addon Holders.
65         if (details != null) {
66             int itemType = details.getItemViewType();
67             if (itemType == DocumentsAdapter.ITEM_TYPE_HEADER_MESSAGE
68                     || itemType == DocumentsAdapter.ITEM_TYPE_INFLATED_MESSAGE
69                     || itemType == DocumentsAdapter.ITEM_TYPE_SECTION_BREAK) {
70                 return false;
71             }
72         }
73 
74         if (mCallbacks.onFocusItem(details, keyCode, event)) {
75             // Handle range selection adjustments. Extending the selection will adjust the
76             // bounds of the in-progress range selection. Each time an unshifted navigation
77             // event is received, the range selection is restarted.
78             if (shouldExtendSelection(details, event)) {
79                 if (!mSelectionHelper.isRangeActive()) {
80                     // Start a range selection if one isn't active
81                     mSelectionHelper.startRange(details.getPosition());
82                 }
83                 mSelectionHelper.extendRange(details.getPosition());
84             } else {
85                 mSelectionHelper.endRange();
86                 mSelectionHelper.clearSelection();
87             }
88             return true;
89         }
90 
91         // we don't yet have a mechanism to handle opening/previewing multiple documents at once
92         if (mSelectionHelper.getSelection().size() > 1) {
93             return false;
94         }
95 
96         return mCallbacks.onItemActivated(details, event);
97     }
98 
shouldExtendSelection(ItemDetails item, KeyEvent event)99     private boolean shouldExtendSelection(ItemDetails item, KeyEvent event) {
100         if (!Events.isNavigationKeyCode(event.getKeyCode()) || !event.isShiftPressed()) {
101             return false;
102         }
103 
104         return mSelectionPredicate.canSetStateForId(item.getStableId(), true);
105     }
106 
107     public static abstract class Callbacks extends MotionInputHandler.Callbacks {
isInteractiveItem(ItemDetails item, KeyEvent e)108         public abstract boolean isInteractiveItem(ItemDetails item, KeyEvent e);
onItemActivated(ItemDetails item, KeyEvent e)109         public abstract boolean onItemActivated(ItemDetails item, KeyEvent e);
onFocusItem(ItemDetails details, int keyCode, KeyEvent event)110         public boolean onFocusItem(ItemDetails details, int keyCode, KeyEvent event) {
111             return true;
112         }
113     }
114 }