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