From 4080fddfe7c0d73a176b0e0196d7e028a7582f8f Mon Sep 17 00:00:00 2001 From: Sebastian Franco Date: Tue, 19 Sep 2023 14:17:23 -0700 Subject: [PATCH] Adding a keyboard shortcut delegarte to move logic outside of the launcher Bug: 301142423 Test: compiling, this change is a no-op change Change-Id: I2f5ee35a9ab645c26cc97669f6a745490fdfdb61 --- src/com/android/launcher3/Launcher.java | 98 ++++-------- .../util/KeyboardShortcutsDelegate.java | 145 ++++++++++++++++++ 2 files changed, 177 insertions(+), 66 deletions(-) create mode 100644 src/com/android/launcher3/util/KeyboardShortcutsDelegate.java diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index b8e7737798..c454a910b2 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -43,7 +43,6 @@ import static com.android.launcher3.LauncherState.NO_OFFSET; import static com.android.launcher3.LauncherState.NO_SCALE; import static com.android.launcher3.LauncherState.SPRING_LOADED; import static com.android.launcher3.Utilities.postAsyncCallback; -import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.getSupportedActions; import static com.android.launcher3.config.FeatureFlags.FOLDABLE_SINGLE_PAGE; import static com.android.launcher3.config.FeatureFlags.MULTI_SELECT_EDIT_MODE; import static com.android.launcher3.config.FeatureFlags.SHOW_DOT_PAGINATION; @@ -114,7 +113,6 @@ import android.util.Log; import android.util.SparseArray; import android.view.KeyEvent; import android.view.KeyboardShortcutGroup; -import android.view.KeyboardShortcutInfo; import android.view.LayoutInflater; import android.view.Menu; import android.view.MotionEvent; @@ -137,7 +135,6 @@ import androidx.annotation.UiThread; import androidx.annotation.VisibleForTesting; import com.android.launcher3.DropTarget.DragObject; -import com.android.launcher3.accessibility.BaseAccessibilityDelegate.LauncherAction; import com.android.launcher3.accessibility.LauncherAccessibilityDelegate; import com.android.launcher3.allapps.ActivityAllAppsContainerView; import com.android.launcher3.allapps.AllAppsRecyclerView; @@ -205,6 +202,7 @@ import com.android.launcher3.util.CannedAnimationCoordinator; import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.IntArray; import com.android.launcher3.util.IntSet; +import com.android.launcher3.util.KeyboardShortcutsDelegate; import com.android.launcher3.util.LockedUserState; import com.android.launcher3.util.OnboardingPrefs; import com.android.launcher3.util.PackageUserKey; @@ -335,6 +333,7 @@ public class Launcher extends StatefulActivity private static final boolean DESKTOP_MODE_SUPPORTED = "1".equals(Utilities.getSystemProperty("persist.wm.debug.desktop_mode_2", "0")); + KeyboardShortcutsDelegate mKeyboardShortcutsDelegate = new KeyboardShortcutsDelegate(this); @Thunk Workspace mWorkspace; @Thunk @@ -3183,84 +3182,51 @@ public class Launcher extends StatefulActivity mOverlayManager.dump(prefix, writer); } + /** + * Populates the list of shortcuts. Logic delegated to {@Link KeyboardShortcutsDelegate}. + * + * @param data The data list to populate with shortcuts. + * @param menu The current menu, which may be null. + * @param deviceId The id for the connected device the shortcuts should be provided for. + */ @Override public void onProvideKeyboardShortcuts( List data, Menu menu, int deviceId) { - - ArrayList shortcutInfos = new ArrayList<>(); - if (isInState(NORMAL)) { - shortcutInfos.add(new KeyboardShortcutInfo(getString(R.string.all_apps_button_label), - KeyEvent.KEYCODE_A, KeyEvent.META_CTRL_ON)); - shortcutInfos.add(new KeyboardShortcutInfo(getString(R.string.widget_button_text), - KeyEvent.KEYCODE_W, KeyEvent.META_CTRL_ON)); - } - getSupportedActions(this, getCurrentFocus()).forEach(la -> - shortcutInfos.add(new KeyboardShortcutInfo( - la.accessibilityAction.getLabel(), la.keyCode, KeyEvent.META_CTRL_ON))); - if (!shortcutInfos.isEmpty()) { - data.add(new KeyboardShortcutGroup(getString(R.string.home_screen), shortcutInfos)); - } - + mKeyboardShortcutsDelegate.onProvideKeyboardShortcuts(data, menu, deviceId); super.onProvideKeyboardShortcuts(data, menu, deviceId); } + /** + * Logic delegated to {@Link KeyboardShortcutsDelegate}. + * @param keyCode The value in event.getKeyCode(). + * @param event Description of the key event. + */ @Override public boolean onKeyShortcut(int keyCode, KeyEvent event) { - if (event.hasModifiers(KeyEvent.META_CTRL_ON)) { - switch (keyCode) { - case KeyEvent.KEYCODE_A: - if (isInState(NORMAL)) { - getStateManager().goToState(ALL_APPS); - return true; - } - break; - case KeyEvent.KEYCODE_W: - if (isInState(NORMAL)) { - OptionsPopupView.openWidgets(this); - return true; - } - break; - default: - for (LauncherAction la : getSupportedActions(this, getCurrentFocus())) { - if (la.keyCode == keyCode) { - return la.invokeFromKeyboard(getCurrentFocus()); - } - } - } - } - return super.onKeyShortcut(keyCode, event); + Boolean result = mKeyboardShortcutsDelegate.onKeyShortcut(keyCode, event); + return result != null ? result : super.onKeyShortcut(keyCode, event); } + /** + * Logic delegated to {@Link KeyboardShortcutsDelegate}. + * @param keyCode The value in event.getKeyCode(). + * @param event Description of the key event. + */ @Override public boolean onKeyDown(int keyCode, KeyEvent event) { - if (keyCode == KeyEvent.KEYCODE_ESCAPE) { - // Close any open floating views. - closeOpenViews(); - return true; - } - return super.onKeyDown(keyCode, event); + Boolean result = mKeyboardShortcutsDelegate.onKeyDown(keyCode, event); + return result != null ? result : super.onKeyDown(keyCode, event); } + /** + * Logic delegated to {@Link KeyboardShortcutsDelegate}. + * @param keyCode The value in event.getKeyCode(). + * @param event Description of the key event. + */ @Override public boolean onKeyUp(int keyCode, KeyEvent event) { - if (keyCode == KeyEvent.KEYCODE_MENU) { - // KEYCODE_MENU is sent by some tests, for example - // LauncherJankTests#testWidgetsContainerFling. Don't just remove its handling. - if (!mDragController.isDragging() && !mWorkspace.isSwitchingState() && - isInState(NORMAL)) { - // Close any open floating views. - closeOpenViews(); - - // Setting the touch point to (-1, -1) will show the options popup in the center of - // the screen. - if (Utilities.isRunningInTestHarness()) { - Log.d(TestProtocol.PERMANENT_DIAG_TAG, "Opening options popup on key up"); - } - showDefaultOptions(-1, -1); - } - return true; - } - return super.onKeyUp(keyCode, event); + Boolean result = mKeyboardShortcutsDelegate.onKeyUp(keyCode, event); + return result != null ? result : super.onKeyUp(keyCode, event); } /** @@ -3338,7 +3304,7 @@ public class Launcher extends StatefulActivity getStateManager().goToState(LauncherState.NORMAL); } - private void closeOpenViews() { + public void closeOpenViews() { closeOpenViews(true); } diff --git a/src/com/android/launcher3/util/KeyboardShortcutsDelegate.java b/src/com/android/launcher3/util/KeyboardShortcutsDelegate.java new file mode 100644 index 0000000000..3ec339d5cb --- /dev/null +++ b/src/com/android/launcher3/util/KeyboardShortcutsDelegate.java @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.util; + +import static com.android.launcher3.LauncherState.ALL_APPS; +import static com.android.launcher3.LauncherState.NORMAL; +import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.getSupportedActions; + +import android.util.Log; +import android.view.KeyEvent; +import android.view.KeyboardShortcutGroup; +import android.view.KeyboardShortcutInfo; +import android.view.Menu; + +import com.android.launcher3.Launcher; +import com.android.launcher3.R; +import com.android.launcher3.Utilities; +import com.android.launcher3.accessibility.BaseAccessibilityDelegate; +import com.android.launcher3.testing.shared.TestProtocol; +import com.android.launcher3.views.OptionsPopupView; + +import java.util.ArrayList; +import java.util.List; + +/** + * Delegate to define the keyboard shortcuts. + */ +public class KeyboardShortcutsDelegate { + + Launcher mLauncher; + + public KeyboardShortcutsDelegate(Launcher launcher) { + mLauncher = launcher; + } + + /** + * Populates the list of shortcuts. + */ + public void onProvideKeyboardShortcuts( + List data, Menu menu, int deviceId) { + ArrayList shortcutInfos = new ArrayList<>(); + if (mLauncher.isInState(NORMAL)) { + shortcutInfos.add( + new KeyboardShortcutInfo(mLauncher.getString(R.string.all_apps_button_label), + KeyEvent.KEYCODE_A, KeyEvent.META_CTRL_ON)); + shortcutInfos.add( + new KeyboardShortcutInfo(mLauncher.getString(R.string.widget_button_text), + KeyEvent.KEYCODE_W, KeyEvent.META_CTRL_ON)); + } + getSupportedActions(mLauncher, mLauncher.getCurrentFocus()).forEach(la -> + shortcutInfos.add(new KeyboardShortcutInfo( + la.accessibilityAction.getLabel(), la.keyCode, KeyEvent.META_CTRL_ON))); + if (!shortcutInfos.isEmpty()) { + data.add(new KeyboardShortcutGroup(mLauncher.getString(R.string.home_screen), + shortcutInfos)); + } + } + + /** + * Handles combinations of keys like ctrl+s or ctrl+c and runs before onKeyDown. + * @param keyCode code of the key being pressed. + * @see android.view.KeyEvent + * @return weather the event is already handled and if it should be passed to other components. + */ + public Boolean onKeyShortcut(int keyCode, KeyEvent event) { + if (event.hasModifiers(KeyEvent.META_CTRL_ON)) { + switch (keyCode) { + case KeyEvent.KEYCODE_A: + if (mLauncher.isInState(NORMAL)) { + mLauncher.getStateManager().goToState(ALL_APPS); + return true; + } + break; + case KeyEvent.KEYCODE_W: + if (mLauncher.isInState(NORMAL)) { + OptionsPopupView.openWidgets(mLauncher); + return true; + } + break; + default: + for (BaseAccessibilityDelegate.LauncherAction la : getSupportedActions( + mLauncher, mLauncher.getCurrentFocus())) { + if (la.keyCode == keyCode) { + return la.invokeFromKeyboard(mLauncher.getCurrentFocus()); + } + } + } + } + return null; + } + + /** + * Handle key down event. + * @param keyCode code of the key being pressed. + * @see android.view.KeyEvent + */ + public Boolean onKeyDown(int keyCode, KeyEvent event) { + if (keyCode == KeyEvent.KEYCODE_ESCAPE) { + // Close any open floating views. + mLauncher.closeOpenViews(); + return true; + } + return null; + } + + /** + * Handle key up event. + * @param keyCode code of the key being pressed. + * @see android.view.KeyEvent + */ + public Boolean onKeyUp(int keyCode, KeyEvent event) { + if (keyCode == KeyEvent.KEYCODE_MENU) { + // KEYCODE_MENU is sent by some tests, for example + // LauncherJankTests#testWidgetsContainerFling. Don't just remove its handling. + if (!mLauncher.getDragController().isDragging() + && !mLauncher.getWorkspace().isSwitchingState() + && mLauncher.isInState(NORMAL)) { + // Close any open floating views. + mLauncher.closeOpenViews(); + + // Setting the touch point to (-1, -1) will show the options popup in the center of + // the screen. + if (Utilities.isRunningInTestHarness()) { + Log.d(TestProtocol.PERMANENT_DIAG_TAG, "Opening options popup on key up"); + } + mLauncher.showDefaultOptions(-1, -1); + } + return true; + } + return null; + } +}