Index: chrome/android/java_staging/src/org/chromium/chrome/browser/KeyboardShortcuts.java |
diff --git a/chrome/android/java_staging/src/org/chromium/chrome/browser/KeyboardShortcuts.java b/chrome/android/java_staging/src/org/chromium/chrome/browser/KeyboardShortcuts.java |
new file mode 100644 |
index 0000000000000000000000000000000000000000..3bc7fb2dde6a25c8948115983b99f42d7c973e90 |
--- /dev/null |
+++ b/chrome/android/java_staging/src/org/chromium/chrome/browser/KeyboardShortcuts.java |
@@ -0,0 +1,239 @@ |
+// Copyright 2015 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+package org.chromium.chrome.browser; |
+ |
+import android.view.KeyEvent; |
+ |
+import com.google.android.apps.chrome.R; |
+ |
+import org.chromium.base.annotations.SuppressFBWarnings; |
+import org.chromium.chrome.browser.tabmodel.ChromeTabCreator; |
+import org.chromium.chrome.browser.tabmodel.TabModel; |
+import org.chromium.chrome.browser.tabmodel.TabModel.TabLaunchType; |
+import org.chromium.chrome.browser.tabmodel.TabModelUtils; |
+import org.chromium.content.browser.ContentViewCore; |
+import org.chromium.content_public.browser.LoadUrlParams; |
+import org.chromium.ui.base.PageTransition; |
+ |
+/** |
+ * Implements app-level keyboard shortcuts for ChromeTabbedActivity and DocumentActivity. |
+ */ |
+public class KeyboardShortcuts { |
+ |
+ private static final int CTRL = 1 << 31; |
+ private static final int ALT = 1 << 30; |
+ private static final int SHIFT = 1 << 29; |
+ |
+ private KeyboardShortcuts() {} |
+ |
+ private static int getMetaState(KeyEvent event) { |
+ return (event.isCtrlPressed() ? CTRL : 0) |
+ | (event.isAltPressed() ? ALT : 0) |
+ | (event.isShiftPressed() ? SHIFT : 0); |
+ } |
+ |
+ /** |
+ * This should be called from the Activity's dispatchKeyEvent() to handle keyboard shortcuts. |
+ * |
+ * Note: dispatchKeyEvent() is called before the active view or web page gets a chance to handle |
+ * the key event. So the keys handled here cannot be overridden by any view or web page. |
+ * |
+ * @param event The KeyEvent to handle. |
+ * @param activity The ChromeActivity in which the key was pressed. |
+ * @param uiInitialized Whether the UI has been initialized. If this is false, most keys will |
+ * not be handled. |
+ * @return True if the event was handled. False if the event was ignored. Null if the event |
+ * should be handled by the activity's parent class. |
+ */ |
+ @SuppressFBWarnings("NP_BOOLEAN_RETURN_NULL") |
+ public static Boolean dispatchKeyEvent(KeyEvent event, ChromeActivity activity, |
+ boolean uiInitialized) { |
+ int keyCode = event.getKeyCode(); |
+ if (!uiInitialized) { |
+ if (keyCode == KeyEvent.KEYCODE_SEARCH || keyCode == KeyEvent.KEYCODE_MENU) return true; |
+ return null; |
+ } |
+ |
+ switch (keyCode) { |
+ case KeyEvent.KEYCODE_SEARCH: |
+ if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) { |
+ activity.onMenuOrKeyboardAction(R.id.focus_url_bar, false); |
+ } |
+ // Always consume the SEARCH key events to prevent android from showing |
+ // the default app search UI, which locks up Chrome. |
+ return true; |
+ case KeyEvent.KEYCODE_MENU: |
+ if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) { |
+ activity.onMenuOrKeyboardAction(R.id.show_menu, false); |
+ } |
+ return true; |
+ case KeyEvent.KEYCODE_TV: |
+ case KeyEvent.KEYCODE_GUIDE: |
+ case KeyEvent.KEYCODE_DVR: |
+ case KeyEvent.KEYCODE_AVR_INPUT: |
+ case KeyEvent.KEYCODE_AVR_POWER: |
+ case KeyEvent.KEYCODE_STB_INPUT: |
+ case KeyEvent.KEYCODE_STB_POWER: |
+ case KeyEvent.KEYCODE_TV_INPUT: |
+ case KeyEvent.KEYCODE_TV_POWER: |
+ case KeyEvent.KEYCODE_WINDOW: |
+ // Do not consume the AV device-related keys so that the system will take |
+ // an appropriate action, such as switching to TV mode. |
+ return false; |
+ } |
+ |
+ return null; |
+ } |
+ |
+ /** |
+ * This should be called from the Activity's onKeyDown() to handle keyboard shortcuts. |
+ * |
+ * Note: onKeyDown() is called after the active view or web page has had a chance to handle |
+ * the key event. So the keys handled here *can* be overridden by any view or web page. |
+ * |
+ * @param event The KeyEvent to handle. |
+ * @param activity The ChromeActivity in which the key was pressed. |
+ * @param isCurrentTabVisible Whether page-related actions are valid, e.g. reload, zoom in. |
+ * This should be false when in the tab switcher. |
+ * @param tabSwitchingEnabled Whether shortcuts that switch between tabs are enabled (e.g. |
+ * Ctrl+Tab, Ctrl+3). |
+ * @return Whether the key event was handled. |
+ */ |
+ public static boolean onKeyDown(KeyEvent event, ChromeActivity activity, |
+ boolean isCurrentTabVisible, boolean tabSwitchingEnabled) { |
+ int keyCode = event.getKeyCode(); |
+ if (event.getRepeatCount() != 0 || KeyEvent.isModifierKey(keyCode)) return false; |
+ if (!event.isCtrlPressed() && !event.isAltPressed() |
+ && keyCode != KeyEvent.KEYCODE_F3 |
+ && keyCode != KeyEvent.KEYCODE_F5) { |
+ return false; |
+ } |
+ |
+ TabModel curModel = activity.getCurrentTabModel(); |
+ int count = curModel.getCount(); |
+ |
+ int metaState = getMetaState(event); |
+ int keyCodeAndMeta = keyCode | metaState; |
+ |
+ switch (keyCodeAndMeta) { |
+ case CTRL | KeyEvent.KEYCODE_T: |
+ activity.onMenuOrKeyboardAction(curModel.isIncognito() |
+ ? R.id.new_incognito_tab_menu_id |
+ : R.id.new_tab_menu_id, false); |
+ return true; |
+ case CTRL | KeyEvent.KEYCODE_N: |
+ activity.onMenuOrKeyboardAction(R.id.new_tab_menu_id, false); |
+ return true; |
+ case CTRL | SHIFT | KeyEvent.KEYCODE_N: |
+ activity.onMenuOrKeyboardAction(R.id.new_incognito_tab_menu_id, false); |
+ return true; |
+ case CTRL | SHIFT | KeyEvent.KEYCODE_B: |
+ case CTRL | KeyEvent.KEYCODE_H: |
+ String url = keyCode == KeyEvent.KEYCODE_B |
+ ? UrlConstants.BOOKMARKS_URL |
+ : UrlConstants.HISTORY_URL; |
+ Tab currentTab = TabModelUtils.getCurrentTab(curModel); |
+ if (currentTab != null && isCurrentTabVisible) { |
+ currentTab.loadUrl(new LoadUrlParams(url, PageTransition.AUTO_BOOKMARK)); |
+ } else { |
+ ChromeTabCreator tabCreator = activity.getCurrentTabCreator(); |
+ if (tabCreator != null) { |
+ tabCreator.launchUrl(url, TabLaunchType.FROM_KEYBOARD); |
+ } |
+ } |
+ return true; |
+ case ALT | KeyEvent.KEYCODE_F: |
+ activity.onMenuOrKeyboardAction(R.id.show_menu, false); |
+ return true; |
+ } |
+ |
+ if (isCurrentTabVisible) { |
+ if (tabSwitchingEnabled && (metaState == CTRL || metaState == ALT)) { |
+ int numCode = keyCode - KeyEvent.KEYCODE_0; |
+ if (numCode > 0 && numCode <= Math.min(count, 8)) { |
+ // Ctrl+1 to Ctrl+8: select tab by index |
+ TabModelUtils.setIndex(curModel, numCode - 1); |
+ return true; |
+ } else if (numCode == 9 && count != 0) { |
+ // Ctrl+9: select last tab |
+ TabModelUtils.setIndex(curModel, count - 1); |
+ return true; |
+ } |
+ } |
+ |
+ switch (keyCodeAndMeta) { |
+ case CTRL | KeyEvent.KEYCODE_TAB: |
+ case CTRL | KeyEvent.KEYCODE_PAGE_DOWN: |
+ if (tabSwitchingEnabled && count > 1) { |
+ TabModelUtils.setIndex(curModel, (curModel.index() + 1) % count); |
+ } |
+ return true; |
+ case CTRL | SHIFT | KeyEvent.KEYCODE_TAB: |
+ case CTRL | KeyEvent.KEYCODE_PAGE_UP: |
+ if (tabSwitchingEnabled && count > 1) { |
+ TabModelUtils.setIndex(curModel, (curModel.index() + count - 1) % count); |
+ } |
+ return true; |
+ case CTRL | KeyEvent.KEYCODE_W: |
+ case CTRL | KeyEvent.KEYCODE_F4: |
+ TabModelUtils.closeCurrentTab(curModel); |
+ return true; |
+ case CTRL | KeyEvent.KEYCODE_F: |
+ case CTRL | KeyEvent.KEYCODE_G: |
+ case CTRL | SHIFT | KeyEvent.KEYCODE_G: |
+ case KeyEvent.KEYCODE_F3: |
+ case SHIFT | KeyEvent.KEYCODE_F3: |
+ activity.onMenuOrKeyboardAction(R.id.find_in_page_id, false); |
+ return true; |
+ case CTRL | KeyEvent.KEYCODE_L: |
+ case ALT | KeyEvent.KEYCODE_D: |
+ activity.onMenuOrKeyboardAction(R.id.focus_url_bar, false); |
+ return true; |
+ case KeyEvent.KEYCODE_BOOKMARK: |
+ case CTRL | KeyEvent.KEYCODE_D: |
+ activity.onMenuOrKeyboardAction(R.id.bookmark_this_page_id, false); |
+ return true; |
+ case CTRL | KeyEvent.KEYCODE_P: |
+ activity.onMenuOrKeyboardAction(R.id.print_id, false); |
+ return true; |
+ case CTRL | KeyEvent.KEYCODE_PLUS: |
+ case CTRL | KeyEvent.KEYCODE_EQUALS: |
+ case CTRL | SHIFT | KeyEvent.KEYCODE_PLUS: |
+ case CTRL | SHIFT | KeyEvent.KEYCODE_EQUALS: |
+ case KeyEvent.KEYCODE_ZOOM_IN: |
+ ContentViewCore cvc = activity.getCurrentContentViewCore(); |
+ if (cvc != null) cvc.zoomIn(); |
+ return true; |
+ case CTRL | KeyEvent.KEYCODE_MINUS: |
+ case KeyEvent.KEYCODE_ZOOM_OUT: |
+ cvc = activity.getCurrentContentViewCore(); |
+ if (cvc != null) cvc.zoomOut(); |
+ return true; |
+ case CTRL | KeyEvent.KEYCODE_0: |
+ cvc = activity.getCurrentContentViewCore(); |
+ if (cvc != null) cvc.zoomReset(); |
+ return true; |
+ case CTRL | KeyEvent.KEYCODE_R: |
+ case KeyEvent.KEYCODE_F5: |
+ Tab tab = activity.getActivityTab(); |
+ if (tab != null) tab.reload(); |
+ return true; |
+ case ALT | KeyEvent.KEYCODE_DPAD_LEFT: |
+ tab = activity.getActivityTab(); |
+ if (tab != null && tab.canGoBack()) tab.goBack(); |
+ return true; |
+ case ALT | KeyEvent.KEYCODE_DPAD_RIGHT: |
+ tab = activity.getActivityTab(); |
+ if (tab != null && tab.canGoForward()) tab.goForward(); |
+ return true; |
+ case CTRL | SHIFT | KeyEvent.KEYCODE_SLASH: // i.e. Ctrl+? |
+ activity.onMenuOrKeyboardAction(R.id.help_id, false); |
+ return true; |
+ } |
+ } |
+ |
+ return false; |
+ } |
+} |