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 package com.android.documentsui;
17 
18 import static com.android.documentsui.base.SharedMinimal.DEBUG;
19 
20 import android.util.Log;
21 import android.view.KeyEvent;
22 
23 import androidx.recyclerview.selection.SelectionTracker;
24 
25 import com.android.documentsui.base.Events;
26 import com.android.documentsui.base.Features;
27 import com.android.documentsui.base.Procedure;
28 import com.android.documentsui.dirlist.FocusHandler;
29 
30 /**
31  * Handle common input events.
32  */
33 public class SharedInputHandler {
34 
35     private static final String TAG = "SharedInputHandler";
36 
37     private final FocusHandler mFocusManager;
38     private final Procedure mSearchCanceler;
39     private final Procedure mDirPopper;
40     private final Runnable mSearchExecutor;
41     private final Features mFeatures;
42     private final SelectionTracker<String> mSelectionMgr;
43     private final DrawerController mDrawer;
44 
SharedInputHandler( FocusHandler focusHandler, SelectionTracker<String> selectionMgr, Procedure searchCanceler, Procedure dirPopper, Features features, DrawerController drawer, Runnable searchExcutor)45     public SharedInputHandler(
46             FocusHandler focusHandler,
47             SelectionTracker<String> selectionMgr,
48             Procedure searchCanceler,
49             Procedure dirPopper,
50             Features features,
51             DrawerController drawer,
52             Runnable searchExcutor) {
53         mFocusManager = focusHandler;
54         mSearchCanceler = searchCanceler;
55         mSelectionMgr = selectionMgr;
56         mDirPopper = dirPopper;
57         mFeatures = features;
58         mDrawer = drawer;
59         mSearchExecutor = searchExcutor;
60     }
61 
onKeyDown(int keyCode, KeyEvent event)62     public boolean onKeyDown(int keyCode, KeyEvent event) {
63         switch (keyCode) {
64             // Unhandled ESC keys end up being rethrown back at us as BACK keys. So by returning
65             // true, we make sure it always does no-op.
66             case KeyEvent.KEYCODE_ESCAPE:
67                 return onEscape();
68 
69             case KeyEvent.KEYCODE_DEL:
70                 return onDelete();
71 
72             // This is the Android back button, not backspace.
73             case KeyEvent.KEYCODE_BACK:
74                 return onBack();
75 
76             case KeyEvent.KEYCODE_TAB:
77                 return onTab();
78 
79             case KeyEvent.KEYCODE_SEARCH:
80                 mSearchExecutor.run();
81                 return true;
82 
83             default:
84                 // Instead of duplicating the switch-case in #isNavigationKeyCode, best just to
85                 // leave it here.
86                 if (Events.isNavigationKeyCode(keyCode)) {
87                     // Forward all unclaimed navigation keystrokes to the directory list.
88                     // This causes any stray navigation keystrokes to focus the content pane,
89                     // which is probably what the user is trying to do.
90                     mFocusManager.focusDirectoryList();
91                     return true;
92                 }
93                 return false;
94         }
95     }
96 
onTab()97     private boolean onTab() {
98         if (!mFeatures.isSystemKeyboardNavigationEnabled()) {
99             // Tab toggles focus on the navigation drawer.
100             // This should only be called in pre-O devices, since O has built-in keyboard
101             // navigation
102             // support.
103             mFocusManager.advanceFocusArea();
104             return true;
105         }
106 
107         return false;
108     }
109 
onDelete()110     private boolean onDelete() {
111         mDirPopper.run();
112         return true;
113     }
114 
onBack()115     private boolean onBack() {
116         if (mDrawer.isPresent() && mDrawer.isOpen()) {
117             mDrawer.setOpen(false);
118             return true;
119         }
120 
121         if (mSearchCanceler.run()) {
122             return true;
123         }
124 
125         if (mSelectionMgr.hasSelection()) {
126             if (DEBUG) {
127                 Log.d(TAG, "Back pressed. Clearing existing selection.");
128             }
129             mSelectionMgr.clearSelection();
130             return true;
131         }
132 
133         return mDirPopper.run();
134     }
135 
onEscape()136     private boolean onEscape() {
137         if (mSearchCanceler.run()) {
138             return true;
139         }
140 
141         if (mSelectionMgr.hasSelection()) {
142             if (DEBUG) {
143                 Log.d(TAG, "ESC pressed. Clearing existing selection.");
144             }
145             mSelectionMgr.clearSelection();
146             return true;
147         }
148 
149         return true;
150     }
151 }
152