blob: 7facc96f39c968d0ae4afc4b0926acc213ea4aa9 [file] [log] [blame]
// Copyright (c) 2012 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.
#include "ash/accelerators/accelerator_controller_impl.h"
#include <algorithm>
#include <cmath>
#include <string>
#include <utility>
#include "ash/accelerators/accelerator_commands.h"
#include "ash/accelerators/accelerator_notifications.h"
#include "ash/accelerators/debug_commands.h"
#include "ash/accessibility/accessibility_controller_impl.h"
#include "ash/accessibility/magnifier/docked_magnifier_controller.h"
#include "ash/accessibility/magnifier/fullscreen_magnifier_controller.h"
#include "ash/accessibility/ui/accessibility_confirmation_dialog.h"
#include "ash/ambient/ambient_controller.h"
#include "ash/app_list/app_list_controller_impl.h"
#include "ash/app_list/app_list_metrics.h"
#include "ash/assistant/model/assistant_ui_model.h"
#include "ash/capture_mode/capture_mode_controller.h"
#include "ash/capture_mode/capture_mode_metrics.h"
#include "ash/clipboard/clipboard_history_controller_impl.h"
#include "ash/components/audio/cras_audio_handler.h"
#include "ash/constants/ash_features.h"
#include "ash/constants/ash_pref_names.h"
#include "ash/constants/ash_switches.h"
#include "ash/constants/devicetype.h"
#include "ash/debug.h"
#include "ash/display/display_configuration_controller.h"
#include "ash/display/display_move_window_util.h"
#include "ash/display/privacy_screen_controller.h"
#include "ash/display/screen_orientation_controller.h"
#include "ash/focus_cycler.h"
#include "ash/frame/non_client_frame_view_ash.h"
#include "ash/ime/ime_controller_impl.h"
#include "ash/ime/ime_switch_type.h"
#include "ash/keyboard/ui/keyboard_ui_controller.h"
#include "ash/metrics/user_metrics_recorder.h"
#include "ash/multi_profile_uma.h"
#include "ash/public/cpp/accelerators.h"
#include "ash/public/cpp/assistant/controller/assistant_ui_controller.h"
#include "ash/public/cpp/new_window_delegate.h"
#include "ash/public/cpp/toast_data.h"
#include "ash/root_window_controller.h"
#include "ash/rotator/window_rotation.h"
#include "ash/shelf/home_button.h"
#include "ash/shelf/shelf.h"
#include "ash/shelf/shelf_focus_cycler.h"
#include "ash/shelf/shelf_navigation_widget.h"
#include "ash/shelf/shelf_widget.h"
#include "ash/shell.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/system/accessibility/floating_accessibility_controller.h"
#include "ash/system/brightness_control_delegate.h"
#include "ash/system/ime_menu/ime_menu_tray.h"
#include "ash/system/keyboard_brightness_control_delegate.h"
#include "ash/system/palette/palette_tray.h"
#include "ash/system/palette/palette_utils.h"
#include "ash/system/power/power_button_controller.h"
#include "ash/system/status_area_widget.h"
#include "ash/system/toast/toast_manager_impl.h"
#include "ash/system/tray/system_tray_notifier.h"
#include "ash/system/unified/unified_system_tray.h"
#include "ash/touch/touch_hud_debug.h"
#include "ash/wm/desks/desks_animations.h"
#include "ash/wm/desks/desks_controller.h"
#include "ash/wm/mru_window_tracker.h"
#include "ash/wm/overview/overview_controller.h"
#include "ash/wm/overview/overview_session.h"
#include "ash/wm/screen_pinning_controller.h"
#include "ash/wm/tablet_mode/tablet_mode_controller.h"
#include "ash/wm/window_positioning_utils.h"
#include "ash/wm/window_state.h"
#include "ash/wm/window_util.h"
#include "ash/wm/wm_event.h"
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/command_line.h"
#include "base/containers/contains.h"
#include "base/files/file_util.h"
#include "base/json/json_reader.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/metrics/user_metrics.h"
#include "base/strings/utf_string_conversions.h"
#include "base/system/sys_info.h"
#include "chromeos/dbus/power/power_manager_client.h"
#include "chromeos/ui/base/display_util.h"
#include "components/user_manager/user_type.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/env.h"
#include "ui/base/accelerators/accelerator.h"
#include "ui/base/accelerators/accelerator_manager.h"
#include "ui/base/emoji/emoji_panel_helper.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/ui_base_features.h"
#include "ui/chromeos/events/keyboard_layout_util.h"
#include "ui/compositor/layer.h"
#include "ui/compositor/layer_animation_sequence.h"
#include "ui/compositor/layer_animator.h"
#include "ui/display/display.h"
#include "ui/display/manager/managed_display_info.h"
#include "ui/display/screen.h"
#include "ui/events/devices/device_data_manager.h"
#include "ui/events/devices/input_device.h"
#include "ui/gfx/paint_vector_icon.h"
namespace ash {
const char kTabletCountOfVolumeAdjustType[] = "Tablet.CountOfVolumeAdjustType";
const char kAccessibilityHighContrastShortcut[] =
"Accessibility.Shortcuts.CrosHighContrast";
const char kAccessibilitySpokenFeedbackShortcut[] =
"Accessibility.Shortcuts.CrosSpokenFeedback";
const char kAccessibilityScreenMagnifierShortcut[] =
"Accessibility.Shortcuts.CrosScreenMagnifier";
const char kAccessibilityDockedMagnifierShortcut[] =
"Accessibility.Shortcuts.CrosDockedMagnifier";
const char kAccelWindowSnap[] = "Ash.Accelerators.WindowSnap";
namespace {
using ::base::UserMetricsAction;
using ::chromeos::WindowStateType;
using input_method::InputMethodManager;
// Toast id and duration for Assistant shortcuts.
constexpr char kAssistantErrorToastId[] = "assistant_error";
constexpr int kToastDurationMs = 2500;
constexpr char kVirtualDesksToastId[] = "virtual_desks_toast";
// Path of the json file that contains side volume button location info.
constexpr char kSideVolumeButtonLocationFilePath[] =
"/usr/share/chromeos-assets/side_volume_button/location.json";
// The interval between two volume control actions within one volume adjust.
constexpr base::TimeDelta kVolumeAdjustTimeout = base::Seconds(2);
// These values are written to logs. New enum values can be added, but existing
// enums must never be renumbered or deleted and reused.
// Records the result of triggering the rotation accelerator.
enum class RotationAcceleratorAction {
kCancelledDialog = 0,
kAcceptedDialog = 1,
kAlreadyAcceptedDialog = 2,
kMaxValue = kAlreadyAcceptedDialog,
};
void RecordRotationAcceleratorAction(const RotationAcceleratorAction& action) {
UMA_HISTOGRAM_ENUMERATION("Ash.Accelerators.Rotation.Usage", action);
}
void RecordWindowSnapAcceleratorAction(WindowSnapAcceleratorAction action) {
UMA_HISTOGRAM_ENUMERATION(kAccelWindowSnap, action);
}
void RecordTabletVolumeAdjustTypeHistogram(TabletModeVolumeAdjustType type) {
UMA_HISTOGRAM_ENUMERATION(kTabletCountOfVolumeAdjustType, type);
}
// Returns the number of times the startup notification has been shown
// from prefs.
int GetStartupNotificationPrefCount(PrefService* pref_service) {
DCHECK(pref_service);
return pref_service->GetInteger(
prefs::kImprovedShortcutsNotificationShownCount);
}
bool ShouldShowStartupNotificationForCurrentUser() {
const absl::optional<user_manager::UserType> user_type =
Shell::Get()->session_controller()->GetUserType();
return user_type &&
(*user_type == user_manager::USER_TYPE_REGULAR ||
*user_type == user_manager::USER_TYPE_CHILD) &&
!Shell::Get()->session_controller()->IsUserFirstLogin();
}
// Increments the number of times the startup notification has been shown
// in prefs.
void IncrementStartupNotificationCount(PrefService* pref_service) {
DCHECK(pref_service);
int count = GetStartupNotificationPrefCount(pref_service);
// Increment the pref count.
pref_service->SetInteger(prefs::kImprovedShortcutsNotificationShownCount,
count + 1);
}
// Shows a notification that accelerators/shortcuts have changed in this
// release.
// TODO(crbug.com/1179893): Remove this function in M97/M98.
void NotifyShortcutChangesInRelease(PrefService* pref_service) {
DCHECK(::features::IsImprovedKeyboardShortcutsEnabled());
DCHECK(pref_service);
if (GetStartupNotificationPrefCount(pref_service) > 0)
return;
ShowShortcutsChangedNotification();
IncrementStartupNotificationCount(pref_service);
}
void ShowToast(std::string id, const std::u16string& text) {
ToastData toast(id, text, kToastDurationMs, absl::nullopt,
/*visible_on_lock_screen=*/true);
Shell::Get()->toast_manager()->Show(toast);
}
ui::Accelerator CreateAccelerator(ui::KeyboardCode keycode,
int modifiers,
bool trigger_on_press) {
ui::Accelerator accelerator(keycode, modifiers);
accelerator.set_key_state(trigger_on_press
? ui::Accelerator::KeyState::PRESSED
: ui::Accelerator::KeyState::RELEASED);
return accelerator;
}
void RecordUmaHistogram(const char* histogram_name,
DeprecatedAcceleratorUsage sample) {
auto* histogram = base::LinearHistogram::FactoryGet(
histogram_name, 1, DEPRECATED_USAGE_COUNT, DEPRECATED_USAGE_COUNT + 1,
base::HistogramBase::kUmaTargetedHistogramFlag);
histogram->Add(sample);
}
void RecordImeSwitchByAccelerator() {
UMA_HISTOGRAM_ENUMERATION("InputMethod.ImeSwitch",
ImeSwitchType::kAccelerator);
}
void RecordImeSwitchByModeChangeKey() {
UMA_HISTOGRAM_ENUMERATION("InputMethod.ImeSwitch",
ImeSwitchType::kModeChangeKey);
}
void RecordCycleBackwardMru(const ui::Accelerator& accelerator) {
if (accelerator.key_code() == ui::VKEY_TAB)
base::RecordAction(base::UserMetricsAction("Accel_PrevWindow_Tab"));
}
void RecordCycleForwardMru(const ui::Accelerator& accelerator) {
if (accelerator.key_code() == ui::VKEY_TAB)
base::RecordAction(base::UserMetricsAction("Accel_NextWindow_Tab"));
}
void HandleActivateDesk(const ui::Accelerator& accelerator,
bool activate_left) {
auto* desks_controller = DesksController::Get();
const bool success = desks_controller->ActivateAdjacentDesk(
activate_left, DesksSwitchSource::kDeskSwitchShortcut);
if (!success)
return;
if (activate_left) {
base::RecordAction(base::UserMetricsAction("Accel_Desks_ActivateLeft"));
} else {
base::RecordAction(base::UserMetricsAction("Accel_Desks_ActivateRight"));
}
}
void HandleMoveActiveItem(const ui::Accelerator& accelerator, bool going_left) {
auto* desks_controller = DesksController::Get();
if (desks_controller->AreDesksBeingModified())
return;
aura::Window* window_to_move = nullptr;
auto* overview_controller = Shell::Get()->overview_controller();
const bool in_overview = overview_controller->InOverviewSession();
if (in_overview) {
window_to_move =
overview_controller->overview_session()->GetHighlightedWindow();
} else {
window_to_move = window_util::GetActiveWindow();
}
if (!window_to_move)
return;
Desk* target_desk = nullptr;
if (going_left) {
target_desk = desks_controller->GetPreviousDesk();
base::RecordAction(base::UserMetricsAction("Accel_Desks_MoveWindowLeft"));
} else {
target_desk = desks_controller->GetNextDesk();
base::RecordAction(base::UserMetricsAction("Accel_Desks_MoveWindowRight"));
}
if (!target_desk)
return;
if (!in_overview) {
desks_animations::PerformWindowMoveToDeskAnimation(window_to_move,
going_left);
}
if (!desks_controller->MoveWindowFromActiveDeskTo(
window_to_move, target_desk, window_to_move->GetRootWindow(),
DesksMoveWindowFromActiveDeskSource::kShortcut)) {
return;
}
if (in_overview) {
// We should not exit overview as a result of this shortcut.
DCHECK(overview_controller->InOverviewSession());
overview_controller->overview_session()->PositionWindows(/*animate=*/true);
}
}
void HandleNewDesk() {
auto* desks_controller = DesksController::Get();
if (!desks_controller->CanCreateDesks()) {
ShowToast(kVirtualDesksToastId,
l10n_util::GetStringUTF16(IDS_ASH_DESKS_MAX_NUM_REACHED));
return;
}
if (desks_controller->AreDesksBeingModified())
return;
// Add a new desk and switch to it.
const size_t new_desk_index = desks_controller->desks().size();
desks_controller->NewDesk(DesksCreationRemovalSource::kKeyboard);
const Desk* desk = desks_controller->desks()[new_desk_index].get();
desks_controller->ActivateDesk(desk, DesksSwitchSource::kNewDeskShortcut);
base::RecordAction(base::UserMetricsAction("Accel_Desks_NewDesk"));
}
void HandleRemoveCurrentDesk() {
if (window_util::IsAnyWindowDragged())
return;
auto* desks_controller = DesksController::Get();
if (!desks_controller->CanRemoveDesks()) {
ShowToast(kVirtualDesksToastId,
l10n_util::GetStringUTF16(IDS_ASH_DESKS_MIN_NUM_REACHED));
return;
}
if (desks_controller->AreDesksBeingModified())
return;
// TODO(afakhry): Finalize the desk removal animation outside of overview with
// UX. https://crbug.com/977434.
desks_controller->RemoveDesk(desks_controller->active_desk(),
DesksCreationRemovalSource::kKeyboard);
base::RecordAction(base::UserMetricsAction("Accel_Desks_RemoveDesk"));
}
void HandleRotatePaneFocus(FocusCycler::Direction direction) {
switch (direction) {
// TODO(stevet): Not sure if this is the same as IDC_FOCUS_NEXT_PANE.
case FocusCycler::FORWARD: {
base::RecordAction(UserMetricsAction("Accel_Focus_Next_Pane"));
break;
}
case FocusCycler::BACKWARD: {
base::RecordAction(UserMetricsAction("Accel_Focus_Previous_Pane"));
break;
}
}
Shell::Get()->focus_cycler()->RotateFocus(direction);
}
void HandleFocusShelf() {
base::RecordAction(UserMetricsAction("Accel_Focus_Shelf"));
if (Shell::Get()->session_controller()->IsRunningInAppMode()) {
// If floating accessibility menu is shown, focus on it instead of the
// shelf.
FloatingAccessibilityController* floating_menu =
Shell::Get()->accessibility_controller()->GetFloatingMenuController();
if (floating_menu) {
floating_menu->FocusOnMenu();
}
return;
}
// TODO(jamescook): Should this be GetRootWindowForNewWindows()?
// Focus the home button.
Shelf* shelf = Shelf::ForWindow(Shell::GetPrimaryRootWindow());
shelf->shelf_focus_cycler()->FocusNavigation(false /* lastElement */);
}
// TODO(zentaro): This is duplicated in accelerator_commands.cc. Remove
// once the CanFocusPipWidget() function is moved.
views::Widget* FindPipWidget() {
return Shell::Get()->focus_cycler()->FindWidget(
base::BindRepeating([](views::Widget* widget) {
return WindowState::Get(widget->GetNativeWindow())->IsPip();
}));
}
void HandleToggleMirrorMode() {
base::RecordAction(UserMetricsAction("Accel_Toggle_Mirror_Mode"));
bool mirror = !Shell::Get()->display_manager()->IsInMirrorMode();
Shell::Get()->display_configuration_controller()->SetMirrorMode(
mirror, true /* throttle */);
}
bool CanHandleNewIncognitoWindow() {
// Guest mode does not use incognito windows. The browser may have other
// restrictions on incognito mode (e.g. enterprise policy) but those are rare.
// For non-guest mode, consume the key and defer the decision to the browser.
absl::optional<user_manager::UserType> user_type =
Shell::Get()->session_controller()->GetUserType();
return user_type && *user_type != user_manager::USER_TYPE_GUEST;
}
void HandleNewTab(const ui::Accelerator& accelerator) {
if (accelerator.key_code() == ui::VKEY_T)
base::RecordAction(UserMetricsAction("Accel_NewTab_T"));
NewWindowDelegate::GetPrimary()->NewTab();
}
bool CanCycleInputMethod() {
return Shell::Get()->ime_controller()->CanSwitchIme();
}
bool CanHandleCycleMru(const ui::Accelerator& accelerator) {
// Don't do anything when Alt+Tab is hit while a virtual keyboard is showing.
// Touchscreen users have better window switching options. It would be
// preferable if we could tell whether this event actually came from a virtual
// keyboard, but there's no easy way to do so, thus we block Alt+Tab when the
// virtual keyboard is showing, even if it came from a real keyboard. See
// http://crbug.com/638269
return !keyboard::KeyboardUIController::Get()->IsKeyboardVisible();
}
void HandleSwitchToNextIme(const ui::Accelerator& accelerator) {
base::RecordAction(UserMetricsAction("Accel_Next_Ime"));
if (accelerator.key_code() == ui::VKEY_MODECHANGE)
RecordImeSwitchByModeChangeKey();
else
RecordImeSwitchByAccelerator();
Shell::Get()->ime_controller()->SwitchToNextIme();
}
void HandleSwitchToLastUsedIme(const ui::Accelerator& accelerator) {
base::RecordAction(UserMetricsAction("Accel_Previous_Ime"));
if (accelerator.key_state() == ui::Accelerator::KeyState::PRESSED) {
RecordImeSwitchByAccelerator();
Shell::Get()->ime_controller()->SwitchToLastUsedIme();
}
// Else: consume the Ctrl+Space ET_KEY_RELEASED event but do not do anything.
}
display::Display::Rotation GetNextRotationInClamshell(
display::Display::Rotation current) {
switch (current) {
case display::Display::ROTATE_0:
return display::Display::ROTATE_90;
case display::Display::ROTATE_90:
return display::Display::ROTATE_180;
case display::Display::ROTATE_180:
return display::Display::ROTATE_270;
case display::Display::ROTATE_270:
return display::Display::ROTATE_0;
}
NOTREACHED() << "Unknown rotation:" << current;
return display::Display::ROTATE_0;
}
display::Display::Rotation GetNextRotationInTabletMode(
int64_t display_id,
display::Display::Rotation current) {
Shell* shell = Shell::Get();
DCHECK(shell->tablet_mode_controller()->InTabletMode());
if (!display::Display::HasInternalDisplay() ||
display_id != display::Display::InternalDisplayId()) {
return GetNextRotationInClamshell(current);
}
const chromeos::OrientationType app_requested_lock =
shell->screen_orientation_controller()
->GetCurrentAppRequestedOrientationLock();
bool add_180_degrees = false;
switch (app_requested_lock) {
case chromeos::OrientationType::kCurrent:
case chromeos::OrientationType::kLandscapePrimary:
case chromeos::OrientationType::kLandscapeSecondary:
case chromeos::OrientationType::kPortraitPrimary:
case chromeos::OrientationType::kPortraitSecondary:
case chromeos::OrientationType::kNatural:
// Do not change the current orientation.
return current;
case chromeos::OrientationType::kLandscape:
case chromeos::OrientationType::kPortrait:
// App allows both primary and secondary orientations in either landscape
// or portrait, therefore switch to the next one by adding 180 degrees.
add_180_degrees = true;
break;
default:
break;
}
switch (current) {
case display::Display::ROTATE_0:
return add_180_degrees ? display::Display::ROTATE_180
: display::Display::ROTATE_90;
case display::Display::ROTATE_90:
return add_180_degrees ? display::Display::ROTATE_270
: display::Display::ROTATE_180;
case display::Display::ROTATE_180:
return add_180_degrees ? display::Display::ROTATE_0
: display::Display::ROTATE_270;
case display::Display::ROTATE_270:
return add_180_degrees ? display::Display::ROTATE_90
: display::Display::ROTATE_0;
}
NOTREACHED() << "Unknown rotation:" << current;
return display::Display::ROTATE_0;
}
bool ShouldLockRotation(int64_t display_id) {
return display::Display::HasInternalDisplay() &&
display_id == display::Display::InternalDisplayId() &&
Shell::Get()->screen_orientation_controller()->IsAutoRotationAllowed();
}
int64_t GetDisplayIdForRotation() {
const gfx::Point point = display::Screen::GetScreen()->GetCursorScreenPoint();
return display::Screen::GetScreen()->GetDisplayNearestPoint(point).id();
}
void RotateScreen() {
auto* shell = Shell::Get();
const bool in_tablet_mode =
Shell::Get()->tablet_mode_controller()->InTabletMode();
const int64_t display_id = GetDisplayIdForRotation();
const display::ManagedDisplayInfo& display_info =
shell->display_manager()->GetDisplayInfo(display_id);
const auto active_rotation = display_info.GetActiveRotation();
const auto next_rotation =
in_tablet_mode ? GetNextRotationInTabletMode(display_id, active_rotation)
: GetNextRotationInClamshell(active_rotation);
if (active_rotation == next_rotation)
return;
// When the auto-rotation is allowed in the device, display rotation requests
// of the internal display are treated as requests to lock the user rotation.
if (ShouldLockRotation(display_id)) {
shell->screen_orientation_controller()->SetLockToRotation(next_rotation);
return;
}
shell->display_configuration_controller()->SetDisplayRotation(
display_id, next_rotation, display::Display::RotationSource::USER);
}
void OnRotationDialogAccepted() {
RecordRotationAcceleratorAction(RotationAcceleratorAction::kAcceptedDialog);
RotateScreen();
Shell::Get()
->accessibility_controller()
->SetDisplayRotationAcceleratorDialogBeenAccepted();
}
void OnRotationDialogCancelled() {
RecordRotationAcceleratorAction(RotationAcceleratorAction::kCancelledDialog);
}
// Rotates the screen.
void HandleRotateScreen() {
if (Shell::Get()->display_manager()->IsInUnifiedMode())
return;
base::RecordAction(UserMetricsAction("Accel_Rotate_Screen"));
const bool dialog_ever_accepted =
Shell::Get()
->accessibility_controller()
->HasDisplayRotationAcceleratorDialogBeenAccepted();
if (!dialog_ever_accepted) {
Shell::Get()->accelerator_controller()->MaybeShowConfirmationDialog(
IDS_ASH_ROTATE_SCREEN_TITLE, IDS_ASH_ROTATE_SCREEN_BODY,
base::BindOnce(&OnRotationDialogAccepted),
base::BindOnce(&OnRotationDialogCancelled));
} else {
RecordRotationAcceleratorAction(
RotationAcceleratorAction::kAlreadyAcceptedDialog);
RotateScreen();
}
}
// Rotate the active window.
void HandleRotateActiveWindow() {
base::RecordAction(UserMetricsAction("Accel_Rotate_Active_Window"));
aura::Window* active_window = window_util::GetActiveWindow();
if (!active_window)
return;
// The rotation animation bases its target transform on the current
// rotation and position. Since there could be an animation in progress
// right now, queue this animation so when it starts it picks up a neutral
// rotation and position. Use replace so we only enqueue one at a time.
active_window->layer()->GetAnimator()->set_preemption_strategy(
ui::LayerAnimator::REPLACE_QUEUED_ANIMATIONS);
active_window->layer()->GetAnimator()->StartAnimation(
new ui::LayerAnimationSequence(
std::make_unique<WindowRotation>(360, active_window->layer())));
}
void HandleShowKeyboardShortcutViewer() {
NewWindowDelegate::GetInstance()->ShowKeyboardShortcutViewer();
}
bool CanHandleScreenshot(AcceleratorAction action) {
// |TAKE_SCREENSHOT| is allowed when user session is blocked.
return action == TAKE_SCREENSHOT ||
!Shell::Get()->session_controller()->IsUserSessionBlocked();
}
bool CanHandleToggleResizeLockMenu() {
aura::Window* active_window = window_util::GetActiveWindow();
if (!active_window)
return false;
auto* frame_view = ash::NonClientFrameViewAsh::Get(active_window);
return frame_view && frame_view->GetToggleResizeLockMenuCallback();
}
bool CanHandleToggleFloatingWindow() {
return features::IsWindowControlMenuEnabled() &&
!Shell::Get()->tablet_mode_controller()->InTabletMode();
}
// Enters capture mode image type with |source|.
void EnterImageCaptureMode(CaptureModeSource source,
CaptureModeEntryType entry_type) {
auto* capture_mode_controller = CaptureModeController::Get();
capture_mode_controller->SetSource(source);
capture_mode_controller->SetType(CaptureModeType::kImage);
capture_mode_controller->Start(entry_type);
}
void HandleTakeWindowScreenshot() {
base::RecordAction(UserMetricsAction("Accel_Take_Window_Screenshot"));
EnterImageCaptureMode(CaptureModeSource::kWindow,
CaptureModeEntryType::kAccelTakeWindowScreenshot);
}
void HandleTakePartialScreenshot() {
base::RecordAction(UserMetricsAction("Accel_Take_Partial_Screenshot"));
EnterImageCaptureMode(CaptureModeSource::kRegion,
CaptureModeEntryType::kAccelTakePartialScreenshot);
}
void HandleTakeScreenshot(ui::KeyboardCode key_code) {
base::RecordAction(UserMetricsAction("Accel_Take_Screenshot"));
// If it is the snip key, toggle capture mode unless the session is blocked,
// in which case, it behaves like a fullscreen screenshot.
auto* capture_mode_controller = CaptureModeController::Get();
if (key_code == ui::VKEY_SNAPSHOT &&
!Shell::Get()->session_controller()->IsUserSessionBlocked()) {
if (capture_mode_controller->IsActive())
capture_mode_controller->Stop();
else
capture_mode_controller->Start(CaptureModeEntryType::kSnipKey);
return;
}
capture_mode_controller->CaptureScreenshotsOfAllDisplays();
}
void HandleToggleSystemTrayBubbleInternal(bool focus_message_center) {
aura::Window* target_root = Shell::GetRootWindowForNewWindows();
UnifiedSystemTray* tray = RootWindowController::ForWindow(target_root)
->GetStatusAreaWidget()
->unified_system_tray();
if (tray->IsBubbleShown()) {
tray->CloseBubble();
} else {
tray->ShowBubble();
tray->ActivateBubble();
if (focus_message_center)
tray->FocusFirstNotification();
}
}
void HandleToggleSystemTrayBubble() {
base::RecordAction(UserMetricsAction("Accel_Toggle_System_Tray_Bubble"));
HandleToggleSystemTrayBubbleInternal(false /*focus_message_center*/);
}
void HandleToggleMessageCenterBubble() {
base::RecordAction(UserMetricsAction("Accel_Toggle_Message_Center_Bubble"));
HandleToggleSystemTrayBubbleInternal(true /*focus_message_center*/);
}
void HandleShowTaskManager() {
base::RecordAction(UserMetricsAction("Accel_Show_Task_Manager"));
NewWindowDelegate::GetInstance()->ShowTaskManager();
}
void HandleSwapPrimaryDisplay() {
base::RecordAction(UserMetricsAction("Accel_Swap_Primary_Display"));
accelerators::ShiftPrimaryDisplay();
}
bool CanHandleSwitchIme(const ui::Accelerator& accelerator) {
return Shell::Get()->ime_controller()->CanSwitchImeWithAccelerator(
accelerator);
}
void HandleSwitchIme(const ui::Accelerator& accelerator) {
base::RecordAction(UserMetricsAction("Accel_Switch_Ime"));
RecordImeSwitchByAccelerator();
Shell::Get()->ime_controller()->SwitchImeWithAccelerator(accelerator);
}
bool CanHandleToggleAppList(
const ui::Accelerator& accelerator,
const ui::Accelerator& previous_accelerator,
const std::set<ui::KeyboardCode>& currently_pressed_keys) {
for (auto key : currently_pressed_keys) {
// The AppList accelerator is triggered on search(VKEY_LWIN) key release.
// Sometimes users will press and release the search key while holding other
// keys in an attempt to trigger a different accelerator. We should not
// toggle the AppList in that case. Check for VKEY_SHIFT because this is
// used to show fullscreen app list.
if (key != ui::VKEY_LWIN && key != ui::VKEY_SHIFT &&
key != ui::VKEY_BROWSER_SEARCH) {
return false;
}
}
if (accelerator.key_code() == ui::VKEY_LWIN) {
// If something else was pressed between the Search key (LWIN)
// being pressed and released, then ignore the release of the
// Search key.
if (previous_accelerator.key_state() !=
ui::Accelerator::KeyState::PRESSED ||
previous_accelerator.key_code() != ui::VKEY_LWIN ||
previous_accelerator.interrupted_by_mouse_event()) {
return false;
}
// When spoken feedback is enabled, we should neither toggle the list nor
// consume the key since Search+Shift is one of the shortcuts the a11y
// feature uses. crbug.com/132296
if (Shell::Get()->accessibility_controller()->spoken_feedback().enabled())
return false;
}
return true;
}
void HandleToggleAppList(const ui::Accelerator& accelerator,
AppListShowSource show_source) {
if (accelerator.key_code() == ui::VKEY_LWIN)
base::RecordAction(UserMetricsAction("Accel_Search_LWin"));
aura::Window* const root_window = Shell::GetRootWindowForNewWindows();
Shell::Get()->app_list_controller()->ToggleAppList(
display::Screen::GetScreen()->GetDisplayNearestWindow(root_window).id(),
show_source, accelerator.time_stamp());
}
void HandleToggleFloating() {
DCHECK(features::IsWindowControlMenuEnabled());
// Floating is currently not supported for tablet mode, see timeline here:
// https://crbug.com/1240411
DCHECK(!Shell::Get()->tablet_mode_controller()->InTabletMode());
accelerators::ToggleFloating();
base::RecordAction(UserMetricsAction("Accel_Toggle_Floating"));
}
void HandleToggleFullscreen(const ui::Accelerator& accelerator) {
if (accelerator.key_code() == ui::VKEY_ZOOM)
base::RecordAction(UserMetricsAction("Accel_Fullscreen_F4"));
OverviewController* overview_controller = Shell::Get()->overview_controller();
// Disable fullscreen while overview animation is running due to
// http://crbug.com/1094739
if (!overview_controller->IsInStartAnimation())
accelerators::ToggleFullscreen();
}
void HandleToggleOverview() {
base::RecordAction(base::UserMetricsAction("Accel_Overview_F5"));
OverviewController* overview_controller = Shell::Get()->overview_controller();
if (overview_controller->InOverviewSession())
overview_controller->EndOverview(OverviewEndAction::kAccelerator);
else
overview_controller->StartOverview(OverviewStartAction::kAccelerator);
}
void HandleToggleUnifiedDesktop() {
Shell::Get()->display_manager()->SetUnifiedDesktopEnabled(
!Shell::Get()->display_manager()->unified_desktop_enabled());
}
bool CanHandleWindowSnap() {
aura::Window* active_window = window_util::GetActiveWindow();
if (!active_window)
return false;
WindowState* window_state = WindowState::Get(active_window);
return window_state && window_state->IsUserPositionable();
}
void HandleWindowSnap(AcceleratorAction action) {
Shell* shell = Shell::Get();
const bool in_tablet = shell->tablet_mode_controller()->InTabletMode();
const bool in_overview = shell->overview_controller()->InOverviewSession();
if (action == WINDOW_CYCLE_SNAP_LEFT) {
base::RecordAction(UserMetricsAction("Accel_Window_Snap_Left"));
if (in_tablet) {
RecordWindowSnapAcceleratorAction(
WindowSnapAcceleratorAction::kCycleLeftSnapInTablet);
} else if (in_overview) {
RecordWindowSnapAcceleratorAction(
WindowSnapAcceleratorAction::kCycleLeftSnapInClamshellOverview);
} else {
RecordWindowSnapAcceleratorAction(
WindowSnapAcceleratorAction::kCycleLeftSnapInClamshellNoOverview);
}
} else {
base::RecordAction(UserMetricsAction("Accel_Window_Snap_Right"));
if (in_tablet) {
RecordWindowSnapAcceleratorAction(
WindowSnapAcceleratorAction::kCycleRightSnapInTablet);
} else if (in_overview) {
RecordWindowSnapAcceleratorAction(
WindowSnapAcceleratorAction::kCycleRightSnapInClamshellOverview);
} else {
RecordWindowSnapAcceleratorAction(
WindowSnapAcceleratorAction::kCycleRightSnapInClamshellNoOverview);
}
}
const WMEvent event(action == WINDOW_CYCLE_SNAP_LEFT
? WM_EVENT_CYCLE_SNAP_PRIMARY
: WM_EVENT_CYCLE_SNAP_SECONDARY);
aura::Window* active_window = window_util::GetActiveWindow();
DCHECK(active_window);
WindowState::Get(active_window)->OnWMEvent(&event);
}
void HandleWindowMinimize() {
base::RecordAction(base::UserMetricsAction("Accel_Toggle_Minimized_Minus"));
accelerators::ToggleMinimized();
}
void HandleTopWindowMinimizeOnBack() {
base::RecordAction(
base::UserMetricsAction("Accel_Minimize_Top_Window_On_Back"));
WindowState::Get(window_util::GetTopWindow())->Minimize();
}
void HandleShowEmojiPicker() {
base::RecordAction(UserMetricsAction("Accel_Show_Emoji_Picker"));
ui::ShowEmojiPanel();
}
void HandleShowImeMenuBubble() {
base::RecordAction(UserMetricsAction("Accel_Show_Ime_Menu_Bubble"));
StatusAreaWidget* status_area_widget =
Shelf::ForWindow(Shell::GetPrimaryRootWindow())->GetStatusAreaWidget();
if (status_area_widget) {
ImeMenuTray* ime_menu_tray = status_area_widget->ime_menu_tray();
if (ime_menu_tray && ime_menu_tray->GetVisible() &&
!ime_menu_tray->GetBubbleView()) {
ime_menu_tray->ShowBubble();
}
}
}
bool CanHandleDisableCapsLock(const ui::Accelerator& previous_accelerator) {
ui::KeyboardCode previous_key_code = previous_accelerator.key_code();
if (previous_accelerator.key_state() == ui::Accelerator::KeyState::RELEASED ||
(previous_key_code != ui::VKEY_LSHIFT &&
previous_key_code != ui::VKEY_SHIFT &&
previous_key_code != ui::VKEY_RSHIFT)) {
// If something else was pressed between the Shift key being pressed
// and released, then ignore the release of the Shift key.
return false;
}
return Shell::Get()->ime_controller()->IsCapsLockEnabled();
}
bool CanHandleLock() {
return Shell::Get()->session_controller()->CanLockScreen();
}
PaletteTray* GetPaletteTray() {
return Shelf::ForWindow(Shell::GetRootWindowForNewWindows())
->GetStatusAreaWidget()
->palette_tray();
}
void HandleShowStylusTools() {
base::RecordAction(UserMetricsAction("Accel_Show_Stylus_Tools"));
GetPaletteTray()->ShowBubble();
}
bool CanHandleShowStylusTools() {
return GetPaletteTray()->ShouldShowPalette();
}
bool CanHandleStartAmbientMode() {
return chromeos::features::IsAmbientModeEnabled();
}
void HandleToggleAmbientMode(const ui::Accelerator& accelerator) {
Shell::Get()->ambient_controller()->ToggleInSessionUi();
}
void HandleToggleAssistant(const ui::Accelerator& accelerator) {
if (accelerator.IsCmdDown() && accelerator.key_code() == ui::VKEY_SPACE) {
base::RecordAction(
base::UserMetricsAction("VoiceInteraction.Started.Search_Space"));
} else if (accelerator.IsCmdDown() && accelerator.key_code() == ui::VKEY_A) {
// Search+A shortcut is disabled on device with an assistant key.
// Currently only Google branded device has the key. Some external keyboard
// may report it has the key but actually not. This would cause keyboard
// shortcut stops working. So we only check the key on these branded
// devices.
if (chromeos::IsGoogleBrandedDevice() &&
ui::DeviceKeyboardHasAssistantKey()) {
return;
}
base::RecordAction(
base::UserMetricsAction("VoiceInteraction.Started.Search_A"));
} else if (accelerator.key_code() == ui::VKEY_ASSISTANT) {
base::RecordAction(
base::UserMetricsAction("VoiceInteraction.Started.Assistant"));
}
using chromeos::assistant::AssistantAllowedState;
switch (AssistantState::Get()->allowed_state().value_or(
AssistantAllowedState::ALLOWED)) {
case AssistantAllowedState::DISALLOWED_BY_NONPRIMARY_USER:
// Show a toast if the active user is not primary.
ShowToast(kAssistantErrorToastId,
l10n_util::GetStringUTF16(
IDS_ASH_ASSISTANT_SECONDARY_USER_TOAST_MESSAGE));
return;
case AssistantAllowedState::DISALLOWED_BY_LOCALE:
// Show a toast if the Assistant is disabled due to unsupported
// locales.
ShowToast(kAssistantErrorToastId,
l10n_util::GetStringUTF16(
IDS_ASH_ASSISTANT_LOCALE_UNSUPPORTED_TOAST_MESSAGE));
return;
case AssistantAllowedState::DISALLOWED_BY_POLICY:
// Show a toast if the Assistant is disabled due to enterprise policy.
ShowToast(kAssistantErrorToastId,
l10n_util::GetStringUTF16(
IDS_ASH_ASSISTANT_DISABLED_BY_POLICY_MESSAGE));
return;
case AssistantAllowedState::DISALLOWED_BY_DEMO_MODE:
// Show a toast if the Assistant is disabled due to being in Demo
// Mode.
ShowToast(kAssistantErrorToastId,
l10n_util::GetStringUTF16(
IDS_ASH_ASSISTANT_DISABLED_IN_DEMO_MODE_MESSAGE));
return;
case AssistantAllowedState::DISALLOWED_BY_PUBLIC_SESSION:
// Show a toast if the Assistant is disabled due to being in public
// session.
ShowToast(kAssistantErrorToastId,
l10n_util::GetStringUTF16(
IDS_ASH_ASSISTANT_DISABLED_IN_PUBLIC_SESSION_MESSAGE));
return;
case AssistantAllowedState::DISALLOWED_BY_INCOGNITO:
ShowToast(kAssistantErrorToastId,
l10n_util::GetStringUTF16(
IDS_ASH_ASSISTANT_DISABLED_IN_GUEST_MESSAGE));
return;
case AssistantAllowedState::DISALLOWED_BY_ACCOUNT_TYPE:
ShowToast(kAssistantErrorToastId,
l10n_util::GetStringUTF16(
IDS_ASH_ASSISTANT_DISABLED_BY_ACCOUNT_MESSAGE));
return;
case AssistantAllowedState::DISALLOWED_BY_KIOSK_MODE:
// No need to show toast in KIOSK mode.
return;
case AssistantAllowedState::ALLOWED:
// Nothing need to do if allowed.
break;
}
AssistantUiController::Get()->ToggleUi(
/*entry_point=*/chromeos::assistant::AssistantEntryPoint::kHotkey,
/*exit_point=*/chromeos::assistant::AssistantExitPoint::kHotkey);
}
void HandleSuspend() {
base::RecordAction(UserMetricsAction("Accel_Suspend"));
chromeos::PowerManagerClient::Get()->RequestSuspend();
}
bool CanHandleCycleUser() {
return Shell::Get()->session_controller()->NumberOfLoggedInUsers() > 1;
}
void HandleCycleUser(CycleUserDirection direction) {
MultiProfileUMA::RecordSwitchActiveUser(
MultiProfileUMA::SWITCH_ACTIVE_USER_BY_ACCELERATOR);
switch (direction) {
case CycleUserDirection::NEXT:
base::RecordAction(UserMetricsAction("Accel_Switch_To_Next_User"));
break;
case CycleUserDirection::PREVIOUS:
base::RecordAction(UserMetricsAction("Accel_Switch_To_Previous_User"));
break;
}
Shell::Get()->session_controller()->CycleActiveUser(direction);
}
bool CanHandleToggleCapsLock(
const ui::Accelerator& accelerator,
const ui::Accelerator& previous_accelerator,
const std::set<ui::KeyboardCode>& currently_pressed_keys) {
// Iterate the set of pressed keys. If any redundant key is pressed, CapsLock
// should not be triggered. Otherwise, CapsLock may be triggered accidentally.
// See issue 789283 (https://crbug.com/789283)
for (const auto& pressed_key : currently_pressed_keys) {
if (pressed_key != ui::VKEY_LWIN && pressed_key != ui::VKEY_MENU)
return false;
}
// This shortcut is set to be trigger on release. Either the current
// accelerator is a Search release or Alt release.
if (accelerator.key_code() == ui::VKEY_LWIN &&
accelerator.key_state() == ui::Accelerator::KeyState::RELEASED) {
// The previous must be either an Alt press or Search press:
// 1. Press Alt, Press Search, Release Search, Release Alt.
// 2. Press Search, Press Alt, Release Search, Release Alt.
if (previous_accelerator.key_state() ==
ui::Accelerator::KeyState::PRESSED &&
(previous_accelerator.key_code() == ui::VKEY_LWIN ||
previous_accelerator.key_code() == ui::VKEY_MENU)) {
return true;
}
}
// Alt release.
if (accelerator.key_code() == ui::VKEY_MENU &&
accelerator.key_state() == ui::Accelerator::KeyState::RELEASED) {
// The previous must be either an Alt press or Search press:
// 3. Press Alt, Press Search, Release Alt, Release Search.
// 4. Press Search, Press Alt, Release Alt, Release Search.
if (previous_accelerator.key_state() ==
ui::Accelerator::KeyState::PRESSED &&
(previous_accelerator.key_code() == ui::VKEY_LWIN ||
previous_accelerator.key_code() == ui::VKEY_MENU)) {
return true;
}
}
return false;
}
void HandleToggleCapsLock() {
base::RecordAction(UserMetricsAction("Accel_Toggle_Caps_Lock"));
ImeControllerImpl* ime_controller = Shell::Get()->ime_controller();
ime_controller->SetCapsLockEnabled(!ime_controller->IsCapsLockEnabled());
}
bool CanHandleToggleClipboardHistory() {
return chromeos::features::IsClipboardHistoryEnabled();
}
void HandleToggleClipboardHistory() {
DCHECK(Shell::Get()->clipboard_history_controller());
Shell::Get()->clipboard_history_controller()->ShowMenuByAccelerator();
}
bool CanHandleToggleDictation() {
return Shell::Get()->accessibility_controller()->dictation().enabled();
}
void HandleToggleDictation() {
base::RecordAction(UserMetricsAction("Accel_Toggle_Dictation"));
Shell::Get()->accessibility_controller()->ToggleDictationFromSource(
DictationToggleSource::kKeyboard);
}
bool CanHandleToggleOverview() {
auto windows =
Shell::Get()->mru_window_tracker()->BuildMruWindowList(kActiveDesk);
// Do not toggle overview if there is a window being dragged.
for (auto* window : windows) {
if (WindowState::Get(window)->is_dragged())
return false;
}
return true;
}
// Return false if the accessibility shortcuts have been disabled, or if
// the accessibility feature itself associated with |accessibility_pref_name|
// is being enforced by the administrator.
bool IsAccessibilityShortcutEnabled(
const std::string& accessibility_pref_name) {
Shell* shell = Shell::Get();
return shell->accessibility_controller()->accessibility_shortcuts_enabled() &&
!shell->session_controller()
->GetActivePrefService()
->IsManagedPreference(accessibility_pref_name);
}
void SetDockedMagnifierEnabled(bool enabled) {
Shell* shell = Shell::Get();
// Check that the attempt to change the value of the accessibility feature
// will be done only when the accessibility shortcuts are enabled, and
// the feature isn't being enforced by the administrator.
DCHECK(IsAccessibilityShortcutEnabled(prefs::kDockedMagnifierEnabled));
shell->docked_magnifier_controller()->SetEnabled(enabled);
RemoveDockedMagnifierNotification();
if (shell->docked_magnifier_controller()->GetEnabled()) {
ShowDockedMagnifierNotification();
}
}
void HandleToggleDockedMagnifier() {
base::RecordAction(UserMetricsAction("Accel_Toggle_Docked_Magnifier"));
const bool is_shortcut_enabled =
IsAccessibilityShortcutEnabled(prefs::kDockedMagnifierEnabled);
base::UmaHistogramBoolean(kAccessibilityDockedMagnifierShortcut,
is_shortcut_enabled);
Shell* shell = Shell::Get();
RemoveDockedMagnifierNotification();
if (!is_shortcut_enabled) {
ShowDockedMagnifierDisabledByAdminNotification(
shell->docked_magnifier_controller()->GetEnabled());
return;
}
DockedMagnifierController* docked_magnifier_controller =
shell->docked_magnifier_controller();
AccessibilityControllerImpl* accessibility_controller =
shell->accessibility_controller();
const bool current_enabled = docked_magnifier_controller->GetEnabled();
const bool dialog_ever_accepted =
accessibility_controller->docked_magnifier().WasDialogAccepted();
if (!current_enabled && !dialog_ever_accepted) {
shell->accelerator_controller()->MaybeShowConfirmationDialog(
IDS_ASH_DOCKED_MAGNIFIER_TITLE, IDS_ASH_DOCKED_MAGNIFIER_BODY,
base::BindOnce([]() {
Shell::Get()
->accessibility_controller()
->docked_magnifier()
.SetDialogAccepted();
SetDockedMagnifierEnabled(true);
}),
base::DoNothing());
} else {
SetDockedMagnifierEnabled(!current_enabled);
}
}
void SetFullscreenMagnifierEnabled(bool enabled) {
// TODO (afakhry): Move the below into a single call (crbug/817157).
// Necessary to make magnification controller in ash observe changes to the
// prefs iteself.
Shell* shell = Shell::Get();
// Check that the attempt to change the value of the accessibility feature
// will be done only when the accessibility shortcuts are enabled, and
// the feature isn't being enforced by the administrator.
DCHECK(IsAccessibilityShortcutEnabled(
prefs::kAccessibilityScreenMagnifierEnabled));
shell->accessibility_controller()->fullscreen_magnifier().SetEnabled(enabled);
RemoveFullscreenMagnifierNotification();
if (shell->fullscreen_magnifier_controller()->IsEnabled()) {
ShowFullscreenMagnifierNotification();
}
}
void SetHighContrastEnabled(bool enabled) {
Shell* shell = Shell::Get();
// Check that the attempt to change the value of the accessibility feature
// will be done only when the accessibility shortcuts are enabled, and
// the feature isn't being enforced by the administrator.
DCHECK(
IsAccessibilityShortcutEnabled(prefs::kAccessibilityHighContrastEnabled));
shell->accessibility_controller()->high_contrast().SetEnabled(enabled);
RemoveHighContrastNotification();
if (shell->accessibility_controller()->high_contrast().enabled()) {
ShowHighContrastNotification();
}
}
void HandleToggleHighContrast() {
base::RecordAction(UserMetricsAction("Accel_Toggle_High_Contrast"));
const bool is_shortcut_enabled =
IsAccessibilityShortcutEnabled(prefs::kAccessibilityHighContrastEnabled);
base::UmaHistogramBoolean(kAccessibilityHighContrastShortcut,
is_shortcut_enabled);
Shell* shell = Shell::Get();
RemoveHighContrastNotification();
if (!is_shortcut_enabled) {
ShowHighContrastDisabledByAdminNotification(
shell->accessibility_controller()->high_contrast().enabled());
return;
}
AccessibilityControllerImpl* controller = shell->accessibility_controller();
const bool current_enabled = controller->high_contrast().enabled();
const bool dialog_ever_accepted =
controller->high_contrast().WasDialogAccepted();
if (!current_enabled && !dialog_ever_accepted) {
shell->accelerator_controller()->MaybeShowConfirmationDialog(
IDS_ASH_HIGH_CONTRAST_TITLE, IDS_ASH_HIGH_CONTRAST_BODY,
base::BindOnce([]() {
Shell::Get()
->accessibility_controller()
->high_contrast()
.SetDialogAccepted();
SetHighContrastEnabled(true);
}),
base::DoNothing());
} else {
SetHighContrastEnabled(!current_enabled);
}
}
void HandleToggleFullscreenMagnifier() {
base::RecordAction(UserMetricsAction("Accel_Toggle_Fullscreen_Magnifier"));
const bool is_shortcut_enabled = IsAccessibilityShortcutEnabled(
prefs::kAccessibilityScreenMagnifierEnabled);
base::UmaHistogramBoolean(kAccessibilityScreenMagnifierShortcut,
is_shortcut_enabled);
Shell* shell = Shell::Get();
RemoveFullscreenMagnifierNotification();
if (!is_shortcut_enabled) {
ShowFullscreenMagnifierDisabledByAdminNotification(
shell->fullscreen_magnifier_controller()->IsEnabled());
return;
}
FullscreenMagnifierController* magnification_controller =
shell->fullscreen_magnifier_controller();
AccessibilityControllerImpl* accessibility_controller =
shell->accessibility_controller();
const bool current_enabled = magnification_controller->IsEnabled();
const bool dialog_ever_accepted =
accessibility_controller->fullscreen_magnifier().WasDialogAccepted();
if (!current_enabled && !dialog_ever_accepted) {
shell->accelerator_controller()->MaybeShowConfirmationDialog(
IDS_ASH_SCREEN_MAGNIFIER_TITLE, IDS_ASH_SCREEN_MAGNIFIER_BODY,
base::BindOnce([]() {
Shell::Get()
->accessibility_controller()
->fullscreen_magnifier()
.SetDialogAccepted();
SetFullscreenMagnifierEnabled(true);
}),
base::DoNothing());
} else {
SetFullscreenMagnifierEnabled(!current_enabled);
}
}
void HandleToggleSpokenFeedback() {
base::RecordAction(UserMetricsAction("Accel_Toggle_Spoken_Feedback"));
const bool is_shortcut_enabled = IsAccessibilityShortcutEnabled(
prefs::kAccessibilitySpokenFeedbackEnabled);
base::UmaHistogramBoolean(kAccessibilitySpokenFeedbackShortcut,
is_shortcut_enabled);
Shell* shell = Shell::Get();
const bool old_value =
shell->accessibility_controller()->spoken_feedback().enabled();
RemoveSpokenFeedbackNotification();
if (!is_shortcut_enabled) {
ShowSpokenFeedbackDisabledByAdminNotification(old_value);
return;
}
shell->accessibility_controller()->SetSpokenFeedbackEnabled(
!old_value, A11Y_NOTIFICATION_SHOW);
}
bool CanHandleTogglePrivacyScreen() {
return Shell::Get()->privacy_screen_controller()->IsSupported();
}
void HandleTogglePrivacyScreen() {
base::RecordAction(UserMetricsAction("Accel_Toggle_Privacy_Screen"));
PrivacyScreenController* controller =
Shell::Get()->privacy_screen_controller();
controller->SetEnabled(
!controller->GetEnabled(),
PrivacyScreenController::kToggleUISurfaceKeyboardShortcut);
}
// Percent by which the volume should be changed when a volume key is pressed.
const double kStepPercentage = 4.0;
void HandleVolumeDown() {
base::RecordAction(UserMetricsAction("Accel_VolumeDown_F9"));
auto* audio_handler = CrasAudioHandler::Get();
if (audio_handler->IsOutputMuted()) {
audio_handler->SetOutputVolumePercent(0);
} else {
audio_handler->AdjustOutputVolumeByPercent(-kStepPercentage);
if (audio_handler->IsOutputVolumeBelowDefaultMuteLevel())
audio_handler->SetOutputMute(true);
else
AcceleratorController::PlayVolumeAdjustmentSound();
}
}
void HandleVolumeMute(const ui::Accelerator& accelerator) {
if (accelerator.key_code() == ui::VKEY_VOLUME_MUTE)
base::RecordAction(UserMetricsAction("Accel_VolumeMute_F8"));
CrasAudioHandler::Get()->SetOutputMute(true);
}
void HandleVolumeUp() {
base::RecordAction(UserMetricsAction("Accel_VolumeUp_F10"));
auto* audio_handler = CrasAudioHandler::Get();
bool play_sound = false;
if (audio_handler->IsOutputMuted()) {
audio_handler->SetOutputMute(false);
audio_handler->AdjustOutputVolumeToAudibleLevel();
play_sound = true;
} else {
play_sound = audio_handler->GetOutputVolumePercent() != 100;
audio_handler->AdjustOutputVolumeByPercent(kStepPercentage);
}
if (play_sound)
AcceleratorController::PlayVolumeAdjustmentSound();
}
bool CanHandleActiveMagnifierZoom() {
return Shell::Get()->fullscreen_magnifier_controller()->IsEnabled() ||
Shell::Get()->docked_magnifier_controller()->GetEnabled();
}
// Change the scale of the active magnifier.
void HandleActiveMagnifierZoom(int delta_index) {
if (Shell::Get()->fullscreen_magnifier_controller()->IsEnabled()) {
Shell::Get()->fullscreen_magnifier_controller()->StepToNextScaleValue(
delta_index);
return;
}
if (Shell::Get()->docked_magnifier_controller()->GetEnabled()) {
Shell::Get()->docked_magnifier_controller()->StepToNextScaleValue(
delta_index);
}
}
bool CanHandleTouchHud() {
return RootWindowController::ForTargetRootWindow()->touch_hud_debug();
}
bool CanUnpinWindow() {
// WindowStateType::kTrustedPinned does not allow the user to press a key to
// exit pinned mode.
WindowState* window_state = WindowState::ForActiveWindow();
return window_state &&
window_state->GetStateType() == WindowStateType::kPinned;
}
void HandleTouchHudClear() {
RootWindowController::ForTargetRootWindow()->touch_hud_debug()->Clear();
}
void HandleTouchHudModeChange() {
RootWindowController* controller =
RootWindowController::ForTargetRootWindow();
controller->touch_hud_debug()->ChangeToNextMode();
}
} // namespace
constexpr const char* AcceleratorControllerImpl::kVolumeButtonRegion;
constexpr const char* AcceleratorControllerImpl::kVolumeButtonSide;
constexpr const char* AcceleratorControllerImpl::kVolumeButtonRegionKeyboard;
constexpr const char* AcceleratorControllerImpl::kVolumeButtonRegionScreen;
constexpr const char* AcceleratorControllerImpl::kVolumeButtonSideLeft;
constexpr const char* AcceleratorControllerImpl::kVolumeButtonSideRight;
constexpr const char* AcceleratorControllerImpl::kVolumeButtonSideTop;
constexpr const char* AcceleratorControllerImpl::kVolumeButtonSideBottom;
////////////////////////////////////////////////////////////////////////////////
// AcceleratorControllerImpl, public:
bool AcceleratorControllerImpl::should_show_shortcut_notification_ = true;
AcceleratorControllerImpl::TestApi::TestApi(
AcceleratorControllerImpl* controller)
: controller_(controller) {
DCHECK(controller_);
}
bool AcceleratorControllerImpl::TestApi::TriggerTabletModeVolumeAdjustTimer() {
if (!controller_->tablet_mode_volume_adjust_timer_.IsRunning())
return false;
controller_->tablet_mode_volume_adjust_timer_.FireNow();
return true;
}
void AcceleratorControllerImpl::TestApi::RegisterAccelerators(
const AcceleratorData accelerators[],
size_t accelerators_length) {
controller_->RegisterAccelerators(accelerators, accelerators_length);
}
bool AcceleratorControllerImpl::TestApi::IsActionForAcceleratorEnabled(
const ui::Accelerator& accelerator) {
return controller_->IsActionForAcceleratorEnabled(accelerator);
}
const DeprecatedAcceleratorData*
AcceleratorControllerImpl::TestApi::GetDeprecatedAcceleratorData(
AcceleratorAction action) {
auto it = controller_->actions_with_deprecations_.find(action);
if (it == controller_->actions_with_deprecations_.end())
return nullptr;
return it->second;
}
AccessibilityConfirmationDialog*
AcceleratorControllerImpl::TestApi::GetConfirmationDialog() {
return controller_->confirmation_dialog_.get();
}
ExitWarningHandler*
AcceleratorControllerImpl::TestApi::GetExitWarningHandler() {
return &controller_->exit_warning_handler_;
}
void AcceleratorControllerImpl::TestApi::SetSideVolumeButtonFilePath(
base::FilePath path) {
controller_->side_volume_button_location_file_path_ = path;
controller_->ParseSideVolumeButtonLocationInfo();
}
void AcceleratorControllerImpl::TestApi::SetSideVolumeButtonLocation(
const std::string& region,
const std::string& side) {
controller_->side_volume_button_location_.region = region;
controller_->side_volume_button_location_.side = side;
}
AcceleratorControllerImpl::AcceleratorControllerImpl()
: accelerator_manager_(std::make_unique<ui::AcceleratorManager>()),
accelerator_history_(std::make_unique<AcceleratorHistoryImpl>()),
side_volume_button_location_file_path_(
base::FilePath(kSideVolumeButtonLocationFilePath)) {
if (::features::IsImprovedKeyboardShortcutsEnabled()) {
// Observe input method changes to determine when to use positional
// shortcuts. Calling AddObserver will cause InputMethodChanged to be
// called once even when the method does not change.
InputMethodManager::Get()->AddObserver(this);
// Observe session changes.
Shell::Get()->session_controller()->AddObserver(this);
}
Init();
ParseSideVolumeButtonLocationInfo();
// Let AcceleratorHistory be a PreTargetHandler on aura::Env to ensure that it
// receives KeyEvents and MouseEvents. In some cases Shell PreTargetHandlers
// will handle Events before AcceleratorHistory gets to see them. This
// interferes with Accelerator processing. See https://crbug.com/1174603.
aura::Env::GetInstance()->AddPreTargetHandler(
accelerator_history_.get(), ui::EventTarget::Priority::kAccessibility);
}
AcceleratorControllerImpl::~AcceleratorControllerImpl() {
aura::Env::GetInstance()->RemovePreTargetHandler(accelerator_history_.get());
}
// static
void AcceleratorControllerImpl::RegisterProfilePrefs(
PrefRegistrySimple* registry) {
registry->RegisterIntegerPref(prefs::kImprovedShortcutsNotificationShownCount,
0);
}
void AcceleratorControllerImpl::OnActiveUserPrefServiceChanged(
PrefService* pref_service) {
DCHECK(pref_service);
if (::features::IsImprovedKeyboardShortcutsEnabled()) {
if (should_show_shortcut_notification_ &&
ShouldShowStartupNotificationForCurrentUser()) {
NotifyShortcutChangesInRelease(pref_service);
}
}
}
void AcceleratorControllerImpl::InputMethodChanged(InputMethodManager* manager,
Profile* profile,
bool show_message) {
DCHECK(::features::IsImprovedKeyboardShortcutsEnabled());
DCHECK(manager);
// InputMethodChanged will be called as soon as the observer is registered
// from Init(), so these settings get propagated before any keys are
// seen.
const bool use_positional_lookup =
manager->ArePositionalShortcutsUsedByCurrentInputMethod();
accelerators_.set_use_positional_lookup(use_positional_lookup);
accelerator_manager_->SetUsePositionalLookup(use_positional_lookup);
}
void AcceleratorControllerImpl::Register(
const std::vector<ui::Accelerator>& accelerators,
ui::AcceleratorTarget* target) {
accelerator_manager_->Register(
accelerators, ui::AcceleratorManager::kNormalPriority, target);
}
void AcceleratorControllerImpl::Unregister(const ui::Accelerator& accelerator,
ui::AcceleratorTarget* target) {
accelerator_manager_->Unregister(accelerator, target);
}
void AcceleratorControllerImpl::UnregisterAll(ui::AcceleratorTarget* target) {
accelerator_manager_->UnregisterAll(target);
}
bool AcceleratorControllerImpl::Process(const ui::Accelerator& accelerator) {
return accelerator_manager_->Process(accelerator);
}
bool AcceleratorControllerImpl::IsDeprecated(
const ui::Accelerator& accelerator) const {
return base::Contains(deprecated_accelerators_, accelerator);
}
bool AcceleratorControllerImpl::PerformActionIfEnabled(
AcceleratorAction action,
const ui::Accelerator& accelerator) {
if (CanPerformAction(action, accelerator)) {
PerformAction(action, accelerator);
return true;
}
return false;
}
bool AcceleratorControllerImpl::OnMenuAccelerator(
const ui::Accelerator& accelerator) {
accelerator_history_->StoreCurrentAccelerator(accelerator);
// Menu shouldn't be closed for an invalid accelerator.
AcceleratorAction* action_ptr = accelerators_.Find(accelerator);
return action_ptr && !base::Contains(actions_keeping_menu_open_, *action_ptr);
}
bool AcceleratorControllerImpl::IsRegistered(
const ui::Accelerator& accelerator) const {
return accelerator_manager_->IsRegistered(accelerator);
}
AcceleratorHistoryImpl* AcceleratorControllerImpl::GetAcceleratorHistory() {
return accelerator_history_.get();
}
bool AcceleratorControllerImpl::DoesAcceleratorMatchAction(
const ui::Accelerator& accelerator,
AcceleratorAction action) {
AcceleratorAction* action_ptr = accelerators_.Find(accelerator);
return action_ptr && *action_ptr == action;
}
bool AcceleratorControllerImpl::IsPreferred(
const ui::Accelerator& accelerator) const {
const AcceleratorAction* action_ptr = accelerators_.Find(accelerator);
return action_ptr && base::Contains(preferred_actions_, *action_ptr);
}
bool AcceleratorControllerImpl::IsReserved(
const ui::Accelerator& accelerator) const {
const AcceleratorAction* action_ptr = accelerators_.Find(accelerator);
return action_ptr && base::Contains(reserved_actions_, *action_ptr);
}
////////////////////////////////////////////////////////////////////////////////
// AcceleratorControllerImpl, ui::AcceleratorTarget implementation:
bool AcceleratorControllerImpl::AcceleratorPressed(
const ui::Accelerator& accelerator) {
AcceleratorAction action = accelerators_.Get(accelerator);
if (!CanPerformAction(action, accelerator))
return false;
// Handling the deprecated accelerators (if any) only if action can be
// performed.
if (MaybeDeprecatedAcceleratorPressed(action, accelerator) ==
AcceleratorProcessingStatus::STOP) {
return false;
}
PerformAction(action, accelerator);
return ShouldActionConsumeKeyEvent(action);
}
bool AcceleratorControllerImpl::CanHandleAccelerators() const {
return true;
}
///////////////////////////////////////////////////////////////////////////////
// AcceleratorControllerImpl, private:
void AcceleratorControllerImpl::Init() {
for (size_t i = 0; i < kActionsAllowedAtLoginOrLockScreenLength; ++i) {
actions_allowed_at_login_screen_.insert(
kActionsAllowedAtLoginOrLockScreen[i]);
actions_allowed_at_lock_screen_.insert(
kActionsAllowedAtLoginOrLockScreen[i]);
}
for (size_t i = 0; i < kActionsAllowedAtLockScreenLength; ++i)
actions_allowed_at_lock_screen_.insert(kActionsAllowedAtLockScreen[i]);
for (size_t i = 0; i < kActionsAllowedAtPowerMenuLength; ++i)
actions_allowed_at_power_menu_.insert(kActionsAllowedAtPowerMenu[i]);
for (size_t i = 0; i < kActionsAllowedAtModalWindowLength; ++i)
actions_allowed_at_modal_window_.insert(kActionsAllowedAtModalWindow[i]);
for (size_t i = 0; i < kPreferredActionsLength; ++i)
preferred_actions_.insert(kPreferredActions[i]);
for (size_t i = 0; i < kReservedActionsLength; ++i)
reserved_actions_.insert(kReservedActions[i]);
for (size_t i = 0; i < kRepeatableActionsLength; ++i)
repeatable_actions_.insert(kRepeatableActions[i]);
for (size_t i = 0; i < kActionsAllowedInAppModeOrPinnedModeLength; ++i) {
actions_allowed_in_app_mode_.insert(
kActionsAllowedInAppModeOrPinnedMode[i]);
actions_allowed_in_pinned_mode_.insert(
kActionsAllowedInAppModeOrPinnedMode[i]);
}
for (size_t i = 0; i < kActionsAllowedInPinnedModeLength; ++i)
actions_allowed_in_pinned_mode_.insert(kActionsAllowedInPinnedMode[i]);
for (size_t i = 0; i < kActionsAllowedInAppModeLength; ++i)
actions_allowed_in_app_mode_.insert(kActionsAllowedInAppMode[i]);
for (size_t i = 0; i < kActionsNeedingWindowLength; ++i)
actions_needing_window_.insert(kActionsNeedingWindow[i]);
for (size_t i = 0; i < kActionsKeepingMenuOpenLength; ++i)
actions_keeping_menu_open_.insert(kActionsKeepingMenuOpen[i]);
RegisterAccelerators(kAcceleratorData, kAcceleratorDataLength);
if (::features::IsImprovedKeyboardShortcutsEnabled()) {
RegisterAccelerators(kEnableWithPositionalAcceleratorsData,
kEnableWithPositionalAcceleratorsDataLength);
} else if (::features::IsNewShortcutMappingEnabled()) {
RegisterAccelerators(kEnableWithNewMappingAcceleratorData,
kEnableWithNewMappingAcceleratorDataLength);
} else {
RegisterAccelerators(kDisableWithNewMappingAcceleratorData,
kDisableWithNewMappingAcceleratorDataLength);
}
RegisterDeprecatedAccelerators();
if (debug::DebugAcceleratorsEnabled()) {
RegisterAccelerators(kDebugAcceleratorData, kDebugAcceleratorDataLength);
// All debug accelerators are reserved.
for (size_t i = 0; i < kDebugAcceleratorDataLength; ++i)
reserved_actions_.insert(kDebugAcceleratorData[i].action);
}
if (debug::DeveloperAcceleratorsEnabled()) {
RegisterAccelerators(kDeveloperAcceleratorData,
kDeveloperAcceleratorDataLength);
// Developer accelerators are also reserved.
for (size_t i = 0; i < kDeveloperAcceleratorDataLength; ++i)
reserved_actions_.insert(kDeveloperAcceleratorData[i].action);
}
}
void AcceleratorControllerImpl::RegisterAccelerators(
const AcceleratorData accelerators[],
size_t accelerators_length) {
std::vector<ui::Accelerator> ui_accelerators;
for (size_t i = 0; i < accelerators_length; ++i) {
ui::Accelerator accelerator =
CreateAccelerator(accelerators[i].keycode, accelerators[i].modifiers,
accelerators[i].trigger_on_press);
ui_accelerators.push_back(accelerator);
accelerators_.InsertNew(
std::make_pair(accelerator, accelerators[i].action));
}
Register(ui_accelerators, this);
}
void AcceleratorControllerImpl::RegisterDeprecatedAccelerators() {
for (size_t i = 0; i < kDeprecatedAcceleratorsDataLength; ++i) {
const DeprecatedAcceleratorData* data = &kDeprecatedAcceleratorsData[i];
actions_with_deprecations_[data->action] = data;
}
std::vector<ui::Accelerator> ui_accelerators;
for (size_t i = 0; i < kDeprecatedAcceleratorsLength; ++i) {
const AcceleratorData& accelerator_data = kDeprecatedAccelerators[i];
const ui::Accelerator deprecated_accelerator =
CreateAccelerator(accelerator_data.keycode, accelerator_data.modifiers,
accelerator_data.trigger_on_press);
ui_accelerators.push_back(deprecated_accelerator);
accelerators_.InsertNew(
std::make_pair(deprecated_accelerator, accelerator_data.action));
deprecated_accelerators_.insert(deprecated_accelerator);
}
Register(ui_accelerators, this);
}
bool AcceleratorControllerImpl::IsActionForAcceleratorEnabled(
const ui::Accelerator& accelerator) const {
const AcceleratorAction* action_ptr = accelerators_.Find(accelerator);
return action_ptr && CanPerformAction(*action_ptr, accelerator);
}
bool AcceleratorControllerImpl::CanPerformAction(
AcceleratorAction action,
const ui::Accelerator& accelerator) const {
if (accelerator.IsRepeat() && !base::Contains(repeatable_actions_, action))
return false;
AcceleratorProcessingRestriction restriction =
GetAcceleratorProcessingRestriction(action);
if (restriction != RESTRICTION_NONE)
return restriction == RESTRICTION_PREVENT_PROCESSING_AND_PROPAGATION;
const ui::Accelerator& previous_accelerator =
accelerator_history_->previous_accelerator();
// True should be returned if running |action| does something. Otherwise,
// false should be returned to give the web contents a chance at handling the
// accelerator.
switch (action) {
case CYCLE_BACKWARD_MRU:
case CYCLE_FORWARD_MRU:
return CanHandleCycleMru(accelerator);
case DESKS_ACTIVATE_DESK_LEFT:
case DESKS_ACTIVATE_DESK_RIGHT:
case DESKS_MOVE_ACTIVE_ITEM_LEFT:
case DESKS_MOVE_ACTIVE_ITEM_RIGHT:
case DESKS_NEW_DESK:
case DESKS_REMOVE_CURRENT_DESK:
return true;
case DEBUG_MICROPHONE_MUTE_TOGGLE:
case DEBUG_PRINT_LAYER_HIERARCHY:
case DEBUG_PRINT_VIEW_HIERARCHY:
case DEBUG_PRINT_WINDOW_HIERARCHY:
case DEBUG_SHOW_TOAST:
case DEBUG_TOGGLE_SHOW_DEBUG_BORDERS:
case DEBUG_TOGGLE_SHOW_FPS_COUNTER:
case DEBUG_TOGGLE_SHOW_PAINT_RECTS:
case DEBUG_TOGGLE_TOUCH_PAD:
case DEBUG_TOGGLE_TOUCH_SCREEN:
case DEBUG_TOGGLE_TABLET_MODE:
case DEBUG_TOGGLE_WALLPAPER_MODE:
case DEBUG_TRIGGER_CRASH:
case DEBUG_TOGGLE_HUD_DISPLAY:
return debug::DebugAcceleratorsEnabled();
case DEV_ADD_REMOVE_DISPLAY:
case DEV_TOGGLE_APP_LIST:
case DEV_TOGGLE_UNIFIED_DESKTOP:
return debug::DeveloperAcceleratorsEnabled();
case DISABLE_CAPS_LOCK:
return CanHandleDisableCapsLock(previous_accelerator);
case LOCK_SCREEN:
return CanHandleLock();
case MAGNIFIER_ZOOM_IN:
case MAGNIFIER_ZOOM_OUT:
return CanHandleActiveMagnifierZoom();
case MICROPHONE_MUTE_TOGGLE:
return true;
case MOVE_ACTIVE_WINDOW_BETWEEN_DISPLAYS:
return display_move_window_util::
CanHandleMoveActiveWindowBetweenDisplays();
case NEW_INCOGNITO_WINDOW:
return CanHandleNewIncognitoWindow();
case PRIVACY_SCREEN_TOGGLE:
return CanHandleTogglePrivacyScreen();
case ROTATE_SCREEN:
return true;
case SCALE_UI_DOWN:
case SCALE_UI_RESET:
case SCALE_UI_UP:
return true;
case SHOW_STYLUS_TOOLS:
return CanHandleShowStylusTools();
case START_AMBIENT_MODE:
return CanHandleStartAmbientMode();
case START_ASSISTANT:
return true;
case SWAP_PRIMARY_DISPLAY:
return display::Screen::GetScreen()->GetNumDisplays() > 1;
case SWITCH_IME:
return CanHandleSwitchIme(accelerator);
case SWITCH_TO_NEXT_IME:
return CanCycleInputMethod();
case SWITCH_TO_LAST_USED_IME:
return CanCycleInputMethod();
case SWITCH_TO_PREVIOUS_USER:
case SWITCH_TO_NEXT_USER:
return CanHandleCycleUser();
case TOGGLE_APP_LIST:
case TOGGLE_APP_LIST_FULLSCREEN:
return CanHandleToggleAppList(
accelerator, previous_accelerator,
accelerator_history_->currently_pressed_keys());
case TOGGLE_CAPS_LOCK:
return CanHandleToggleCapsLock(
accelerator, previous_accelerator,
accelerator_history_->currently_pressed_keys());
case TOGGLE_CLIPBOARD_HISTORY:
return CanHandleToggleClipboardHistory();
case TOGGLE_DICTATION:
return CanHandleToggleDictation();
case TOGGLE_DOCKED_MAGNIFIER:
return true;
case TOGGLE_FULLSCREEN_MAGNIFIER:
return true;
case TOGGLE_MESSAGE_CENTER_BUBBLE:
return true;
case TOGGLE_MIRROR_MODE:
return true;
case TOGGLE_OVERVIEW:
return CanHandleToggleOverview();
case TOUCH_HUD_CLEAR:
case TOUCH_HUD_MODE_CHANGE:
return CanHandleTouchHud();
case UNPIN:
return CanUnpinWindow();
case WINDOW_CYCLE_SNAP_LEFT:
case WINDOW_CYCLE_SNAP_RIGHT:
return CanHandleWindowSnap();
case FOCUS_PIP:
return !!FindPipWidget();
case MINIMIZE_TOP_WINDOW_ON_BACK:
return window_util::ShouldMinimizeTopWindowOnBack();
case TAKE_PARTIAL_SCREENSHOT:
case TAKE_SCREENSHOT:
case TAKE_WINDOW_SCREENSHOT:
return CanHandleScreenshot(action);
case TOGGLE_RESIZE_LOCK_MENU:
return CanHandleToggleResizeLockMenu();
case TOGGLE_FLOATING:
return CanHandleToggleFloatingWindow();
// The following are always enabled.
case BRIGHTNESS_DOWN:
case BRIGHTNESS_UP:
case EXIT:
case FOCUS_NEXT_PANE:
case FOCUS_PREVIOUS_PANE:
case FOCUS_SHELF:
case KEYBOARD_BRIGHTNESS_DOWN:
case KEYBOARD_BRIGHTNESS_UP:
case LAUNCH_APP_0:
case LAUNCH_APP_1:
case LAUNCH_APP_2:
case LAUNCH_APP_3:
case LAUNCH_APP_4:
case LAUNCH_APP_5:
case LAUNCH_APP_6:
case LAUNCH_APP_7:
case LAUNCH_LAST_APP:
case LOCK_PRESSED:
case LOCK_RELEASED:
case MEDIA_FAST_FORWARD:
case MEDIA_NEXT_TRACK:
case MEDIA_PAUSE:
case MEDIA_PLAY:
case MEDIA_PLAY_PAUSE:
case MEDIA_PREV_TRACK:
case MEDIA_REWIND:
case MEDIA_STOP:
case NEW_TAB:
case NEW_WINDOW:
case OPEN_CALCULATOR:
case OPEN_CROSH:
case OPEN_DIAGNOSTICS:
case OPEN_FEEDBACK_PAGE:
case OPEN_FILE_MANAGER:
case OPEN_GET_HELP:
case POWER_PRESSED:
case POWER_RELEASED:
case PRINT_UI_HIERARCHIES:
case RESTORE_TAB:
case ROTATE_WINDOW:
case SHOW_EMOJI_PICKER:
case SHOW_IME_MENU_BUBBLE:
case SHOW_SHORTCUT_VIEWER:
case SHOW_TASK_MANAGER:
case SUSPEND:
case TOGGLE_FULLSCREEN:
case TOGGLE_HIGH_CONTRAST:
case TOGGLE_MAXIMIZED:
case TOGGLE_SPOKEN_FEEDBACK:
case TOGGLE_SYSTEM_TRAY_BUBBLE:
case TOGGLE_WIFI:
case VOLUME_DOWN:
case VOLUME_MUTE:
case VOLUME_UP:
case WINDOW_MINIMIZE:
return true;
}
}
void AcceleratorControllerImpl::PerformAction(
AcceleratorAction action,
const ui::Accelerator& accelerator) {
AcceleratorProcessingRestriction restriction =
GetAcceleratorProcessingRestriction(action);
if (restriction != RESTRICTION_NONE)
return;
if ((action == VOLUME_DOWN || action == VOLUME_UP) &&
Shell::Get()->tablet_mode_controller()->InTabletMode()) {
if (ShouldSwapSideVolumeButtons(accelerator.source_device_id()))
action = action == VOLUME_DOWN ? VOLUME_UP : VOLUME_DOWN;
StartTabletModeVolumeAdjustTimer(action);
}
// If your accelerator invokes more than one line of code, please either
// implement it in your module's controller code or pull it into a HandleFoo()
// function above.
switch (action) {
case BRIGHTNESS_DOWN: {
BrightnessControlDelegate* delegate =
Shell::Get()->brightness_control_delegate();
if (delegate)
delegate->HandleBrightnessDown(accelerator);
break;
}
case BRIGHTNESS_UP: {
BrightnessControlDelegate* delegate =
Shell::Get()->brightness_control_delegate();
if (delegate)
delegate->HandleBrightnessUp(accelerator);
break;
}
case CYCLE_BACKWARD_MRU:
RecordCycleBackwardMru(accelerator);
accelerators::CycleBackwardMru();
break;
case CYCLE_FORWARD_MRU:
RecordCycleForwardMru(accelerator);
accelerators::CycleForwardMru();
break;
case DESKS_ACTIVATE_DESK_LEFT:
HandleActivateDesk(accelerator, /*activate_left=*/true);
break;
case DESKS_ACTIVATE_DESK_RIGHT:
HandleActivateDesk(accelerator, /*activate_left=*/false);
break;
case DESKS_MOVE_ACTIVE_ITEM_LEFT:
HandleMoveActiveItem(accelerator, /*going_left=*/true);
break;
case DESKS_MOVE_ACTIVE_ITEM_RIGHT:
HandleMoveActiveItem(accelerator, /*going_left=*/false);
break;
case DESKS_NEW_DESK:
HandleNewDesk();
break;
case DESKS_REMOVE_CURRENT_DESK:
HandleRemoveCurrentDesk();
break;
case DEBUG_MICROPHONE_MUTE_TOGGLE:
base::RecordAction(base::UserMetricsAction("Accel_Microphone_Mute"));
accelerators::MicrophoneMuteToggle();
break;
case DEBUG_PRINT_LAYER_HIERARCHY:
case DEBUG_PRINT_VIEW_HIERARCHY:
case DEBUG_PRINT_WINDOW_HIERARCHY:
case DEBUG_SHOW_TOAST:
debug::PerformDebugActionIfEnabled(action);
break;
case DEBUG_TOGGLE_SHOW_DEBUG_BORDERS:
debug::ToggleShowDebugBorders();
break;
case DEBUG_TOGGLE_SHOW_FPS_COUNTER:
debug::ToggleShowFpsCounter();
break;
case DEBUG_TOGGLE_SHOW_PAINT_RECTS:
debug::ToggleShowPaintRects();
break;
case DEBUG_TOGGLE_TOUCH_PAD:
case DEBUG_TOGGLE_TOUCH_SCREEN:
case DEBUG_TOGGLE_TABLET_MODE:
case DEBUG_TOGGLE_WALLPAPER_MODE:
case DEBUG_TRIGGER_CRASH:
case DEBUG_TOGGLE_HUD_DISPLAY:
debug::PerformDebugActionIfEnabled(action);
break;
case DEV_ADD_REMOVE_DISPLAY:
Shell::Get()->display_manager()->AddRemoveDisplay();
break;
case DEV_TOGGLE_APP_LIST:
HandleToggleAppList(accelerator, kSearchKey);
break;
case DEV_TOGGLE_UNIFIED_DESKTOP:
HandleToggleUnifiedDesktop();
break;
case DISABLE_CAPS_LOCK:
base::RecordAction(base::UserMetricsAction("Accel_Disable_Caps_Lock"));
accelerators::DisableCapsLock();
break;
case EXIT:
// UMA metrics are recorded in the handler.
exit_warning_handler_.HandleAccelerator();
break;
case FOCUS_NEXT_PANE:
HandleRotatePaneFocus(FocusCycler::FORWARD);
break;
case FOCUS_PREVIOUS_PANE:
HandleRotatePaneFocus(FocusCycler::BACKWARD);
break;
case FOCUS_SHELF:
HandleFocusShelf();
break;
case FOCUS_PIP:
base::RecordAction(base::UserMetricsAction("Accel_Focus_Pip"));
accelerators::FocusPip();
break;
case KEYBOARD_BRIGHTNESS_DOWN: {
KeyboardBrightnessControlDelegate* delegate =
Shell::Get()->keyboard_brightness_control_delegate();
if (delegate)
delegate->HandleKeyboardBrightnessDown(accelerator);
break;
}
case KEYBOARD_BRIGHTNESS_UP: {
KeyboardBrightnessControlDelegate* delegate =
Shell::Get()->keyboard_brightness_control_delegate();
if (delegate)
delegate->HandleKeyboardBrightnessUp(accelerator);
break;
}
case LAUNCH_APP_0:
base::RecordAction(base::UserMetricsAction("Accel_Launch_App"));
accelerators::LaunchAppN(0);
break;
case LAUNCH_APP_1:
base::RecordAction(base::UserMetricsAction("Accel_Launch_App"));
accelerators::LaunchAppN(1);
break;
case LAUNCH_APP_2:
base::RecordAction(base::UserMetricsAction("Accel_Launch_App"));
accelerators::LaunchAppN(2);
break;
case LAUNCH_APP_3:
base::RecordAction(base::UserMetricsAction("Accel_Launch_App"));
accelerators::LaunchAppN(3);
break;
case LAUNCH_APP_4:
base::RecordAction(base::UserMetricsAction("Accel_Launch_App"));
accelerators::LaunchAppN(4);
break;
case LAUNCH_APP_5:
base::RecordAction(base::UserMetricsAction("Accel_Launch_App"));
accelerators::LaunchAppN(5);
break;
case LAUNCH_APP_6:
base::RecordAction(base::UserMetricsAction("Accel_Launch_App"));
accelerators::LaunchAppN(6);
break;
case LAUNCH_APP_7:
base::RecordAction(base::UserMetricsAction("Accel_Launch_App"));
accelerators::LaunchAppN(7);
break;
case LAUNCH_LAST_APP:
base::RecordAction(base::UserMetricsAction("Accel_Launch_Last_App"));
accelerators::LaunchLastApp();
break;
case LOCK_PRESSED:
case LOCK_RELEASED:
Shell::Get()->power_button_controller()->OnLockButtonEvent(
action == LOCK_PRESSED, base::TimeTicks());
break;
case LOCK_SCREEN:
base::RecordAction(base::UserMetricsAction("Accel_LockScreen_L"));
accelerators::LockScreen();
break;
case MAGNIFIER_ZOOM_IN:
HandleActiveMagnifierZoom(1);
break;
case MAGNIFIER_ZOOM_OUT:
HandleActiveMagnifierZoom(-1);
break;
case MEDIA_FAST_FORWARD:
base::RecordAction(base::UserMetricsAction("Accel_Media_Fast_Forward"));
accelerators::MediaFastForward();
break;
case MEDIA_NEXT_TRACK:
base::RecordAction(base::UserMetricsAction("Accel_Media_Next_Track"));
accelerators::MediaNextTrack();
break;
case MEDIA_PAUSE:
base::RecordAction(base::UserMetricsAction("Accel_Media_Pause"));
accelerators::MediaPause();
break;
case MEDIA_PLAY:
base::RecordAction(base::UserMetricsAction("Accel_Media_Play"));
accelerators::MediaPlay();
break;
case MEDIA_PLAY_PAUSE:
base::RecordAction(base::UserMetricsAction("Accel_Media_PlayPause"));
accelerators::MediaPlayPause();
break;
case MEDIA_PREV_TRACK:
base::RecordAction(base::UserMetricsAction("Accel_Media_Prev_Track"));
accelerators::MediaPrevTrack();
break;
case MEDIA_REWIND:
base::RecordAction(base::UserMetricsAction("Accel_Media_Rewind"));
accelerators::MediaRewind();
break;
case MEDIA_STOP:
base::RecordAction(base::UserMetricsAction("Accel_Media_Stop"));
accelerators::MediaStop();
break;
case MICROPHONE_MUTE_TOGGLE:
base::RecordAction(base::UserMetricsAction("Accel_Microphone_Mute"));
accelerators::MicrophoneMuteToggle();
break;
case MOVE_ACTIVE_WINDOW_BETWEEN_DISPLAYS:
display_move_window_util::HandleMoveActiveWindowBetweenDisplays();
break;
case NEW_INCOGNITO_WINDOW:
base::RecordAction(base::UserMetricsAction("Accel_New_Incognito_Window"));
accelerators::NewIncognitoWindow();
break;
case NEW_TAB:
HandleNewTab(accelerator);
break;
case NEW_WINDOW:
base::RecordAction(base::UserMetricsAction("Accel_New_Window"));
accelerators::NewWindow();
break;
case OPEN_CALCULATOR:
base::RecordAction(base::UserMetricsAction("Accel_Open_Calculator"));
accelerators::OpenCalculator();
break;
case OPEN_CROSH:
base::RecordAction(base::UserMetricsAction("Accel_Open_Crosh"));
accelerators::OpenCrosh();
break;
case OPEN_DIAGNOSTICS:
base::RecordAction(base::UserMetricsAction("Accel_Open_Diagnostics"));
accelerators::OpenDiagnostics();
break;
case OPEN_FEEDBACK_PAGE:
base::RecordAction(base::UserMetricsAction("Accel_Open_Feedback_Page"));
accelerators::OpenFeedbackPage();
break;
case OPEN_FILE_MANAGER:
base::RecordAction(base::UserMetricsAction("Accel_Open_File_Manager"));
accelerators::OpenFileManager();
break;
case OPEN_GET_HELP:
accelerators::OpenHelp();
break;
case POWER_PRESSED:
case POWER_RELEASED:
if (!base::SysInfo::IsRunningOnChromeOS()) {
// There is no powerd, the Chrome OS power manager, in linux desktop,
// so call the PowerButtonController here.
Shell::Get()->power_button_controller()->OnPowerButtonEvent(
action == POWER_PRESSED, base::TimeTicks());
}
// We don't do anything with these at present on the device,
// (power button events are reported to us from powerm via
// D-BUS), but we consume them to prevent them from getting
// passed to apps -- see http://crbug.com/146609.
break;
case PRINT_UI_HIERARCHIES:
debug::PrintUIHierarchies();
break;
case PRIVACY_SCREEN_TOGGLE:
HandleTogglePrivacyScreen();
break;
case ROTATE_SCREEN:
HandleRotateScreen();
break;
case RESTORE_TAB:
base::RecordAction(base::UserMetricsAction("Accel_Restore_Tab"));
accelerators::RestoreTab();
break;
case ROTATE_WINDOW:
HandleRotateActiveWindow();
break;
case SCALE_UI_DOWN:
accelerators::ZoomDisplay(false /* down */);
break;
case SCALE_UI_RESET:
accelerators::ResetDisplayZoom();
break;
case SCALE_UI_UP:
accelerators::ZoomDisplay(true /* up */);
break;
case SHOW_EMOJI_PICKER:
HandleShowEmojiPicker();
break;
case SHOW_IME_MENU_BUBBLE:
HandleShowImeMenuBubble();
break;
case SHOW_SHORTCUT_VIEWER:
HandleShowKeyboardShortcutViewer();
break;
case SHOW_STYLUS_TOOLS:
HandleShowStylusTools();
break;
case SHOW_TASK_MANAGER:
HandleShowTaskManager();
break;
case START_AMBIENT_MODE:
HandleToggleAmbientMode(accelerator);
break;
case START_ASSISTANT:
HandleToggleAssistant(accelerator);
break;
case SUSPEND:
HandleSuspend();
break;
case SWAP_PRIMARY_DISPLAY:
HandleSwapPrimaryDisplay();
break;
case SWITCH_IME:
HandleSwitchIme(accelerator);
break;
case SWITCH_TO_LAST_USED_IME:
HandleSwitchToLastUsedIme(accelerator);
break;
case SWITCH_TO_NEXT_IME:
HandleSwitchToNextIme(accelerator);
break;
case SWITCH_TO_NEXT_USER:
HandleCycleUser(CycleUserDirection::NEXT);
break;
case SWITCH_TO_PREVIOUS_USER:
HandleCycleUser(CycleUserDirection::PREVIOUS);
break;
case TAKE_PARTIAL_SCREENSHOT:
HandleTakePartialScreenshot();
break;
case TAKE_SCREENSHOT:
HandleTakeScreenshot(accelerator.key_code());
break;
case TAKE_WINDOW_SCREENSHOT:
HandleTakeWindowScreenshot();
break;
case TOGGLE_APP_LIST:
HandleToggleAppList(accelerator, kSearchKey);
break;
case TOGGLE_APP_LIST_FULLSCREEN:
HandleToggleAppList(accelerator, kSearchKeyFullscreen);
break;
case TOGGLE_CAPS_LOCK:
HandleToggleCapsLock();
break;
case TOGGLE_CLIPBOARD_HISTORY:
HandleToggleClipboardHistory();
break;
case TOGGLE_DICTATION:
HandleToggleDictation();
break;
case TOGGLE_DOCKED_MAGNIFIER:
HandleToggleDockedMagnifier();
break;
case TOGGLE_FLOATING:
HandleToggleFloating();
break;
case TOGGLE_FULLSCREEN:
HandleToggleFullscreen(accelerator);
break;
case TOGGLE_FULLSCREEN_MAGNIFIER:
HandleToggleFullscreenMagnifier();
break;
case TOGGLE_HIGH_CONTRAST:
HandleToggleHighContrast();
break;
case TOGGLE_MAXIMIZED:
accelerators::ToggleMaximized();
break;
case TOGGLE_MESSAGE_CENTER_BUBBLE:
HandleToggleMessageCenterBubble();
break;
case TOGGLE_MIRROR_MODE:
HandleToggleMirrorMode();
break;
case TOGGLE_OVERVIEW:
HandleToggleOverview();
break;
case TOGGLE_RESIZE_LOCK_MENU:
base::RecordAction(
base::UserMetricsAction("Accel_Toggle_Resize_Lock_Menu"));
accelerators::ToggleResizeLockMenu();
break;
case TOGGLE_SPOKEN_FEEDBACK:
HandleToggleSpokenFeedback();
break;
case TOGGLE_SYSTEM_TRAY_BUBBLE:
HandleToggleSystemTrayBubble();
break;
case TOGGLE_WIFI:
Shell::Get()->system_tray_notifier()->NotifyRequestToggleWifi();
break;
case TOUCH_HUD_CLEAR:
HandleTouchHudClear();
break;
case TOUCH_HUD_MODE_CHANGE:
HandleTouchHudModeChange();
break;
case UNPIN:
accelerators::UnpinWindow();
break;
case VOLUME_DOWN:
HandleVolumeDown();
break;
case VOLUME_MUTE:
HandleVolumeMute(accelerator);
break;
case VOLUME_UP:
HandleVolumeUp();
break;
case WINDOW_CYCLE_SNAP_LEFT:
case WINDOW_CYCLE_SNAP_RIGHT:
HandleWindowSnap(action);
break;
case WINDOW_MINIMIZE:
HandleWindowMinimize();
break;
case MINIMIZE_TOP_WINDOW_ON_BACK:
HandleTopWindowMinimizeOnBack();
break;
}
NotifyActionPerformed(action);
// Reset any in progress composition.
if (::features::IsImprovedKeyboardShortcutsEnabled()) {
auto* input_method =
Shell::Get()->window_tree_host_manager()->input_method();
input_method->CancelComposition(input_method->GetTextInputClient());
}
}
bool AcceleratorControllerImpl::ShouldActionConsumeKeyEvent(
AcceleratorAction action) {
// Adding new exceptions is *STRONGLY* discouraged.
return true;
}
AcceleratorControllerImpl::AcceleratorProcessingRestriction
AcceleratorControllerImpl::GetAcceleratorProcessingRestriction(
int action) const {
if (Shell::Get()->screen_pinning_controller()->IsPinned() &&
!base::Contains(actions_allowed_in_pinned_mode_, action)) {
return RESTRICTION_PREVENT_PROCESSING_AND_PROPAGATION;
}
if (!Shell::Get()->session_controller()->IsActiveUserSessionStarted() &&
!base::Contains(actions_allowed_at_login_screen_, action)) {
return RESTRICTION_PREVENT_PROCESSING;
}
if (Shell::Get()->session_controller()->IsScreenLocked() &&
!base::Contains(actions_allowed_at_lock_screen_, action)) {
return RESTRICTION_PREVENT_PROCESSING;
}
if (Shell::Get()->power_button_controller()->IsMenuOpened() &&
!base::Contains(actions_allowed_at_power_menu_, action)) {
return RESTRICTION_PREVENT_PROCESSING;
}
if (Shell::Get()->session_controller()->IsRunningInAppMode() &&
!base::Contains(actions_allowed_in_app_mode_, action)) {
return RESTRICTION_PREVENT_PROCESSING;
}
if (Shell::IsSystemModalWindowOpen() &&
!base::Contains(actions_allowed_at_modal_window_, action)) {
// Note we prevent the shortcut from propagating so it will not
// be passed to the modal window. This is important for things like
// Alt+Tab that would cause an undesired effect in the modal window by
// cycling through its window elements.
return RESTRICTION_PREVENT_PROCESSING_AND_PROPAGATION;
}
if (base::Contains(actions_needing_window_, action) &&
Shell::Get()
->mru_window_tracker()
->BuildMruWindowList(kActiveDesk)
.empty()) {
Shell::Get()->accessibility_controller()->TriggerAccessibilityAlert(
AccessibilityAlert::WINDOW_NEEDED);
return RESTRICTION_PREVENT_PROCESSING_AND_PROPAGATION;
}
return RESTRICTION_NONE;
}
AcceleratorControllerImpl::AcceleratorProcessingStatus
AcceleratorControllerImpl::MaybeDeprecatedAcceleratorPressed(
AcceleratorAction action,
const ui::Accelerator& accelerator) const {
auto itr = actions_with_deprecations_.find(action);
if (itr == actions_with_deprecations_.end()) {
// The action is not associated with any deprecated accelerators, and hence
// should be performed normally.
return AcceleratorProcessingStatus::PROCEED;
}
// This action is associated with new and deprecated accelerators, find which
// one is |accelerator|.
const DeprecatedAcceleratorData* data = itr->second;
if (!base::Contains(deprecated_accelerators_, accelerator)) {
// This is a new accelerator replacing the old deprecated one.
// Record UMA stats and proceed normally to perform it.
RecordUmaHistogram(data->uma_histogram_name, NEW_USED);
return AcceleratorProcessingStatus::PROCEED;
}
// This accelerator has been deprecated and should be treated according
// to its |DeprecatedAcceleratorData|.
// Record UMA stats.
RecordUmaHistogram(data->uma_histogram_name, DEPRECATED_USED);
// We always display the notification as long as this |data| entry exists.
ShowDeprecatedAcceleratorNotification(
data->uma_histogram_name, data->notification_message_id,
data->old_shortcut_id, data->new_shortcut_id);
if (!data->deprecated_enabled)
return AcceleratorProcessingStatus::STOP;
return AcceleratorProcessingStatus::PROCEED;
}
void AcceleratorControllerImpl::MaybeShowConfirmationDialog(
int window_title_text_id,
int dialog_text_id,
base::OnceClosure on_accept_callback,
base::OnceClosure on_cancel_callback) {
// An active dialog exists already.
if (confirmation_dialog_)
return;
auto* dialog = new AccessibilityConfirmationDialog(
l10n_util::GetStringUTF16(window_title_text_id),
l10n_util::GetStringUTF16(dialog_text_id), std::move(on_accept_callback),
std::move(on_cancel_callback), /* on close */ base::DoNothing());
confirmation_dialog_ = dialog->GetWeakPtr();
}
void AcceleratorControllerImpl::Shutdown() {
if (::features::IsImprovedKeyboardShortcutsEnabled()) {
InputMethodManager::Get()->RemoveObserver(this);
Shell::Get()->session_controller()->RemoveObserver(this);
}
}
bool AcceleratorControllerImpl::IsInternalKeyboardOrUncategorizedDevice(
int source_device_id) const {
if (source_device_id == ui::ED_UNKNOWN_DEVICE)
return false;
for (const ui::InputDevice& keyboard :
ui::DeviceDataManager::GetInstance()->GetKeyboardDevices()) {
if (keyboard.type == ui::InputDeviceType::INPUT_DEVICE_INTERNAL &&
keyboard.id == source_device_id) {
return true;
}
}
for (const ui::InputDevice& uncategorized_device :
ui::DeviceDataManager::GetInstance()->GetUncategorizedDevices()) {
if (uncategorized_device.id == source_device_id &&
uncategorized_device.type ==
ui::InputDeviceType::INPUT_DEVICE_INTERNAL) {
return true;
}
}
return false;
}
bool AcceleratorControllerImpl::IsValidSideVolumeButtonLocation() const {
const std::string region = side_volume_button_location_.region;
const std::string side = side_volume_button_location_.side;
if (region != kVolumeButtonRegionKeyboard &&
region != kVolumeButtonRegionScreen) {
return false;
}
if (side != kVolumeButtonSideLeft && side != kVolumeButtonSideRight &&
side != kVolumeButtonSideTop && side != kVolumeButtonSideBottom) {
return false;
}
return true;
}
bool AcceleratorControllerImpl::ShouldSwapSideVolumeButtons(
int source_device_id) const {
if (!IsInternalKeyboardOrUncategorizedDevice(source_device_id))
return false;
if (!IsValidSideVolumeButtonLocation())
return false;
chromeos::OrientationType screen_orientation =
Shell::Get()->screen_orientation_controller()->GetCurrentOrientation();
const std::string side = side_volume_button_location_.side;
const bool is_landscape_secondary_or_portrait_primary =
screen_orientation == chromeos::OrientationType::kLandscapeSecondary ||
screen_orientation == chromeos::OrientationType::kPortraitPrimary;
if (side_volume_button_location_.region == kVolumeButtonRegionKeyboard) {
if (side == kVolumeButtonSideLeft || side == kVolumeButtonSideRight)
return chromeos::IsPrimaryOrientation(screen_orientation);
return is_landscape_secondary_or_portrait_primary;
}
DCHECK_EQ(kVolumeButtonRegionScreen, side_volume_button_location_.region);
if (side == kVolumeButtonSideLeft || side == kVolumeButtonSideRight)
return !chromeos::IsPrimaryOrientation(screen_orientation);
return is_landscape_secondary_or_portrait_primary;
}
void AcceleratorControllerImpl::ParseSideVolumeButtonLocationInfo() {
std::string location_info;
const base::CommandLine* cl = base::CommandLine::ForCurrentProcess();
if (cl->HasSwitch(switches::kAshSideVolumeButtonPosition)) {
location_info =
cl->GetSwitchValueASCII(switches::kAshSideVolumeButtonPosition);
} else if (!base::PathExists(side_volume_button_location_file_path_) ||
!base::ReadFileToString(side_volume_button_location_file_path_,
&location_info) ||
location_info.empty()) {
return;
}
std::unique_ptr<base::DictionaryValue> info_in_dict =
base::DictionaryValue::From(
base::JSONReader::ReadDeprecated(location_info));
if (!info_in_dict) {
LOG(ERROR) << "JSONReader failed reading side volume button location info: "
<< location_info;
return;
}
info_in_dict->GetString(kVolumeButtonRegion,
&side_volume_button_location_.region);
info_in_dict->GetString(kVolumeButtonSide,
&side_volume_button_location_.side);
}
void AcceleratorControllerImpl::UpdateTabletModeVolumeAdjustHistogram() {
const int volume_percent = CrasAudioHandler::Get()->GetOutputVolumePercent();
if ((volume_adjust_starts_with_up_ &&
volume_percent >= initial_volume_percent_) ||
(!volume_adjust_starts_with_up_ &&
volume_percent <= initial_volume_percent_)) {
RecordTabletVolumeAdjustTypeHistogram(
TabletModeVolumeAdjustType::kNormalAdjustWithSwapEnabled);
} else {
RecordTabletVolumeAdjustTypeHistogram(
TabletModeVolumeAdjustType::kAccidentalAdjustWithSwapEnabled);
}
}
void AcceleratorControllerImpl::StartTabletModeVolumeAdjustTimer(
AcceleratorAction action) {
if (!tablet_mode_volume_adjust_timer_.IsRunning()) {
volume_adjust_starts_with_up_ = action == VOLUME_UP;
initial_volume_percent_ = CrasAudioHandler::Get()->GetOutputVolumePercent();
}
tablet_mode_volume_adjust_timer_.Start(
FROM_HERE, kVolumeAdjustTimeout, this,
&AcceleratorControllerImpl::UpdateTabletModeVolumeAdjustHistogram);
}
} // namespace ash