| // 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. |
| |
| #ifndef ASH_ACCELERATORS_ACCELERATOR_CONTROLLER_IMPL_H_ |
| #define ASH_ACCELERATORS_ACCELERATOR_CONTROLLER_IMPL_H_ |
| |
| #include <stddef.h> |
| |
| #include <map> |
| #include <memory> |
| #include <set> |
| #include <vector> |
| |
| #include "ash/accelerators/accelerator_history_impl.h" |
| #include "ash/accelerators/accelerator_table.h" |
| #include "ash/accelerators/exit_warning_handler.h" |
| #include "ash/accessibility/ui/accessibility_confirmation_dialog.h" |
| #include "ash/ash_export.h" |
| #include "ash/public/cpp/accelerators.h" |
| #include "ash/public/cpp/session/session_observer.h" |
| #include "base/gtest_prod_util.h" |
| #include "ui/base/accelerators/accelerator.h" |
| #include "ui/base/accelerators/accelerator_map.h" |
| #include "ui/base/ime/ash/input_method_manager.h" |
| |
| class PrefRegistrySimple; |
| |
| namespace ui { |
| class AcceleratorManager; |
| } |
| |
| namespace ash { |
| |
| struct AcceleratorData; |
| class ExitWarningHandler; |
| |
| // See TabletModeVolumeAdjustType at tools/metrics/histograms/enums.xml. |
| enum class TabletModeVolumeAdjustType { |
| kAccidentalAdjustWithSwapEnabled = 0, |
| kNormalAdjustWithSwapEnabled = 1, |
| kAccidentalAdjustWithSwapDisabled = 2, |
| kNormalAdjustWithSwapDisabled = 3, |
| kMaxValue = kNormalAdjustWithSwapDisabled, |
| }; |
| |
| // These values are persisted to logs. Entries should not be renumbered and |
| // numeric values should never be reused. |
| // Captures usage of Alt+[ and Alt+]. |
| enum class WindowSnapAcceleratorAction { |
| kCycleLeftSnapInClamshellNoOverview = 0, |
| kCycleLeftSnapInClamshellOverview = 1, |
| kCycleLeftSnapInTablet = 2, |
| kCycleRightSnapInClamshellNoOverview = 3, |
| kCycleRightSnapInClamshellOverview = 4, |
| kCycleRightSnapInTablet = 5, |
| kMaxValue = kCycleRightSnapInTablet, |
| }; |
| |
| // Histogram for volume adjustment in tablet mode. |
| ASH_EXPORT extern const char kTabletCountOfVolumeAdjustType[]; |
| |
| // UMA accessibility histogram names. |
| ASH_EXPORT extern const char kAccessibilityHighContrastShortcut[]; |
| ASH_EXPORT extern const char kAccessibilitySpokenFeedbackShortcut[]; |
| ASH_EXPORT extern const char kAccessibilityScreenMagnifierShortcut[]; |
| ASH_EXPORT extern const char kAccessibilityDockedMagnifierShortcut[]; |
| |
| // Name of histogram corresponding to |WindowSnapAcceleratorAction|. |
| ASH_EXPORT extern const char kAccelWindowSnap[]; |
| |
| // AcceleratorControllerImpl provides functions for registering or unregistering |
| // global keyboard accelerators, which are handled earlier than any windows. It |
| // also implements several handlers as an accelerator target. |
| class ASH_EXPORT AcceleratorControllerImpl |
| : public ui::AcceleratorTarget, |
| public AcceleratorController, |
| public SessionObserver, |
| public input_method::InputMethodManager::Observer { |
| public: |
| // Some Chrome OS devices have volume up and volume down buttons on their |
| // side. We want the button that's closer to the top/right to increase the |
| // volume and the button that's closer to the bottom/left to decrease the |
| // volume, so we use the buttons' location and the device orientation to |
| // determine whether the buttons should be swapped. |
| struct SideVolumeButtonLocation { |
| // The button can be at the side of the keyboard or the display. Then value |
| // of the region could be kVolumeButtonRegionKeyboard or |
| // kVolumeButtonRegionScreen. |
| std::string region; |
| // Side info of region. The value could be kVolumeButtonSideLeft, |
| // kVolumeButtonSideRight, kVolumeButtonSideTop or kVolumeButtonSideBottom. |
| std::string side; |
| }; |
| |
| // TestApi is used for tests to get internal implementation details. |
| class TestApi { |
| public: |
| explicit TestApi(AcceleratorControllerImpl* controller); |
| |
| TestApi(const TestApi&) = delete; |
| TestApi& operator=(const TestApi&) = delete; |
| |
| ~TestApi() = default; |
| |
| // If |controller_->tablet_mode_volume_adjust_timer_| is running, stops it, |
| // runs its task, and returns true. Otherwise returns false. |
| [[nodiscard]] bool TriggerTabletModeVolumeAdjustTimer(); |
| |
| // Registers the specified accelerators. |
| void RegisterAccelerators(const AcceleratorData accelerators[], |
| size_t accelerators_length); |
| |
| // Returns whether the action for this accelerator is enabled. |
| bool IsActionForAcceleratorEnabled(const ui::Accelerator& accelerator); |
| |
| // Returns the corresponding accelerator data if |action| maps to a |
| // deprecated accelerator, otherwise return nullptr. |
| const DeprecatedAcceleratorData* GetDeprecatedAcceleratorData( |
| AcceleratorAction action); |
| |
| // Accessor to accelerator confirmation dialog. |
| AccessibilityConfirmationDialog* GetConfirmationDialog(); |
| |
| // Provides access to the ExitWarningHandler. |
| ExitWarningHandler* GetExitWarningHandler(); |
| |
| AcceleratorControllerImpl::SideVolumeButtonLocation |
| side_volume_button_location() { |
| return controller_->side_volume_button_location_; |
| } |
| void SetSideVolumeButtonFilePath(base::FilePath path); |
| void SetSideVolumeButtonLocation(const std::string& region, |
| const std::string& side); |
| |
| private: |
| AcceleratorControllerImpl* controller_; // Not owned. |
| }; |
| |
| // Fields of the side volume button location info. |
| static constexpr const char* kVolumeButtonRegion = "region"; |
| static constexpr const char* kVolumeButtonSide = "side"; |
| |
| // Values of kVolumeButtonRegion. |
| static constexpr const char* kVolumeButtonRegionKeyboard = "keyboard"; |
| static constexpr const char* kVolumeButtonRegionScreen = "screen"; |
| // Values of kVolumeButtonSide. |
| static constexpr const char* kVolumeButtonSideLeft = "left"; |
| static constexpr const char* kVolumeButtonSideRight = "right"; |
| static constexpr const char* kVolumeButtonSideTop = "top"; |
| static constexpr const char* kVolumeButtonSideBottom = "bottom"; |
| |
| AcceleratorControllerImpl(); |
| AcceleratorControllerImpl(const AcceleratorControllerImpl&) = delete; |
| AcceleratorControllerImpl& operator=(const AcceleratorControllerImpl&) = |
| delete; |
| ~AcceleratorControllerImpl() override; |
| |
| static void RegisterProfilePrefs(PrefRegistrySimple* registry); |
| |
| // Allows overriding whether the new shortcuts notification should be shown |
| // for tests. |
| static void SetShouldShowShortcutNotificationForTest(bool value) { |
| should_show_shortcut_notification_ = value; |
| } |
| |
| // A list of possible ways in which an accelerator should be restricted before |
| // processing. Any target registered with this controller should respect |
| // restrictions by calling GetAcceleratorProcessingRestriction() during |
| // processing. |
| enum AcceleratorProcessingRestriction { |
| // Process the accelerator normally. |
| RESTRICTION_NONE, |
| |
| // Don't process the accelerator. |
| RESTRICTION_PREVENT_PROCESSING, |
| |
| // Don't process the accelerator and prevent propagation to other targets. |
| RESTRICTION_PREVENT_PROCESSING_AND_PROPAGATION |
| }; |
| |
| // SessionObserver overrides: |
| void OnActiveUserPrefServiceChanged(PrefService* pref_service) override; |
| |
| // input_method::InputMethodManager::Observer overrides: |
| void InputMethodChanged(input_method::InputMethodManager* manager, |
| Profile* profile, |
| bool show_message) override; |
| |
| // Registers global keyboard accelerators for the specified target. If |
| // multiple targets are registered for any given accelerator, a target |
| // registered later has higher priority. |
| void Register(const std::vector<ui::Accelerator>& accelerators, |
| ui::AcceleratorTarget* target); |
| |
| // Unregisters the specified keyboard accelerator for the specified target. |
| void Unregister(const ui::Accelerator& accelerator, |
| ui::AcceleratorTarget* target); |
| |
| // Unregisters all keyboard accelerators for the specified target. |
| void UnregisterAll(ui::AcceleratorTarget* target); |
| |
| // AcceleratorControllerImpl: |
| bool Process(const ui::Accelerator& accelerator) override; |
| bool IsDeprecated(const ui::Accelerator& accelerator) const override; |
| bool PerformActionIfEnabled(AcceleratorAction action, |
| const ui::Accelerator& accelerator) override; |
| bool OnMenuAccelerator(const ui::Accelerator& accelerator) override; |
| bool IsRegistered(const ui::Accelerator& accelerator) const override; |
| AcceleratorHistoryImpl* GetAcceleratorHistory() override; |
| bool DoesAcceleratorMatchAction(const ui::Accelerator& accelerator, |
| AcceleratorAction action) override; |
| |
| // Returns true if the |accelerator| is preferred. A preferred accelerator |
| // is handled before being passed to an window/web contents, unless |
| // the window is in fullscreen state. |
| bool IsPreferred(const ui::Accelerator& accelerator) const; |
| |
| // Returns true if the |accelerator| is reserved. A reserved accelerator |
| // is always handled and will never be passed to an window/web contents. |
| bool IsReserved(const ui::Accelerator& accelerator) const; |
| |
| // Provides access to the ExitWarningHandler for testing. |
| ExitWarningHandler* GetExitWarningHandlerForTest() { |
| return &exit_warning_handler_; |
| } |
| |
| // Overridden from ui::AcceleratorTarget: |
| bool AcceleratorPressed(const ui::Accelerator& accelerator) override; |
| bool CanHandleAccelerators() const override; |
| |
| // A confirmation dialog will be shown the first time an accessibility feature |
| // is enabled using the specified accelerator key sequence. Only one dialog |
| // will be shown at a time, and will not be shown again if the user has |
| // selected "accept" on a given dialog. The dialog was added to ensure that |
| // users would be aware of the shortcut they have just enabled, and to prevent |
| // users from accidentally triggering the feature. The dialog is currently |
| // shown when enabling the following features: high contrast, full screen |
| // magnifier, docked magnifier and screen rotation. The shown dialog is stored |
| // as a weak pointer in the variable |confirmation_dialog_| below. |
| void MaybeShowConfirmationDialog(int window_title_text_id, |
| int dialog_text_id, |
| base::OnceClosure on_accept_callback, |
| base::OnceClosure on_cancel_callback); |
| |
| // Remove the observers. |
| void Shutdown(); |
| |
| private: |
| // A map for looking up actions from accelerators. |
| using AcceleratorActionMap = ui::AcceleratorMap<AcceleratorAction>; |
| |
| // Initializes the accelerators this class handles as a target. |
| void Init(); |
| |
| // Registers the specified accelerators. |
| void RegisterAccelerators(const AcceleratorData accelerators[], |
| size_t accelerators_length); |
| |
| // Registers the deprecated accelerators and their replacing new ones. |
| void RegisterDeprecatedAccelerators(); |
| |
| // Returns true if there is an action for |accelerator| and it is enabled. |
| bool IsActionForAcceleratorEnabled(const ui::Accelerator& accelerator) const; |
| |
| // Returns whether |action| can be performed. The |accelerator| may provide |
| // additional data the action needs. |
| bool CanPerformAction(AcceleratorAction action, |
| const ui::Accelerator& accelerator) const; |
| |
| // Performs the specified action. The |accelerator| may provide additional |
| // data the action needs. |
| void PerformAction(AcceleratorAction action, |
| const ui::Accelerator& accelerator); |
| |
| // Returns whether performing |action| should consume the key event. |
| bool ShouldActionConsumeKeyEvent(AcceleratorAction action); |
| |
| // Get the accelerator restriction for the given action. Supply an |action| |
| // of -1 to get restrictions that apply for the current context. |
| AcceleratorProcessingRestriction GetAcceleratorProcessingRestriction( |
| int action) const; |
| |
| // If |accelerator| is a deprecated accelerator, it performs the appropriate |
| // deprecated accelerator pre-handling. |
| // Returns PROCEED if the accelerator's action should be performed (i.e. if |
| // |accelerator| is not a deprecated accelerator, or it's an enabled |
| // deprecated accelerator), and STOP otherwise (if the accelerator is a |
| // disabled deprecated accelerator). |
| enum class AcceleratorProcessingStatus { PROCEED, STOP }; |
| AcceleratorProcessingStatus MaybeDeprecatedAcceleratorPressed( |
| AcceleratorAction action, |
| const ui::Accelerator& accelerator) const; |
| |
| // Returns true if |source_device_id| corresponds to the internal keyboard or |
| // an internal uncategorized input device. |
| bool IsInternalKeyboardOrUncategorizedDevice(int source_device_id) const; |
| |
| // Returns true if |side_volume_button_location_| is in agreed format and |
| // values. |
| bool IsValidSideVolumeButtonLocation() const; |
| |
| // Returns true if the side volume buttons should be swapped. See |
| // SideVolumeButonLocation for the details. |
| bool ShouldSwapSideVolumeButtons(int source_device_id) const; |
| |
| // Read the side volume button location info from local file under |
| // kSideVolumeButtonLocationFilePath, parse and write it into |
| // |side_volume_button_location_|. |
| void ParseSideVolumeButtonLocationInfo(); |
| |
| // The metrics recorded include accidental volume adjustments (defined as a |
| // sequence of volume button events in close succession starting with a |
| // volume-up event but ending with an overall-decreased volume, or vice versa) |
| // or normal volume adjustments w/o SwapSideVolumeButtonsForOrientation |
| // feature enabled. |
| void UpdateTabletModeVolumeAdjustHistogram(); |
| |
| // Starts |tablet_mode_volume_adjust_timer_| while see VOLUME_UP or |
| // VOLUME_DOWN acceleration action when in tablet mode. |
| void StartTabletModeVolumeAdjustTimer(AcceleratorAction action); |
| |
| // Determines whether the notification about changed shortcuts at startup |
| // should show. This needs to be overridden in tests and set to false, |
| // because many tests rely on knowing the current active window, or test |
| // for the number of notifications visible. |
| // TODO(crbug.com/1179893): Remove in M94. |
| static bool should_show_shortcut_notification_; |
| |
| std::unique_ptr<ui::AcceleratorManager> accelerator_manager_; |
| |
| // A tracker for the current and previous accelerators. |
| std::unique_ptr<AcceleratorHistoryImpl> accelerator_history_; |
| |
| // Handles the exit accelerator which requires a double press to exit and |
| // shows a popup with an explanation. |
| ExitWarningHandler exit_warning_handler_; |
| |
| // A map from accelerators to the AcceleratorAction values, which are used in |
| // the implementation. |
| AcceleratorActionMap accelerators_; |
| |
| std::map<AcceleratorAction, const DeprecatedAcceleratorData*> |
| actions_with_deprecations_; |
| std::set<ui::Accelerator> deprecated_accelerators_; |
| |
| // Actions allowed when the user is not signed in. |
| std::set<int> actions_allowed_at_login_screen_; |
| // Actions allowed when the screen is locked. |
| std::set<int> actions_allowed_at_lock_screen_; |
| // Actions allowed when the power menu is opened. |
| std::set<int> actions_allowed_at_power_menu_; |
| // Actions allowed when a modal window is up. |
| std::set<int> actions_allowed_at_modal_window_; |
| // Preferred actions. See accelerator_table.h for details. |
| std::set<int> preferred_actions_; |
| // Reserved actions. See accelerator_table.h for details. |
| std::set<int> reserved_actions_; |
| // Actions which will be repeated while holding the accelerator key. |
| std::set<int> repeatable_actions_; |
| // Actions allowed in app mode. |
| std::set<int> actions_allowed_in_app_mode_; |
| // Actions allowed in pinned mode. |
| std::set<int> actions_allowed_in_pinned_mode_; |
| // Actions disallowed if there are no windows. |
| std::set<int> actions_needing_window_; |
| // Actions that can be performed without closing the menu (if one is present). |
| std::set<int> actions_keeping_menu_open_; |
| |
| // Holds a weak pointer to the accessibility confirmation dialog. |
| base::WeakPtr<AccessibilityConfirmationDialog> confirmation_dialog_; |
| |
| // Path of the file that contains the side volume button location info. It |
| // should always be kSideVolumeButtonLocationFilePath. But it is allowed to be |
| // set to different paths in test. |
| base::FilePath side_volume_button_location_file_path_; |
| |
| // Stores the location info of side volume buttons. |
| SideVolumeButtonLocation side_volume_button_location_; |
| |
| // Started when VOLUME_DOWN or VOLUME_UP accelerator action is seen while in |
| // tablet mode. Runs UpdateTabletModeVolumeAdjustHistogram() to record |
| // metrics. |
| base::OneShotTimer tablet_mode_volume_adjust_timer_; |
| |
| // True if volume adjust starts with VOLUME_UP action. |
| bool volume_adjust_starts_with_up_ = false; |
| |
| // The initial volume percentage when volume adjust starts. |
| int initial_volume_percent_ = 0; |
| }; |
| |
| } // namespace ash |
| |
| #endif // ASH_ACCELERATORS_ACCELERATOR_CONTROLLER_IMPL_H_ |