platformKeys: Add per-extension sign permissions.
[chromium-blink-merge.git] / ash / wm / maximize_mode / maximize_mode_controller.cc
blobe2ea1275439ca6a30cccb748965855f74c772832
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "ash/wm/maximize_mode/maximize_mode_controller.h"
7 #include "ash/accelerators/accelerator_controller.h"
8 #include "ash/accelerators/accelerator_table.h"
9 #include "ash/ash_switches.h"
10 #include "ash/display/display_manager.h"
11 #include "ash/shell.h"
12 #include "ash/wm/maximize_mode/maximize_mode_window_manager.h"
13 #include "ash/wm/maximize_mode/scoped_disable_internal_mouse_and_keyboard.h"
14 #include "base/command_line.h"
15 #include "base/metrics/histogram.h"
16 #include "base/time/default_tick_clock.h"
17 #include "base/time/tick_clock.h"
18 #include "ui/base/accelerators/accelerator.h"
19 #include "ui/events/event.h"
20 #include "ui/events/keycodes/keyboard_codes.h"
21 #include "ui/gfx/geometry/vector3d_f.h"
23 #if defined(USE_X11)
24 #include "ash/wm/maximize_mode/scoped_disable_internal_mouse_and_keyboard_x11.h"
25 #endif
27 #if defined(USE_OZONE)
28 #include "ash/wm/maximize_mode/scoped_disable_internal_mouse_and_keyboard_ozone.h"
29 #endif
31 #if defined(OS_CHROMEOS)
32 #include "chromeos/dbus/dbus_thread_manager.h"
33 #include "ui/chromeos/accelerometer/accelerometer_util.h"
34 #endif // OS_CHROMEOS
36 namespace ash {
38 namespace {
40 #if defined(OS_CHROMEOS)
41 // The hinge angle at which to enter maximize mode.
42 const float kEnterMaximizeModeAngle = 200.0f;
44 // The angle at which to exit maximize mode, this is specifically less than the
45 // angle to enter maximize mode to prevent rapid toggling when near the angle.
46 const float kExitMaximizeModeAngle = 160.0f;
48 // Defines a range for which accelerometer readings are considered accurate.
49 // When the lid is near open (or near closed) the accelerometer readings may be
50 // inaccurate and a lid that is fully open may appear to be near closed (and
51 // vice versa).
52 const float kMinStableAngle = 20.0f;
53 const float kMaxStableAngle = 340.0f;
54 #endif // OS_CHROMEOS
56 // The time duration to consider the lid to be recently opened.
57 // This is used to prevent entering maximize mode if an erroneous accelerometer
58 // reading makes the lid appear to be fully open when the user is opening the
59 // lid from a closed position.
60 const base::TimeDelta kLidRecentlyOpenedDuration =
61 base::TimeDelta::FromSeconds(2);
63 #if defined(OS_CHROMEOS)
64 // When the device approaches vertical orientation (i.e. portrait orientation)
65 // the accelerometers for the base and lid approach the same values (i.e.
66 // gravity pointing in the direction of the hinge). When this happens we cannot
67 // compute the hinge angle reliably and must turn ignore accelerometer readings.
68 // This is the minimum acceleration perpendicular to the hinge under which to
69 // detect hinge angle in m/s^2.
70 const float kHingeAngleDetectionThreshold = 2.5f;
72 // The maximum deviation between the magnitude of the two accelerometers under
73 // which to detect hinge angle in m/s^2. These accelerometers are attached to
74 // the same physical device and so should be under the same acceleration.
75 const float kNoisyMagnitudeDeviation = 1.0f;
77 // The angle between chromeos::AccelerometerReadings are considered stable if
78 // their magnitudes do not differ greatly. This returns false if the deviation
79 // between the screen and keyboard accelerometers is too high.
80 bool IsAngleBetweenAccelerometerReadingsStable(
81 const chromeos::AccelerometerUpdate& update) {
82 return std::abs(
83 ui::ConvertAccelerometerReadingToVector3dF(
84 update.get(chromeos::ACCELEROMETER_SOURCE_ATTACHED_KEYBOARD))
85 .Length() -
86 ui::ConvertAccelerometerReadingToVector3dF(
87 update.get(chromeos::ACCELEROMETER_SOURCE_SCREEN)).Length()) <=
88 kNoisyMagnitudeDeviation;
90 #endif // OS_CHROMEOS
92 } // namespace
94 MaximizeModeController::MaximizeModeController()
95 : have_seen_accelerometer_data_(false),
96 lid_open_past_180_(false),
97 touchview_usage_interval_start_time_(base::Time::Now()),
98 tick_clock_(new base::DefaultTickClock()),
99 lid_is_closed_(false) {
100 Shell::GetInstance()->AddShellObserver(this);
101 #if defined(OS_CHROMEOS)
102 chromeos::AccelerometerReader::GetInstance()->AddObserver(this);
103 chromeos::DBusThreadManager::Get()->
104 GetPowerManagerClient()->AddObserver(this);
105 #endif // OS_CHROMEOS
108 MaximizeModeController::~MaximizeModeController() {
109 Shell::GetInstance()->RemoveShellObserver(this);
110 #if defined(OS_CHROMEOS)
111 chromeos::AccelerometerReader::GetInstance()->RemoveObserver(this);
112 chromeos::DBusThreadManager::Get()->
113 GetPowerManagerClient()->RemoveObserver(this);
114 #endif // OS_CHROMEOS
117 bool MaximizeModeController::CanEnterMaximizeMode() {
118 // If we have ever seen accelerometer data, then HandleHingeRotation may
119 // trigger maximize mode at some point in the future.
120 // The --enable-touch-view-testing switch can also mean that we may enter
121 // maximize mode.
122 // TODO(mgiuca): This can result in false positives, as it returns true for
123 // any device with an accelerometer. Have TouchView-enabled devices explicitly
124 // set a flag, and change this implementation to simply return true iff the
125 // flag is present (http://crbug.com/457445).
126 return have_seen_accelerometer_data_ ||
127 base::CommandLine::ForCurrentProcess()->HasSwitch(
128 switches::kAshEnableTouchViewTesting);
131 void MaximizeModeController::EnableMaximizeModeWindowManager(bool enable) {
132 if (enable && !maximize_mode_window_manager_.get()) {
133 maximize_mode_window_manager_.reset(new MaximizeModeWindowManager());
134 // TODO(jonross): Move the maximize mode notifications from ShellObserver
135 // to MaximizeModeController::Observer
136 Shell::GetInstance()->OnMaximizeModeStarted();
137 } else if (!enable && maximize_mode_window_manager_.get()) {
138 maximize_mode_window_manager_.reset();
139 Shell::GetInstance()->OnMaximizeModeEnded();
143 bool MaximizeModeController::IsMaximizeModeWindowManagerEnabled() const {
144 return maximize_mode_window_manager_.get() != NULL;
147 void MaximizeModeController::AddWindow(aura::Window* window) {
148 if (IsMaximizeModeWindowManagerEnabled())
149 maximize_mode_window_manager_->AddWindow(window);
152 #if defined(OS_CHROMEOS)
153 void MaximizeModeController::OnAccelerometerUpdated(
154 const chromeos::AccelerometerUpdate& update) {
155 bool first_accelerometer_update = !have_seen_accelerometer_data_;
156 have_seen_accelerometer_data_ = true;
158 if (!update.has(chromeos::ACCELEROMETER_SOURCE_SCREEN))
159 return;
161 // Whether or not we enter maximize mode affects whether we handle screen
162 // rotation, so determine whether to enter maximize mode first.
163 if (!update.has(chromeos::ACCELEROMETER_SOURCE_ATTACHED_KEYBOARD)) {
164 if (first_accelerometer_update)
165 EnterMaximizeMode();
166 } else if (ui::IsAccelerometerReadingStable(
167 update, chromeos::ACCELEROMETER_SOURCE_SCREEN) &&
168 ui::IsAccelerometerReadingStable(
169 update, chromeos::ACCELEROMETER_SOURCE_ATTACHED_KEYBOARD) &&
170 IsAngleBetweenAccelerometerReadingsStable(update)) {
171 // update.has(chromeos::ACCELEROMETER_SOURCE_ATTACHED_KEYBOARD)
172 // Ignore the reading if it appears unstable. The reading is considered
173 // unstable if it deviates too much from gravity and/or the magnitude of the
174 // reading from the lid differs too much from the reading from the base.
175 HandleHingeRotation(update);
179 void MaximizeModeController::LidEventReceived(bool open,
180 const base::TimeTicks& time) {
181 if (open)
182 last_lid_open_time_ = time;
183 lid_is_closed_ = !open;
184 LeaveMaximizeMode();
187 void MaximizeModeController::SuspendImminent() {
188 // The system is about to suspend, so record TouchView usage interval metrics
189 // based on whether TouchView mode is currently active.
190 RecordTouchViewUsageInterval(CurrentTouchViewIntervalType());
193 void MaximizeModeController::SuspendDone(
194 const base::TimeDelta& sleep_duration) {
195 // We do not want TouchView usage metrics to include time spent in suspend.
196 touchview_usage_interval_start_time_ = base::Time::Now();
199 void MaximizeModeController::HandleHingeRotation(
200 const chromeos::AccelerometerUpdate& update) {
201 static const gfx::Vector3dF hinge_vector(1.0f, 0.0f, 0.0f);
202 // Ignore the component of acceleration parallel to the hinge for the purposes
203 // of hinge angle calculation.
204 gfx::Vector3dF base_flattened(ui::ConvertAccelerometerReadingToVector3dF(
205 update.get(chromeos::ACCELEROMETER_SOURCE_ATTACHED_KEYBOARD)));
206 gfx::Vector3dF lid_flattened(ui::ConvertAccelerometerReadingToVector3dF(
207 update.get(chromeos::ACCELEROMETER_SOURCE_SCREEN)));
208 base_flattened.set_x(0.0f);
209 lid_flattened.set_x(0.0f);
211 // As the hinge approaches a vertical angle, the base and lid accelerometers
212 // approach the same values making any angle calculations highly inaccurate.
213 // Bail out early when it is too close.
214 if (base_flattened.Length() < kHingeAngleDetectionThreshold ||
215 lid_flattened.Length() < kHingeAngleDetectionThreshold) {
216 return;
219 // Compute the angle between the base and the lid.
220 float lid_angle = 180.0f - gfx::ClockwiseAngleBetweenVectorsInDegrees(
221 base_flattened, lid_flattened, hinge_vector);
222 if (lid_angle < 0.0f)
223 lid_angle += 360.0f;
225 bool is_angle_stable = lid_angle >= kMinStableAngle &&
226 lid_angle <= kMaxStableAngle;
228 // Clear the last_lid_open_time_ for a stable reading so that there is less
229 // chance of a delay if the lid is moved from the close state to the fully
230 // open state very quickly.
231 if (is_angle_stable)
232 last_lid_open_time_ = base::TimeTicks();
234 // Toggle maximize mode on or off when corresponding thresholds are passed.
235 if (lid_open_past_180_ && is_angle_stable &&
236 lid_angle <= kExitMaximizeModeAngle) {
237 lid_open_past_180_ = false;
238 if (!base::CommandLine::ForCurrentProcess()->
239 HasSwitch(switches::kAshEnableTouchViewTesting)) {
240 LeaveMaximizeMode();
242 event_blocker_.reset();
243 } else if (!lid_open_past_180_ && !lid_is_closed_ &&
244 lid_angle >= kEnterMaximizeModeAngle &&
245 (is_angle_stable || !WasLidOpenedRecently())) {
246 lid_open_past_180_ = true;
247 if (!base::CommandLine::ForCurrentProcess()->
248 HasSwitch(switches::kAshEnableTouchViewTesting)) {
249 EnterMaximizeMode();
251 #if defined(USE_X11)
252 event_blocker_.reset(new ScopedDisableInternalMouseAndKeyboardX11);
253 #elif defined(USE_OZONE)
254 event_blocker_.reset(new ScopedDisableInternalMouseAndKeyboardOzone);
255 #endif
258 #endif // OS_CHROMEOS
260 void MaximizeModeController::EnterMaximizeMode() {
261 if (IsMaximizeModeWindowManagerEnabled())
262 return;
263 EnableMaximizeModeWindowManager(true);
266 void MaximizeModeController::LeaveMaximizeMode() {
267 if (!IsMaximizeModeWindowManagerEnabled())
268 return;
269 EnableMaximizeModeWindowManager(false);
272 // Called after maximize mode has started, windows might still animate though.
273 void MaximizeModeController::OnMaximizeModeStarted() {
274 RecordTouchViewUsageInterval(TOUCH_VIEW_INTERVAL_INACTIVE);
277 // Called after maximize mode has ended, windows might still be returning to
278 // their original position.
279 void MaximizeModeController::OnMaximizeModeEnded() {
280 RecordTouchViewUsageInterval(TOUCH_VIEW_INTERVAL_ACTIVE);
283 void MaximizeModeController::RecordTouchViewUsageInterval(
284 TouchViewIntervalType type) {
285 if (!CanEnterMaximizeMode())
286 return;
288 base::Time current_time = base::Time::Now();
289 base::TimeDelta delta = current_time - touchview_usage_interval_start_time_;
290 switch (type) {
291 case TOUCH_VIEW_INTERVAL_INACTIVE:
292 UMA_HISTOGRAM_LONG_TIMES("Ash.TouchView.TouchViewInactive", delta);
293 total_non_touchview_time_ += delta;
294 break;
295 case TOUCH_VIEW_INTERVAL_ACTIVE:
296 UMA_HISTOGRAM_LONG_TIMES("Ash.TouchView.TouchViewActive", delta);
297 total_touchview_time_ += delta;
298 break;
301 touchview_usage_interval_start_time_ = current_time;
304 MaximizeModeController::TouchViewIntervalType
305 MaximizeModeController::CurrentTouchViewIntervalType() {
306 if (IsMaximizeModeWindowManagerEnabled())
307 return TOUCH_VIEW_INTERVAL_ACTIVE;
308 return TOUCH_VIEW_INTERVAL_INACTIVE;
311 void MaximizeModeController::OnAppTerminating() {
312 // The system is about to shut down, so record TouchView usage interval
313 // metrics based on whether TouchView mode is currently active.
314 RecordTouchViewUsageInterval(CurrentTouchViewIntervalType());
316 if (CanEnterMaximizeMode()) {
317 UMA_HISTOGRAM_CUSTOM_COUNTS("Ash.TouchView.TouchViewActiveTotal",
318 total_touchview_time_.InMinutes(),
319 1, base::TimeDelta::FromDays(7).InMinutes(), 50);
320 UMA_HISTOGRAM_CUSTOM_COUNTS("Ash.TouchView.TouchViewInactiveTotal",
321 total_non_touchview_time_.InMinutes(),
322 1, base::TimeDelta::FromDays(7).InMinutes(), 50);
323 base::TimeDelta total_runtime = total_touchview_time_ +
324 total_non_touchview_time_;
325 if (total_runtime.InSeconds() > 0) {
326 UMA_HISTOGRAM_PERCENTAGE("Ash.TouchView.TouchViewActivePercentage",
327 100 * total_touchview_time_.InSeconds() / total_runtime.InSeconds());
332 bool MaximizeModeController::WasLidOpenedRecently() const {
333 if (last_lid_open_time_.is_null())
334 return false;
336 base::TimeTicks now = tick_clock_->NowTicks();
337 DCHECK(now >= last_lid_open_time_);
338 base::TimeDelta elapsed_time = now - last_lid_open_time_;
339 return elapsed_time <= kLidRecentlyOpenedDuration;
342 void MaximizeModeController::SetTickClockForTest(
343 scoped_ptr<base::TickClock> tick_clock) {
344 DCHECK(tick_clock_);
345 tick_clock_ = tick_clock.Pass();
348 } // namespace ash