blob: 23ea8cf49ee2659103e3d41df692e3c45105cdf6 [file] [log] [blame]
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_ASH_ARC_INPUT_OVERLAY_TOUCH_INJECTOR_H_
#define CHROME_BROWSER_ASH_ARC_INPUT_OVERLAY_TOUCH_INJECTOR_H_
#include <memory>
#include <vector>
#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "base/scoped_observation.h"
#include "chrome/browser/ash/arc/input_overlay/actions/action.h"
#include "chrome/browser/ash/arc/input_overlay/constants.h"
#include "chrome/browser/ash/arc/input_overlay/display_overlay_controller.h"
#include "ui/events/event_rewriter.h"
#include "ui/gfx/geometry/rect_f.h"
#include "ui/gfx/geometry/vector2d_f.h"
namespace aura {
class Window;
} // namespace aura
namespace ui {
class EventSource;
} // namespace ui
namespace arc::input_overlay {
class Action;
class ArcInputOverlayManagerTest;
// If the following touch move sent immediately, the touch move event is not
// processed correctly by apps. This is a delayed time to send touch move
// event.
constexpr base::TimeDelta kSendTouchMoveDelay = base::Milliseconds(50);
gfx::RectF CalculateWindowContentBounds(aura::Window* window);
// Maximum default action ID. User-added actions have ID > kMaxDefaultActionID.
constexpr int kMaxDefaultActionID = 9999;
// TouchInjector includes all the touch actions related to the specific window
// and performs as a bridge between the ArcInputOverlayManager and the touch
// actions. It implements EventRewriter to transform input events to touch
// events.
class TouchInjector : public ui::EventRewriter {
public:
using OnSaveProtoFileCallback =
base::RepeatingCallback<void(std::unique_ptr<AppDataProto>, std::string)>;
TouchInjector(aura::Window* top_level_window,
const std::string& package_name,
OnSaveProtoFileCallback save_file_callback);
TouchInjector(const TouchInjector&) = delete;
TouchInjector& operator=(const TouchInjector&) = delete;
~TouchInjector() override;
// Parse Json to actions.
// Json value format:
// {
// "tap": [
// {},
// ...
// ],
// "move": [
// {},
// ...
// ]
// }
void ParseActions(const base::Value::Dict& root);
// Notify the EventRewriter whether the text input is focused or not.
void NotifyTextInputState(bool active);
// Register the EventRewriter.
void RegisterEventRewriter();
// Unregister the EventRewriter.
void UnRegisterEventRewriter();
// Change bindings. This could be from user editing from display overlay
// (|mode| = DisplayMode::kEdit) or from customized protobuf data (|mode| =
// DisplayMode::kView).
void OnInputBindingChange(Action* target_action,
std::unique_ptr<InputElement> input_element);
// Apply pending binding as current binding, but don't save into the storage.
void OnApplyPendingBinding();
// Save customized input binding/pending binding as current binding and go
// back from edit mode to view mode.
void OnBindingSave();
// Set input binding back to previous status before entering to the edit mode
// and go back from edit mode to view mode.
void OnBindingCancel();
// Set input binding back to original binding.
void OnBindingRestore();
void OnProtoDataAvailable(AppDataProto& proto);
// Save the input menu state when the menu is closed.
void OnInputMenuViewRemoved();
void NotifyFirstTimeLaunch();
// Save the menu entry view position when it's changed.
void SaveMenuEntryLocation(gfx::Point menu_entry_location_point);
absl::optional<gfx::Vector2dF> menu_entry_location() {
return menu_entry_location_;
}
// Update |content_bounds_| and touch positions for each |actions_| for
// different reasons.
void UpdatePositionsForRegister();
void UpdateForOverlayBoundsChanged(const gfx::RectF& new_bounds);
// Add or delete an Action.
// Return an action ID (> kMaxDefaultActionID) for adding a new action.
int GetNextActionID();
// Add a new action of type |action_type| from UI without input binding and
// with default position binding at the center.
void AddNewAction(ActionType action_type);
// Add action view for |action|.
void AddActionView(Action* action);
void RemoveAction(Action* action);
// Remove action view for |action|.
void RemoveActionView(Action* action);
// UMA stats.
void RecordMenuStateOnLaunch();
// ui::EventRewriter:
ui::EventDispatchDetails RewriteEvent(
const ui::Event& event,
const Continuation continuation) override;
aura::Window* window() { return window_; }
const std::string& package_name() const { return package_name_; }
const gfx::RectF& content_bounds() const { return content_bounds_; }
const gfx::Transform* rotation_transform() {
return rotation_transform_.get();
}
const std::vector<std::unique_ptr<Action>>& actions() const {
return actions_;
}
bool is_mouse_locked() const { return is_mouse_locked_; }
bool touch_injector_enable() const { return touch_injector_enable_; }
void store_touch_injector_enable(bool enable) {
touch_injector_enable_ = enable;
}
bool input_mapping_visible() const { return input_mapping_visible_; }
void store_input_mapping_visible(bool enable) {
input_mapping_visible_ = enable;
}
bool first_launch() const { return first_launch_; }
void set_first_launch(bool first_launch) { first_launch_ = first_launch; }
bool show_nudge() const { return show_nudge_; }
void set_show_nudge(bool show_nudge) { show_nudge_ = show_nudge; }
void set_display_mode(DisplayMode mode) { display_mode_ = mode; }
void set_display_overlay_controller(DisplayOverlayController* controller) {
display_overlay_controller_ = controller;
}
bool enable_mouse_lock() { return enable_mouse_lock_; }
void set_enable_mouse_lock(bool enable) { enable_mouse_lock_ = true; }
bool allow_reposition() const { return allow_reposition_; }
void set_allow_reposition(bool allow_reposition) {
allow_reposition_ = allow_reposition;
}
bool beta() const { return beta_; }
void set_beta(bool beta) { beta_ = beta; }
private:
friend class ArcInputOverlayManagerTest;
friend class TouchInjectorTest;
struct TouchPointInfo {
// ID managed by input overlay.
int rewritten_touch_id;
// The latest root location of this given touch event.
gfx::PointF touch_root_location;
};
class KeyCommand;
// Clean up active touch events before entering into other mode from |kView|
// mode.
void CleanupTouchEvents();
// If the window is destroying or focusing out, releasing the active touch
// event.
void DispatchTouchCancelEvent();
void SendExtraEvent(const ui::EventRewriter::Continuation continuation,
const ui::Event& event);
void DispatchTouchReleaseEventOnMouseUnLock();
void DispatchTouchReleaseEvent();
// Json format:
// "mouse_lock": {
// "key": "KeyA",
// "modifier": [""]
// }
void ParseMouseLock(const base::Value::Dict& dict);
void FlipMouseLockFlag();
// Check if the event located on menu entry. |press_required| tells whether or
// not a mouse press or touch press is required.
bool LocatedEventOnMenuEntry(const ui::Event& event,
const gfx::RectF& content_bounds,
bool press_required);
// Takes valid touch events and overrides their ids with an id managed by the
// TouchIdManager.
std::unique_ptr<ui::TouchEvent> RewriteOriginalTouch(
const ui::TouchEvent* touch_event);
// This method will generate a new touch event with a managed touch id.
std::unique_ptr<ui::TouchEvent> CreateTouchEvent(
const ui::TouchEvent* touch_event,
ui::PointerId original_id,
int managed_touch_id,
gfx::PointF root_location_f);
// Search action by its id.
Action* GetActionById(int id);
// Convert the customized data to AppDataProto.
std::unique_ptr<AppDataProto> ConvertToProto();
// Save proto file.
void OnSaveProtoFile();
// Add the menu state to |proto|.
void AddMenuStateToProto(AppDataProto& proto);
// Load menu state from |proto|. The default state is on for the toggles.
void LoadMenuStateFromProto(AppDataProto& proto);
// Add the menu entry view position to |proto|, if it has been customized.
void AddMenuEntryToProtoIfCustomized(AppDataProto& proto) const;
// Load menu entry position from |proto|, if it exists.
void LoadMenuEntryFromProto(AppDataProto& proto);
void AddSystemVersionToProto(AppDataProto& proto);
void LoadSystemVersionFromProto(AppDataProto& proto);
// Create Action by |action_type| without any input bindings.
std::unique_ptr<Action> CreateRawAction(ActionType action_type);
// Remove all user-added actions from |actions| and return the deleted
// actions.
std::vector<std::unique_ptr<Action>> RemoveUserActionsAndViews(
std::vector<std::unique_ptr<Action>>& actions);
// Add removed default actions and show their views in |actions|, and save
// these actions in |added_actions|.
void AddDefaultActionsAndViews(std::vector<std::unique_ptr<Action>>& actions,
std::vector<Action*>& added_actions);
// Add the |deleted_default_actions| back and show their views.
void AddDefaultActionsAndViews(std::vector<Action*>& deleted_default_actions);
// Remove the |added_default_actions| and remove their views.
void RemoveDefaultActionsAndViews(
std::vector<Action*>& added_default_actions);
// For test.
int GetRewrittenTouchIdForTesting(ui::PointerId original_id);
gfx::PointF GetRewrittenRootLocationForTesting(ui::PointerId original_id);
int GetRewrittenTouchInfoSizeForTesting();
DisplayOverlayController* GetControllerForTesting();
// TouchInjector is created when targeted |window_| is created and is
// registered only when |window_| is focused. And TouchInjector doesn't own
// |window_| and it is destroyed when |window_| is destroyed.
raw_ptr<aura::Window, DanglingUntriaged> window_;
std::string package_name_;
gfx::RectF content_bounds_;
base::WeakPtr<ui::EventRewriterContinuation> continuation_;
std::vector<std::unique_ptr<Action>> actions_;
base::ScopedObservation<ui::EventSource, ui::EventRewriter> observation_{
this};
std::unique_ptr<KeyCommand> mouse_lock_;
std::unique_ptr<gfx::Transform> rotation_transform_;
bool text_input_active_ = false;
// The mouse is unlocked by default.
bool is_mouse_locked_ = false;
DisplayMode display_mode_ = DisplayMode::kView;
raw_ptr<DisplayOverlayController> display_overlay_controller_ = nullptr;
// Linked to game controller toggle in the menu. Set it enabled by default.
// This is to save status if display overlay is destroyed during window
// operations.
bool touch_injector_enable_ = true;
// Linked to input mapping toggle in the menu. Set it enabled by default. This
// is to save status if display overlay is destroyed during window operations.
bool input_mapping_visible_ = true;
// Used for UMA stats. Don't record the stats when users just switch the
// toggle back and forth and finish at the same state. Only record the state
// change once the menu is closed.
bool touch_injector_enable_uma_ = true;
bool input_mapping_visible_uma_ = true;
// The game app is launched for the first time when input overlay is enabled
// if the value is true.
bool first_launch_ = false;
// Check whether to show the nudge view. We only show the nudge view for the
// first time launch and before it is dismissed.
bool show_nudge_ = false;
// Key is the original touch id. Value is a struct containing required info
// for this touch event.
base::flat_map<ui::PointerId, TouchPointInfo> rewritten_touch_infos_;
// This for Action adding or deleting. For default action, ID <=
// kMaxDefaultActionID. For custom actions, ID > kMaxDefaultActionID.
int next_action_id_ = kMaxDefaultActionID + 1;
// Pending status for adding and and deleting actions.
std::vector<std::unique_ptr<Action>> pending_add_user_actions_;
std::vector<std::unique_ptr<Action>> pending_delete_user_actions_;
// Default actions wont be removed from |actions_|.
std::vector<Action*> pending_add_default_actions_;
std::vector<Action*> pending_delete_default_actions_;
// Callback when saving proto file.
OnSaveProtoFileCallback save_file_callback_;
// TODO(cuicuiruan): It can be removed after the mouse lock is enabled for
// post MVP.
bool enable_mouse_lock_ = false;
// TODO(b/260937747): Update or remove when removing flags
// |kArcInputOverlayAlphaV2| or |kArcInputOverlayBeta|.
bool allow_reposition_ = false;
// Corresponds to |kArcInputOverlayBeta| flag to turn on/off the editor
// feature of adding or removing actions.
bool beta_ = ash::features::IsArcInputOverlayBetaEnabled();
// Use default position if it is null.
absl::optional<gfx::Vector2dF> menu_entry_location_;
base::WeakPtrFactory<TouchInjector> weak_ptr_factory_{this};
};
} // namespace arc::input_overlay
#endif // CHROME_BROWSER_ASH_ARC_INPUT_OVERLAY_TOUCH_INJECTOR_H_