chromium/ash/wm/tablet_mode/tablet_mode_controller_test_api.cc

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

#include "ash/wm/tablet_mode/tablet_mode_controller_test_api.h"

#include "ash/constants/ash_switches.h"
#include "ash/shell.h"
#include "base/command_line.h"
#include "base/numerics/math_constants.h"
#include "base/run_loop.h"
#include "base/time/default_tick_clock.h"
#include "ui/events/devices/device_data_manager_test_api.h"
#include "ui/events/devices/input_device.h"
#include "ui/events/devices/touchpad_device.h"

namespace ash {

namespace {

bool IsTabletModeControllerInitialized() {
  return base::CommandLine::ForCurrentProcess()->HasSwitch(
      switches::kAshEnableTabletMode);
}

}  // namespace

TabletModeControllerTestApi::TabletModeControllerTestApi()
    : tablet_mode_controller_(Shell::Get()->tablet_mode_controller()) {
  tablet_mode_controller_->OnDeviceListsComplete();
}

TabletModeControllerTestApi::~TabletModeControllerTestApi() = default;

void TabletModeControllerTestApi::EnterTabletMode() {
  tablet_mode_controller_->SetEnabledForTest(true);
}

void TabletModeControllerTestApi::LeaveTabletMode() {
  tablet_mode_controller_->SetEnabledForTest(false);
}

void TabletModeControllerTestApi::AttachExternalMouse() {
  // Calling RunUntilIdle() here is necessary before setting the mouse devices
  // to prevent the callback from evdev thread to overwrite whatever we set
  // here below. See `InputDeviceFactoryEvdevProxy::OnStartupScanComplete()`.
  base::RunLoop().RunUntilIdle();
  ui::DeviceDataManagerTestApi().SetMouseDevices(
      {ui::InputDevice(3, ui::InputDeviceType::INPUT_DEVICE_USB, "mouse")});
  if (!IsTabletModeControllerInitialized()) {
    // The controller is not observing the DeviceDataManager, hence we need to
    // notify it ourselves.
    tablet_mode_controller_->OnInputDeviceConfigurationChanged(
        ui::InputDeviceEventObserver::kMouse);
  }
}

void TabletModeControllerTestApi::AttachExternalTouchpad() {
  // Similar to |AttachExternalMouse|.
  base::RunLoop().RunUntilIdle();
  ui::DeviceDataManagerTestApi().SetTouchpadDevices({ui::TouchpadDevice(
      4, ui::InputDeviceType::INPUT_DEVICE_USB, "touchpad")});
  if (!IsTabletModeControllerInitialized()) {
    tablet_mode_controller_->OnInputDeviceConfigurationChanged(
        ui::InputDeviceEventObserver::kTouchpad);
  }
}

void TabletModeControllerTestApi::DetachAllMice() {
  // See comment in |AttachExternalMouse| for why we need to call
  // |base::RunLoop::RunUntilIdle|.
  base::RunLoop().RunUntilIdle();
  ui::DeviceDataManagerTestApi().SetMouseDevices({});
  if (!IsTabletModeControllerInitialized()) {
    // The controller is not observing the DeviceDataManager, hence we need to
    // notify it ourselves.
    tablet_mode_controller_->OnInputDeviceConfigurationChanged(
        ui::InputDeviceEventObserver::kMouse);
  }
}

void TabletModeControllerTestApi::DetachAllTouchpads() {
  // Similar to |DetachAllMice|.
  base::RunLoop().RunUntilIdle();
  ui::DeviceDataManagerTestApi().SetTouchpadDevices({});
  if (!IsTabletModeControllerInitialized()) {
    tablet_mode_controller_->OnInputDeviceConfigurationChanged(
        ui::InputDeviceEventObserver::kTouchpad);
  }
}

void TabletModeControllerTestApi::TriggerLidUpdate(const gfx::Vector3dF& lid) {
  AccelerometerUpdate update;
  update.Set(ACCELEROMETER_SOURCE_SCREEN, lid.x(), lid.y(), lid.z());
  tablet_mode_controller_->OnAccelerometerUpdated(update);
}

void TabletModeControllerTestApi::TriggerBaseAndLidUpdate(
    const gfx::Vector3dF& base,
    const gfx::Vector3dF& lid) {
  AccelerometerUpdate update;
  update.Set(ACCELEROMETER_SOURCE_ATTACHED_KEYBOARD, base.x(), base.y(),
             base.z());
  update.Set(ACCELEROMETER_SOURCE_SCREEN, lid.x(), lid.y(), lid.z());
  tablet_mode_controller_->OnAccelerometerUpdated(update);
}

void TabletModeControllerTestApi::OpenLidToAngle(float degrees) {
  DCHECK(degrees >= 0.0f);
  DCHECK(degrees <= 360.0f);

  float radians = degrees * kDegreesToRadians;
  gfx::Vector3dF base_vector(0.0f, -base::kMeanGravityFloat, 0.0f);
  gfx::Vector3dF lid_vector(0.0f, base::kMeanGravityFloat * cos(radians),
                            base::kMeanGravityFloat * sin(radians));
  TriggerBaseAndLidUpdate(base_vector, lid_vector);
}

void TabletModeControllerTestApi::HoldDeviceVertical() {
  gfx::Vector3dF base_vector(9.8f, 0.0f, 0.0f);
  gfx::Vector3dF lid_vector(9.8f, 0.0f, 0.0f);
  TriggerBaseAndLidUpdate(base_vector, lid_vector);
}

void TabletModeControllerTestApi::OpenLid() {
  tablet_mode_controller_->LidEventReceived(
      chromeos::PowerManagerClient::LidState::OPEN, tick_clock()->NowTicks());
}

void TabletModeControllerTestApi::CloseLid() {
  tablet_mode_controller_->LidEventReceived(
      chromeos::PowerManagerClient::LidState::CLOSED, tick_clock()->NowTicks());
}

void TabletModeControllerTestApi::SetTabletMode(bool on) {
  tablet_mode_controller_->TabletModeEventReceived(
      on ? chromeos::PowerManagerClient::TabletMode::ON
         : chromeos::PowerManagerClient::TabletMode::OFF,
      tick_clock()->NowTicks());
}

void TabletModeControllerTestApi::SuspendImminent() {
  tablet_mode_controller_->SuspendImminent(
      power_manager::SuspendImminent::Reason::SuspendImminent_Reason_IDLE);
}

void TabletModeControllerTestApi::SuspendDone(base::TimeDelta sleep_duration) {
  tablet_mode_controller_->SuspendDone(sleep_duration);
}

}  // namespace ash