Implement listing tests to a JSON file for iOS gtest test launcher
[chromium-blink-merge.git] / ash / touch / touch_uma.cc
blobfdac10cbd8dc643defed184eeb82e04e9662b64a
1 // Copyright (c) 2012 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/touch/touch_uma.h"
7 #include "ash/metrics/user_metrics_recorder.h"
8 #include "ash/shell.h"
9 #include "base/metrics/histogram.h"
10 #include "base/strings/stringprintf.h"
11 #include "ui/aura/env.h"
12 #include "ui/aura/window.h"
13 #include "ui/aura/window_event_dispatcher.h"
14 #include "ui/aura/window_property.h"
15 #include "ui/events/event.h"
16 #include "ui/events/event_utils.h"
17 #include "ui/gfx/point_conversions.h"
19 #if defined(USE_X11)
20 #include <X11/extensions/XInput2.h>
21 #include <X11/Xlib.h>
22 #endif
24 namespace {
26 struct WindowTouchDetails {
27 // Move and start times of the touch points. The key is the touch-id.
28 std::map<int, base::TimeDelta> last_move_time_;
29 std::map<int, base::TimeDelta> last_start_time_;
31 // The first and last positions of the touch points.
32 std::map<int, gfx::Point> start_touch_position_;
33 std::map<int, gfx::Point> last_touch_position_;
35 // Last time-stamp of the last touch-end event.
36 base::TimeDelta last_release_time_;
38 // Stores the time of the last touch released on this window (if there was a
39 // multi-touch gesture on the window, then this is the release-time of the
40 // last touch on the window).
41 base::TimeDelta last_mt_time_;
44 DEFINE_OWNED_WINDOW_PROPERTY_KEY(WindowTouchDetails,
45 kWindowTouchDetails,
46 NULL);
49 namespace ash {
51 // static
52 TouchUMA* TouchUMA::GetInstance() {
53 return Singleton<TouchUMA>::get();
56 void TouchUMA::RecordGestureEvent(aura::Window* target,
57 const ui::GestureEvent& event) {
58 GestureActionType action = FindGestureActionType(target, event);
59 RecordGestureAction(action);
61 if (event.type() == ui::ET_GESTURE_END &&
62 event.details().touch_points() == 2) {
63 WindowTouchDetails* details = target->GetProperty(kWindowTouchDetails);
64 if (!details) {
65 LOG(ERROR) << "Window received gesture events without receiving any touch"
66 " events";
67 return;
69 details->last_mt_time_ = event.time_stamp();
73 void TouchUMA::RecordGestureAction(GestureActionType action) {
74 if (action == GESTURE_UNKNOWN || action >= GESTURE_ACTION_COUNT)
75 return;
76 UMA_HISTOGRAM_ENUMERATION("Ash.GestureTarget", action,
77 GESTURE_ACTION_COUNT);
80 void TouchUMA::RecordTouchEvent(aura::Window* target,
81 const ui::TouchEvent& event) {
82 UMA_HISTOGRAM_CUSTOM_COUNTS("Ash.TouchRadius",
83 static_cast<int>(std::max(event.radius_x(), event.radius_y())),
84 1, 500, 100);
86 UpdateTouchState(event);
88 WindowTouchDetails* details = target->GetProperty(kWindowTouchDetails);
89 if (!details) {
90 details = new WindowTouchDetails;
91 target->SetProperty(kWindowTouchDetails, details);
94 // Record the location of the touch points.
95 const int kBucketCountForLocation = 100;
96 const gfx::Rect bounds = target->GetRootWindow()->bounds();
97 const int bucket_size_x = std::max(1,
98 bounds.width() / kBucketCountForLocation);
99 const int bucket_size_y = std::max(1,
100 bounds.height() / kBucketCountForLocation);
102 gfx::Point position = event.root_location();
104 // Prefer raw event location (when available) over calibrated location.
105 if (event.HasNativeEvent()) {
106 #if defined(USE_X11)
107 XEvent* xevent = event.native_event();
108 CHECK_EQ(GenericEvent, xevent->type);
109 XIEvent* xievent = static_cast<XIEvent*>(xevent->xcookie.data);
110 if (xievent->evtype == XI_TouchBegin ||
111 xievent->evtype == XI_TouchUpdate ||
112 xievent->evtype == XI_TouchEnd) {
113 XIDeviceEvent* device_event =
114 static_cast<XIDeviceEvent*>(xevent->xcookie.data);
115 position.SetPoint(static_cast<int>(device_event->event_x),
116 static_cast<int>(device_event->event_y));
117 } else {
118 position = ui::EventLocationFromNative(event.native_event());
120 #else
121 position = ui::EventLocationFromNative(event.native_event());
122 #endif
123 position = gfx::ToFlooredPoint(
124 gfx::ScalePoint(position, 1. / target->layer()->device_scale_factor()));
127 position.set_x(std::min(bounds.width() - 1, std::max(0, position.x())));
128 position.set_y(std::min(bounds.height() - 1, std::max(0, position.y())));
130 UMA_HISTOGRAM_CUSTOM_COUNTS("Ash.TouchPositionX",
131 position.x() / bucket_size_x,
132 0, kBucketCountForLocation, kBucketCountForLocation + 1);
133 UMA_HISTOGRAM_CUSTOM_COUNTS("Ash.TouchPositionY",
134 position.y() / bucket_size_y,
135 0, kBucketCountForLocation, kBucketCountForLocation + 1);
137 if (event.type() == ui::ET_TOUCH_PRESSED) {
138 Shell::GetInstance()->metrics()->RecordUserMetricsAction(
139 UMA_TOUCHSCREEN_TAP_DOWN);
141 details->last_start_time_[event.touch_id()] = event.time_stamp();
142 details->start_touch_position_[event.touch_id()] = event.root_location();
143 details->last_touch_position_[event.touch_id()] = event.location();
145 if (details->last_release_time_.ToInternalValue()) {
146 // Measuring the interval between a touch-release and the next
147 // touch-start is probably less useful when doing multi-touch (e.g.
148 // gestures, or multi-touch friendly apps). So count this only if the user
149 // hasn't done any multi-touch during the last 30 seconds.
150 base::TimeDelta diff = event.time_stamp() - details->last_mt_time_;
151 if (diff.InSeconds() > 30) {
152 base::TimeDelta gap = event.time_stamp() - details->last_release_time_;
153 UMA_HISTOGRAM_COUNTS_10000("Ash.TouchStartAfterEnd",
154 gap.InMilliseconds());
158 // Record the number of touch-points currently active for the window.
159 const int kMaxTouchPoints = 10;
160 UMA_HISTOGRAM_CUSTOM_COUNTS("Ash.ActiveTouchPoints",
161 details->last_start_time_.size(),
162 1, kMaxTouchPoints, kMaxTouchPoints + 1);
163 } else if (event.type() == ui::ET_TOUCH_RELEASED) {
164 if (details->last_start_time_.count(event.touch_id())) {
165 base::TimeDelta duration = event.time_stamp() -
166 details->last_start_time_[event.touch_id()];
167 // Look for touches that were [almost] stationary for a long time.
168 const double kLongStationaryTouchDuration = 10;
169 const int kLongStationaryTouchDistanceSquared = 100;
170 if (duration.InSecondsF() > kLongStationaryTouchDuration) {
171 gfx::Vector2d distance = event.root_location() -
172 details->start_touch_position_[event.touch_id()];
173 if (distance.LengthSquared() < kLongStationaryTouchDistanceSquared) {
174 UMA_HISTOGRAM_CUSTOM_COUNTS("Ash.StationaryTouchDuration",
175 duration.InSeconds(),
176 kLongStationaryTouchDuration,
177 1000,
178 20);
182 details->last_start_time_.erase(event.touch_id());
183 details->last_move_time_.erase(event.touch_id());
184 details->start_touch_position_.erase(event.touch_id());
185 details->last_touch_position_.erase(event.touch_id());
186 details->last_release_time_ = event.time_stamp();
187 } else if (event.type() == ui::ET_TOUCH_MOVED) {
188 int distance = 0;
189 if (details->last_touch_position_.count(event.touch_id())) {
190 gfx::Point lastpos = details->last_touch_position_[event.touch_id()];
191 distance =
192 std::abs(lastpos.x() - event.x()) + std::abs(lastpos.y() - event.y());
195 if (details->last_move_time_.count(event.touch_id())) {
196 base::TimeDelta move_delay = event.time_stamp() -
197 details->last_move_time_[event.touch_id()];
198 UMA_HISTOGRAM_CUSTOM_COUNTS("Ash.TouchMoveInterval",
199 move_delay.InMilliseconds(),
200 1, 50, 25);
203 UMA_HISTOGRAM_CUSTOM_COUNTS("Ash.TouchMoveSteps", distance, 1, 1000, 50);
205 details->last_move_time_[event.touch_id()] = event.time_stamp();
206 details->last_touch_position_[event.touch_id()] = event.location();
210 TouchUMA::TouchUMA()
211 : is_single_finger_gesture_(false),
212 touch_in_progress_(false),
213 burst_length_(0) {
216 TouchUMA::~TouchUMA() {
219 void TouchUMA::UpdateTouchState(const ui::TouchEvent& event) {
220 if (event.type() == ui::ET_TOUCH_PRESSED) {
221 if (!touch_in_progress_) {
222 is_single_finger_gesture_ = true;
223 base::TimeDelta difference = event.time_stamp() - last_touch_down_time_;
224 if (difference > base::TimeDelta::FromMilliseconds(250)) {
225 if (burst_length_) {
226 UMA_HISTOGRAM_COUNTS_100("Ash.TouchStartBurst",
227 std::min(burst_length_, 100));
229 burst_length_ = 1;
230 } else {
231 ++burst_length_;
233 } else {
234 is_single_finger_gesture_ = false;
236 touch_in_progress_ = true;
237 last_touch_down_time_ = event.time_stamp();
238 } else if (event.type() == ui::ET_TOUCH_RELEASED) {
239 if (!aura::Env::GetInstance()->is_touch_down())
240 touch_in_progress_ = false;
244 TouchUMA::GestureActionType TouchUMA::FindGestureActionType(
245 aura::Window* window,
246 const ui::GestureEvent& event) {
247 if (!window || window->GetRootWindow() == window) {
248 if (event.type() == ui::ET_GESTURE_SCROLL_BEGIN)
249 return GESTURE_BEZEL_SCROLL;
250 if (event.type() == ui::ET_GESTURE_BEGIN)
251 return GESTURE_BEZEL_DOWN;
252 return GESTURE_UNKNOWN;
255 std::string name = window ? window->name() : std::string();
257 const char kDesktopBackgroundView[] = "DesktopBackgroundView";
258 if (name == kDesktopBackgroundView) {
259 if (event.type() == ui::ET_GESTURE_SCROLL_BEGIN)
260 return GESTURE_DESKTOP_SCROLL;
261 if (event.type() == ui::ET_GESTURE_PINCH_BEGIN)
262 return GESTURE_DESKTOP_PINCH;
263 return GESTURE_UNKNOWN;
266 const char kWebPage[] = "RenderWidgetHostViewAura";
267 if (name == kWebPage) {
268 if (event.type() == ui::ET_GESTURE_PINCH_BEGIN)
269 return GESTURE_WEBPAGE_PINCH;
270 if (event.type() == ui::ET_GESTURE_SCROLL_BEGIN)
271 return GESTURE_WEBPAGE_SCROLL;
272 if (event.type() == ui::ET_GESTURE_TAP)
273 return GESTURE_WEBPAGE_TAP;
274 return GESTURE_UNKNOWN;
277 views::Widget* widget = views::Widget::GetWidgetForNativeView(window);
278 if (!widget)
279 return GESTURE_UNKNOWN;
281 views::View* view = widget->GetRootView()->
282 GetEventHandlerForPoint(event.location());
283 if (!view)
284 return GESTURE_UNKNOWN;
286 name = view->GetClassName();
288 const char kTabStrip[] = "TabStrip";
289 const char kTab[] = "BrowserTab";
290 if (name == kTabStrip || name == kTab) {
291 if (event.type() == ui::ET_GESTURE_SCROLL_BEGIN)
292 return GESTURE_TABSTRIP_SCROLL;
293 if (event.type() == ui::ET_GESTURE_PINCH_BEGIN)
294 return GESTURE_TABSTRIP_PINCH;
295 if (event.type() == ui::ET_GESTURE_TAP)
296 return GESTURE_TABSTRIP_TAP;
297 return GESTURE_UNKNOWN;
300 const char kOmnibox[] = "BrowserOmniboxViewViews";
301 if (name == kOmnibox) {
302 if (event.type() == ui::ET_GESTURE_SCROLL_BEGIN)
303 return GESTURE_OMNIBOX_SCROLL;
304 if (event.type() == ui::ET_GESTURE_PINCH_BEGIN)
305 return GESTURE_OMNIBOX_PINCH;
306 return GESTURE_UNKNOWN;
309 return GESTURE_UNKNOWN;
312 } // namespace ash