chromium/ash/accessibility/chromevox/touch_accessibility_enabler.h

// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef ASH_ACCESSIBILITY_CHROMEVOX_TOUCH_ACCESSIBILITY_ENABLER_H_
#define ASH_ACCESSIBILITY_CHROMEVOX_TOUCH_ACCESSIBILITY_ENABLER_H_

#include <map>

#include "ash/ash_export.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/time/tick_clock.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "ui/events/event.h"
#include "ui/events/event_handler.h"
#include "ui/events/gesture_detection/gesture_detector.h"

namespace aura {
class Window;
}

namespace ash {

// A delegate to handle commands in response to detected accessibility gesture
// events.
class TouchAccessibilityEnablerDelegate {
 public:
  virtual ~TouchAccessibilityEnablerDelegate() {}

  // Toggles spoken feedback.
  virtual void ToggleSpokenFeedback() {}
};

// TouchAccessibilityEnabler triggers turning spoken feedback on or off
// by holding down two fingers on the touch screen for several seconds.
class ASH_EXPORT TouchAccessibilityEnabler : public ui::EventHandler {
 public:
  TouchAccessibilityEnabler(aura::Window* root_window,
                            TouchAccessibilityEnablerDelegate* delegate);

  TouchAccessibilityEnabler(const TouchAccessibilityEnabler&) = delete;
  TouchAccessibilityEnabler& operator=(const TouchAccessibilityEnabler&) =
      delete;

  ~TouchAccessibilityEnabler() override;

  bool IsInNoFingersDownForTesting() { return state_ == NO_FINGERS_DOWN; }
  bool IsInOneFingerDownForTesting() { return state_ == ONE_FINGER_DOWN; }
  bool IsInTwoFingersDownForTesting() { return state_ == TWO_FINGERS_DOWN; }
  bool IsInWaitForNoFingersForTesting() {
    return state_ == WAIT_FOR_NO_FINGERS;
  }
  void TriggerOnTimerForTesting() { OnTimer(); }

  // When TouchExplorationController is running, it tells this class to
  // remove its event handler so that it can pass it the unrewritten events
  // directly. Otherwise, this class would only receive the rewritten events,
  // which would require entirely separate logic.
  void RemoveEventHandler();
  void AddEventHandler();
  void HandleTouchEvent(const ui::TouchEvent& event);

  // Expose a weak ptr so that TouchExplorationController can hold a reference
  // to this object without worrying about destruction order during shutdown.
  base::WeakPtr<TouchAccessibilityEnabler> GetWeakPtr();

 private:
  // Overridden from ui::EventHandler
  void OnTouchEvent(ui::TouchEvent* event) override;

  void StartTimer();
  void CancelTimer();
  void OnTimer();

  void ResetToNoFingersDown();

  // Returns the current time of the tick clock.
  base::TimeTicks Now();

  enum State {
    // No fingers are down.
    NO_FINGERS_DOWN,

    // One finger is down and it's possible this could be a two-finger-hold.
    ONE_FINGER_DOWN,

    // Two fingers are down and stationary and we will trigger enabling
    // spoken feedback after a delay.
    TWO_FINGERS_DOWN,

    // This is the "reject" state when we get anything other than two fingers
    // held down and stationary. Stay in this state until all fingers are
    // removed.
    WAIT_FOR_NO_FINGERS
  };

  raw_ptr<aura::Window> root_window_;

  // Called when we detect a long-press of two fingers. Not owned.
  raw_ptr<TouchAccessibilityEnablerDelegate> delegate_;

  // The current state.
  State state_;

  // Map of touch ids to their initial locations.
  std::map<int, gfx::PointF> touch_locations_;

  // A timer that triggers after two fingers are held down for a given duration.
  base::RetainingOneShotTimer timer_;

  // A default gesture detector config, so we can share the same
  // timeout and pixel slop constants.
  ui::GestureDetector::Config gesture_detector_config_;

  // When touch_accessibility_enabler gets time relative to real time during
  // testing, this clock is set to the simulated clock and used.
  raw_ptr<const base::TickClock> tick_clock_;

  // Whether or not we currently have an event handler installed. It can
  // be removed when TouchExplorationController is running.
  bool event_handler_installed_ = false;

  base::WeakPtrFactory<TouchAccessibilityEnabler> weak_factory_{this};
};

}  // namespace ash

#endif  // ASH_ACCESSIBILITY_CHROMEVOX_TOUCH_ACCESSIBILITY_ENABLER_H_