diff options
author | Sunny Goyal <sunnygoyal@google.com> | 2016-09-21 15:57:55 -0700 |
---|---|---|
committer | Sunny Goyal <sunnygoyal@google.com> | 2016-09-23 15:57:08 -0700 |
commit | 66b24572e41a13ba5b85b37cf7be64804299c8f6 (patch) | |
tree | eb28b6c264a0a589a911dbcf5540621b111953d9 /src/com/android | |
parent | f09bfab404fa0118a6c3cb6a317edaab3907eb0b (diff) | |
download | android_packages_apps_Trebuchet-66b24572e41a13ba5b85b37cf7be64804299c8f6.tar.gz android_packages_apps_Trebuchet-66b24572e41a13ba5b85b37cf7be64804299c8f6.tar.bz2 android_packages_apps_Trebuchet-66b24572e41a13ba5b85b37cf7be64804299c8f6.zip |
Exposing custom actions using keyboard shortcut
Keyboard shortcuts:
ctrl+A => Open all apps
ctrl+S => shows deep shortcuts
ctrl+O => shows custom actions popup
This also removes the direct delete/uninstall key shortcuts, making
actidental icon removal less likely
Bug: 24065447
Change-Id: Iae63370c0f33620628567cffd4df024064d4d02e
Diffstat (limited to 'src/com/android')
8 files changed, 176 insertions, 45 deletions
diff --git a/src/com/android/launcher3/FocusHelper.java b/src/com/android/launcher3/FocusHelper.java index c73ceea14..de079feda 100644 --- a/src/com/android/launcher3/FocusHelper.java +++ b/src/com/android/launcher3/FocusHelper.java @@ -250,14 +250,6 @@ public class FocusHelper { } else if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT && profile.isVerticalBarLayout()) { keyCode = KeyEvent.KEYCODE_PAGE_DOWN; - } else if (isUninstallKeyChord(e)) { - matrix = FocusLogic.createSparseMatrix(iconLayout); - if (UninstallDropTarget.supportsDrop(launcher, itemInfo)) { - UninstallDropTarget.startUninstallActivity(launcher, itemInfo); - } - } else if (isDeleteKeyChord(e)) { - matrix = FocusLogic.createSparseMatrix(iconLayout); - launcher.removeItem(v, itemInfo, true /* deleteFromDb */); } else { // For other KEYCODE_DPAD_LEFT and KEYCODE_DPAD_RIGHT navigation, do not use the // matrix extended with hotseat. @@ -374,14 +366,6 @@ public class FocusHelper { } else if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT && profile.isVerticalBarLayout()) { matrix = FocusLogic.createSparseMatrixWithHotseat(iconLayout, hotseatLayout, profile); - } else if (isUninstallKeyChord(e)) { - matrix = FocusLogic.createSparseMatrix(iconLayout); - if (UninstallDropTarget.supportsDrop(launcher, itemInfo)) { - UninstallDropTarget.startUninstallActivity(launcher, itemInfo); - } - } else if (isDeleteKeyChord(e)) { - matrix = FocusLogic.createSparseMatrix(iconLayout); - launcher.removeItem(v, itemInfo, true /* deleteFromDb */); } else { matrix = FocusLogic.createSparseMatrix(iconLayout); } @@ -532,24 +516,6 @@ public class FocusHelper { } } - /** - * Returns whether the key event represents a valid uninstall key chord. - */ - private static boolean isUninstallKeyChord(KeyEvent event) { - int keyCode = event.getKeyCode(); - return (keyCode == KeyEvent.KEYCODE_DEL || keyCode == KeyEvent.KEYCODE_FORWARD_DEL) && - event.hasModifiers(KeyEvent.META_CTRL_ON | KeyEvent.META_SHIFT_ON); - } - - /** - * Returns whether the key event represents a valid delete key chord. - */ - private static boolean isDeleteKeyChord(KeyEvent event) { - int keyCode = event.getKeyCode(); - return (keyCode == KeyEvent.KEYCODE_DEL || keyCode == KeyEvent.KEYCODE_FORWARD_DEL) && - event.hasModifiers(KeyEvent.META_CTRL_ON); - } - private static View handlePreviousPageLastItem(Workspace workspace, CellLayout hotseatLayout, int pageIndex, boolean isRtl) { if (pageIndex - 1 < 0) { diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index dc71d0ca1..5a25069e7 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -69,6 +69,8 @@ import android.util.Log; import android.view.Display; import android.view.HapticFeedbackConstants; import android.view.KeyEvent; +import android.view.KeyboardShortcutGroup; +import android.view.KeyboardShortcutInfo; import android.view.Menu; import android.view.MotionEvent; import android.view.Surface; @@ -79,6 +81,7 @@ import android.view.ViewGroup; import android.view.ViewTreeObserver; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; +import android.view.accessibility.AccessibilityNodeInfo; import android.view.animation.OvershootInterpolator; import android.view.inputmethod.InputMethodManager; import android.widget.Advanceable; @@ -106,6 +109,7 @@ import com.android.launcher3.dragndrop.DragView; import com.android.launcher3.dynamicui.ExtractedColors; import com.android.launcher3.folder.Folder; import com.android.launcher3.folder.FolderIcon; +import com.android.launcher3.keyboard.CustomActionsPopup; import com.android.launcher3.keyboard.ViewGroupFocusHelper; import com.android.launcher3.logging.FileLog; import com.android.launcher3.logging.UserEventDispatcher; @@ -4422,6 +4426,65 @@ public class Launcher extends Activity } } + @Override + @TargetApi(Build.VERSION_CODES.N) + public void onProvideKeyboardShortcuts( + List<KeyboardShortcutGroup> data, Menu menu, int deviceId) { + + ArrayList<KeyboardShortcutInfo> shortcutInfos = new ArrayList<>(); + if (mState == State.WORKSPACE) { + shortcutInfos.add(new KeyboardShortcutInfo(getString(R.string.all_apps_button_label), + KeyEvent.KEYCODE_A, KeyEvent.META_CTRL_ON)); + } + View currentFocus = getCurrentFocus(); + if (new CustomActionsPopup(this, currentFocus).canShow()) { + shortcutInfos.add(new KeyboardShortcutInfo(getString(R.string.custom_actions), + KeyEvent.KEYCODE_O, KeyEvent.META_CTRL_ON)); + } + if (currentFocus instanceof BubbleTextView && + ((BubbleTextView) currentFocus).hasDeepShortcuts()) { + shortcutInfos.add(new KeyboardShortcutInfo(getString(R.string.action_deep_shortcut), + KeyEvent.KEYCODE_S, KeyEvent.META_CTRL_ON)); + } + if (!shortcutInfos.isEmpty()) { + data.add(new KeyboardShortcutGroup(getString(R.string.home_screen), shortcutInfos)); + } + + super.onProvideKeyboardShortcuts(data, menu, deviceId); + } + + @Override + public boolean onKeyShortcut(int keyCode, KeyEvent event) { + if (event.hasModifiers(KeyEvent.META_CTRL_ON)) { + switch (keyCode) { + case KeyEvent.KEYCODE_A: + if (mState == State.WORKSPACE) { + showAppsView(true, true, false); + return true; + } + break; + case KeyEvent.KEYCODE_S: { + View focusedView = getCurrentFocus(); + if (focusedView instanceof BubbleTextView + && focusedView.getTag() instanceof ItemInfo + && mAccessibilityDelegate.performAction(focusedView, + (ItemInfo) focusedView.getTag(), + LauncherAccessibilityDelegate.DEEP_SHORTCUTS)) { + getOpenShortcutsContainer().requestFocus(); + return true; + } + break; + } + case KeyEvent.KEYCODE_O: + if (new CustomActionsPopup(this, getCurrentFocus()).show()) { + return true; + } + break; + } + } + return super.onKeyShortcut(keyCode, event); + } + public static CustomAppWidget getCustomAppWidget(String name) { return sCustomAppWidgets.get(name); } diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java index 173aad044..439e3145e 100644 --- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java +++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java @@ -57,7 +57,7 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme protected static final int MOVE = R.id.action_move; protected static final int MOVE_TO_WORKSPACE = R.id.action_move_to_workspace; protected static final int RESIZE = R.id.action_resize; - protected static final int DEEP_SHORTCUTS = R.id.action_deep_shortcuts; + public static final int DEEP_SHORTCUTS = R.id.action_deep_shortcuts; public enum DragType { ICON, @@ -100,14 +100,17 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme @Override public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfo(host, info); - addActions(host, info); + addSupportedActions(host, info, false); } - protected void addActions(View host, AccessibilityNodeInfo info) { + public void addSupportedActions(View host, AccessibilityNodeInfo info, boolean fromKeyboard) { if (!(host.getTag() instanceof ItemInfo)) return; ItemInfo item = (ItemInfo) host.getTag(); - if (host instanceof BubbleTextView && ((BubbleTextView) host).hasDeepShortcuts()) { + // If the request came from keyboard, do not add custom shortcuts as that is already + // exposed as a direct shortcut + if (!fromKeyboard && host instanceof BubbleTextView + && ((BubbleTextView) host).hasDeepShortcuts()) { info.addAction(mActions.get(DEEP_SHORTCUTS)); } @@ -121,9 +124,10 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme info.addAction(mActions.get(INFO)); } - if ((item instanceof ShortcutInfo) + // Do not add move actions for keyboard request as this uses virtual nodes. + if (!fromKeyboard && ((item instanceof ShortcutInfo) || (item instanceof LauncherAppWidgetInfo) - || (item instanceof FolderInfo)) { + || (item instanceof FolderInfo))) { info.addAction(mActions.get(MOVE)); if (item.container >= 0) { diff --git a/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java index 0baa8f3db..5baa7b59e 100644 --- a/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java +++ b/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java @@ -40,8 +40,10 @@ public class ShortcutMenuAccessibilityDelegate extends LauncherAccessibilityDele } @Override - protected void addActions(View host, AccessibilityNodeInfo info) { - info.addAction(mActions.get(ADD_TO_WORKSPACE)); + public void addSupportedActions(View host, AccessibilityNodeInfo info, boolean fromKeyboard) { + if ((host.getParent() instanceof DeepShortcutView)) { + info.addAction(mActions.get(ADD_TO_WORKSPACE)); + } } @Override diff --git a/src/com/android/launcher3/keyboard/CustomActionsPopup.java b/src/com/android/launcher3/keyboard/CustomActionsPopup.java new file mode 100644 index 000000000..6056f4c10 --- /dev/null +++ b/src/com/android/launcher3/keyboard/CustomActionsPopup.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2016 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.keyboard; + +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.view.accessibility.AccessibilityNodeInfo; +import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; +import android.widget.PopupMenu; +import android.widget.PopupMenu.OnMenuItemClickListener; + +import com.android.launcher3.ItemInfo; +import com.android.launcher3.Launcher; +import com.android.launcher3.accessibility.LauncherAccessibilityDelegate; +import com.android.launcher3.shortcuts.DeepShortcutsContainer; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Handles showing a popup menu with available custom actions for a launcher icon. + * This allows exposing various custom actions using keyboard shortcuts. + */ +public class CustomActionsPopup implements OnMenuItemClickListener { + + private final Launcher mLauncher; + private final LauncherAccessibilityDelegate mDelegate; + private final View mIcon; + + public CustomActionsPopup(Launcher launcher, View icon) { + mLauncher = launcher; + mIcon = icon; + DeepShortcutsContainer container = launcher.getOpenShortcutsContainer(); + if (container != null) { + mDelegate = container.getAccessibilityDelegate(); + } else { + mDelegate = launcher.getAccessibilityDelegate(); + } + } + + private List<AccessibilityAction> getActionList() { + if (mIcon == null || !(mIcon.getTag() instanceof ItemInfo)) { + return Collections.EMPTY_LIST; + } + + AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain(); + mDelegate.addSupportedActions(mIcon, info, true); + List<AccessibilityAction> result = new ArrayList<>(info.getActionList()); + info.recycle(); + return result; + } + + public boolean canShow() { + return !getActionList().isEmpty(); + } + + public boolean show() { + List<AccessibilityAction> actions = getActionList(); + if (actions.isEmpty()) { + return false; + } + + PopupMenu popup = new PopupMenu(mLauncher, mIcon); + popup.setOnMenuItemClickListener(this); + Menu menu = popup.getMenu(); + for (AccessibilityAction action : actions) { + menu.add(Menu.NONE, action.getId(), Menu.NONE, action.getLabel()); + } + popup.show(); + return true; + } + + @Override + public boolean onMenuItemClick(MenuItem menuItem) { + return mDelegate.performAction(mIcon, (ItemInfo) mIcon.getTag(), menuItem.getItemId()); + } +} diff --git a/src/com/android/launcher3/keyboard/FocusIndicatorHelper.java b/src/com/android/launcher3/keyboard/FocusIndicatorHelper.java index 7672f5a79..b0d6b2dbf 100644 --- a/src/com/android/launcher3/keyboard/FocusIndicatorHelper.java +++ b/src/com/android/launcher3/keyboard/FocusIndicatorHelper.java @@ -143,7 +143,7 @@ public abstract class FocusIndicatorHelper implements } private Rect getDrawRect() { - if (mCurrentView != null) { + if (mCurrentView != null && mCurrentView.isAttachedToWindow()) { viewToRect(mCurrentView, sTempRect1); if (mShift > 0 && mTargetView != null) { diff --git a/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java b/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java index b02e8abda..19624fec6 100644 --- a/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java +++ b/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java @@ -677,6 +677,10 @@ public class DeepShortcutsContainer extends LinearLayout implements View.OnLongC shortcutAnims.start(); } + public ShortcutMenuAccessibilityDelegate getAccessibilityDelegate() { + return mAccessibilityDelegate; + } + /** * Closes the folder without animation. */ diff --git a/src/com/android/launcher3/util/FocusLogic.java b/src/com/android/launcher3/util/FocusLogic.java index 163c953bb..afc45fe35 100644 --- a/src/com/android/launcher3/util/FocusLogic.java +++ b/src/com/android/launcher3/util/FocusLogic.java @@ -79,8 +79,7 @@ public class FocusLogic { return (keyCode == KeyEvent.KEYCODE_DPAD_LEFT || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT || keyCode == KeyEvent.KEYCODE_DPAD_UP || keyCode == KeyEvent.KEYCODE_DPAD_DOWN || keyCode == KeyEvent.KEYCODE_MOVE_HOME || keyCode == KeyEvent.KEYCODE_MOVE_END || - keyCode == KeyEvent.KEYCODE_PAGE_UP || keyCode == KeyEvent.KEYCODE_PAGE_DOWN || - keyCode == KeyEvent.KEYCODE_DEL || keyCode == KeyEvent.KEYCODE_FORWARD_DEL); + keyCode == KeyEvent.KEYCODE_PAGE_UP || keyCode == KeyEvent.KEYCODE_PAGE_DOWN); } public static int handleKeyEvent(int keyCode, int [][] map, int iconIdx, int pageIndex, |