| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "ash/touch/touch_uma.h" | 5 #include "ash/touch/touch_uma.h" |
| 6 | 6 |
| 7 #include "ash/common/wm_shell.h" | 7 #include "ash/common/wm_shell.h" |
| 8 #include "base/metrics/histogram.h" | 8 #include "base/metrics/histogram.h" |
| 9 #include "base/strings/stringprintf.h" | 9 #include "base/strings/stringprintf.h" |
| 10 #include "ui/aura/env.h" | 10 #include "ui/aura/env.h" |
| (...skipping 22 matching lines...) Expand all Loading... |
| 33 | 33 |
| 34 // Last time-stamp of the last touch-end event. | 34 // Last time-stamp of the last touch-end event. |
| 35 base::TimeTicks last_release_time_; | 35 base::TimeTicks last_release_time_; |
| 36 | 36 |
| 37 // Stores the time of the last touch released on this window (if there was a | 37 // Stores the time of the last touch released on this window (if there was a |
| 38 // multi-touch gesture on the window, then this is the release-time of the | 38 // multi-touch gesture on the window, then this is the release-time of the |
| 39 // last touch on the window). | 39 // last touch on the window). |
| 40 base::TimeTicks last_mt_time_; | 40 base::TimeTicks last_mt_time_; |
| 41 }; | 41 }; |
| 42 | 42 |
| 43 DEFINE_OWNED_WINDOW_PROPERTY_KEY(WindowTouchDetails, | 43 DEFINE_OWNED_WINDOW_PROPERTY_KEY(WindowTouchDetails, kWindowTouchDetails, NULL); |
| 44 kWindowTouchDetails, | |
| 45 NULL); | |
| 46 } | 44 } |
| 47 | 45 |
| 48 DECLARE_WINDOW_PROPERTY_TYPE(WindowTouchDetails*); | 46 DECLARE_WINDOW_PROPERTY_TYPE(WindowTouchDetails*); |
| 49 | 47 |
| 50 namespace ash { | 48 namespace ash { |
| 51 | 49 |
| 52 // static | 50 // static |
| 53 TouchUMA* TouchUMA::GetInstance() { | 51 TouchUMA* TouchUMA::GetInstance() { |
| 54 return base::Singleton<TouchUMA>::get(); | 52 return base::Singleton<TouchUMA>::get(); |
| 55 } | 53 } |
| (...skipping 11 matching lines...) Expand all Loading... |
| 67 " events"; | 65 " events"; |
| 68 return; | 66 return; |
| 69 } | 67 } |
| 70 details->last_mt_time_ = event.time_stamp(); | 68 details->last_mt_time_ = event.time_stamp(); |
| 71 } | 69 } |
| 72 } | 70 } |
| 73 | 71 |
| 74 void TouchUMA::RecordGestureAction(GestureActionType action) { | 72 void TouchUMA::RecordGestureAction(GestureActionType action) { |
| 75 if (action == GESTURE_UNKNOWN || action >= GESTURE_ACTION_COUNT) | 73 if (action == GESTURE_UNKNOWN || action >= GESTURE_ACTION_COUNT) |
| 76 return; | 74 return; |
| 77 UMA_HISTOGRAM_ENUMERATION("Ash.GestureTarget", action, | 75 UMA_HISTOGRAM_ENUMERATION("Ash.GestureTarget", action, GESTURE_ACTION_COUNT); |
| 78 GESTURE_ACTION_COUNT); | |
| 79 } | 76 } |
| 80 | 77 |
| 81 void TouchUMA::RecordTouchEvent(aura::Window* target, | 78 void TouchUMA::RecordTouchEvent(aura::Window* target, |
| 82 const ui::TouchEvent& event) { | 79 const ui::TouchEvent& event) { |
| 83 UMA_HISTOGRAM_CUSTOM_COUNTS( | 80 UMA_HISTOGRAM_CUSTOM_COUNTS( |
| 84 "Ash.TouchRadius", | 81 "Ash.TouchRadius", |
| 85 static_cast<int>(std::max(event.pointer_details().radius_x, | 82 static_cast<int>(std::max(event.pointer_details().radius_x, |
| 86 event.pointer_details().radius_y)), | 83 event.pointer_details().radius_y)), |
| 87 1, 500, 100); | 84 1, 500, 100); |
| 88 | 85 |
| 89 UpdateTouchState(event); | 86 UpdateTouchState(event); |
| 90 | 87 |
| 91 WindowTouchDetails* details = target->GetProperty(kWindowTouchDetails); | 88 WindowTouchDetails* details = target->GetProperty(kWindowTouchDetails); |
| 92 if (!details) { | 89 if (!details) { |
| 93 details = new WindowTouchDetails; | 90 details = new WindowTouchDetails; |
| 94 target->SetProperty(kWindowTouchDetails, details); | 91 target->SetProperty(kWindowTouchDetails, details); |
| 95 } | 92 } |
| 96 | 93 |
| 97 // Record the location of the touch points. | 94 // Record the location of the touch points. |
| 98 const int kBucketCountForLocation = 100; | 95 const int kBucketCountForLocation = 100; |
| 99 const gfx::Rect bounds = target->GetRootWindow()->bounds(); | 96 const gfx::Rect bounds = target->GetRootWindow()->bounds(); |
| 100 const int bucket_size_x = std::max(1, | 97 const int bucket_size_x = |
| 101 bounds.width() / kBucketCountForLocation); | 98 std::max(1, bounds.width() / kBucketCountForLocation); |
| 102 const int bucket_size_y = std::max(1, | 99 const int bucket_size_y = |
| 103 bounds.height() / kBucketCountForLocation); | 100 std::max(1, bounds.height() / kBucketCountForLocation); |
| 104 | 101 |
| 105 gfx::Point position = event.root_location(); | 102 gfx::Point position = event.root_location(); |
| 106 | 103 |
| 107 // Prefer raw event location (when available) over calibrated location. | 104 // Prefer raw event location (when available) over calibrated location. |
| 108 if (event.HasNativeEvent()) { | 105 if (event.HasNativeEvent()) { |
| 109 position = ui::EventLocationFromNative(event.native_event()); | 106 position = ui::EventLocationFromNative(event.native_event()); |
| 110 position = gfx::ScaleToFlooredPoint( | 107 position = gfx::ScaleToFlooredPoint( |
| 111 position, 1.f / target->layer()->device_scale_factor()); | 108 position, 1.f / target->layer()->device_scale_factor()); |
| 112 } | 109 } |
| 113 | 110 |
| 114 position.set_x(std::min(bounds.width() - 1, std::max(0, position.x()))); | 111 position.set_x(std::min(bounds.width() - 1, std::max(0, position.x()))); |
| 115 position.set_y(std::min(bounds.height() - 1, std::max(0, position.y()))); | 112 position.set_y(std::min(bounds.height() - 1, std::max(0, position.y()))); |
| 116 | 113 |
| 117 UMA_HISTOGRAM_CUSTOM_COUNTS("Ash.TouchPositionX", | 114 UMA_HISTOGRAM_CUSTOM_COUNTS( |
| 118 position.x() / bucket_size_x, | 115 "Ash.TouchPositionX", position.x() / bucket_size_x, 0, |
| 119 0, kBucketCountForLocation, kBucketCountForLocation + 1); | 116 kBucketCountForLocation, kBucketCountForLocation + 1); |
| 120 UMA_HISTOGRAM_CUSTOM_COUNTS("Ash.TouchPositionY", | 117 UMA_HISTOGRAM_CUSTOM_COUNTS( |
| 121 position.y() / bucket_size_y, | 118 "Ash.TouchPositionY", position.y() / bucket_size_y, 0, |
| 122 0, kBucketCountForLocation, kBucketCountForLocation + 1); | 119 kBucketCountForLocation, kBucketCountForLocation + 1); |
| 123 | 120 |
| 124 if (event.type() == ui::ET_TOUCH_PRESSED) { | 121 if (event.type() == ui::ET_TOUCH_PRESSED) { |
| 125 WmShell::Get()->RecordUserMetricsAction(UMA_TOUCHSCREEN_TAP_DOWN); | 122 WmShell::Get()->RecordUserMetricsAction(UMA_TOUCHSCREEN_TAP_DOWN); |
| 126 | 123 |
| 127 details->last_start_time_[event.touch_id()] = event.time_stamp(); | 124 details->last_start_time_[event.touch_id()] = event.time_stamp(); |
| 128 details->start_touch_position_[event.touch_id()] = event.root_location(); | 125 details->start_touch_position_[event.touch_id()] = event.root_location(); |
| 129 details->last_touch_position_[event.touch_id()] = event.location(); | 126 details->last_touch_position_[event.touch_id()] = event.location(); |
| 130 | 127 |
| 131 if (details->last_release_time_.ToInternalValue()) { | 128 if (details->last_release_time_.ToInternalValue()) { |
| 132 // Measuring the interval between a touch-release and the next | 129 // Measuring the interval between a touch-release and the next |
| 133 // touch-start is probably less useful when doing multi-touch (e.g. | 130 // touch-start is probably less useful when doing multi-touch (e.g. |
| 134 // gestures, or multi-touch friendly apps). So count this only if the user | 131 // gestures, or multi-touch friendly apps). So count this only if the user |
| 135 // hasn't done any multi-touch during the last 30 seconds. | 132 // hasn't done any multi-touch during the last 30 seconds. |
| 136 base::TimeDelta diff = event.time_stamp() - details->last_mt_time_; | 133 base::TimeDelta diff = event.time_stamp() - details->last_mt_time_; |
| 137 if (diff.InSeconds() > 30) { | 134 if (diff.InSeconds() > 30) { |
| 138 base::TimeDelta gap = event.time_stamp() - details->last_release_time_; | 135 base::TimeDelta gap = event.time_stamp() - details->last_release_time_; |
| 139 UMA_HISTOGRAM_COUNTS_10000("Ash.TouchStartAfterEnd", | 136 UMA_HISTOGRAM_COUNTS_10000("Ash.TouchStartAfterEnd", |
| 140 gap.InMilliseconds()); | 137 gap.InMilliseconds()); |
| 141 } | 138 } |
| 142 } | 139 } |
| 143 | 140 |
| 144 // Record the number of touch-points currently active for the window. | 141 // Record the number of touch-points currently active for the window. |
| 145 const int kMaxTouchPoints = 10; | 142 const int kMaxTouchPoints = 10; |
| 146 UMA_HISTOGRAM_CUSTOM_COUNTS("Ash.ActiveTouchPoints", | 143 UMA_HISTOGRAM_CUSTOM_COUNTS("Ash.ActiveTouchPoints", |
| 147 details->last_start_time_.size(), | 144 details->last_start_time_.size(), 1, |
| 148 1, kMaxTouchPoints, kMaxTouchPoints + 1); | 145 kMaxTouchPoints, kMaxTouchPoints + 1); |
| 149 } else if (event.type() == ui::ET_TOUCH_RELEASED) { | 146 } else if (event.type() == ui::ET_TOUCH_RELEASED) { |
| 150 if (details->last_start_time_.count(event.touch_id())) { | 147 if (details->last_start_time_.count(event.touch_id())) { |
| 151 base::TimeDelta duration = event.time_stamp() - | 148 base::TimeDelta duration = |
| 152 details->last_start_time_[event.touch_id()]; | 149 event.time_stamp() - details->last_start_time_[event.touch_id()]; |
| 153 // Look for touches that were [almost] stationary for a long time. | 150 // Look for touches that were [almost] stationary for a long time. |
| 154 const double kLongStationaryTouchDuration = 10; | 151 const double kLongStationaryTouchDuration = 10; |
| 155 const int kLongStationaryTouchDistanceSquared = 100; | 152 const int kLongStationaryTouchDistanceSquared = 100; |
| 156 if (duration.InSecondsF() > kLongStationaryTouchDuration) { | 153 if (duration.InSecondsF() > kLongStationaryTouchDuration) { |
| 157 gfx::Vector2d distance = event.root_location() - | 154 gfx::Vector2d distance = |
| 155 event.root_location() - |
| 158 details->start_touch_position_[event.touch_id()]; | 156 details->start_touch_position_[event.touch_id()]; |
| 159 if (distance.LengthSquared() < kLongStationaryTouchDistanceSquared) { | 157 if (distance.LengthSquared() < kLongStationaryTouchDistanceSquared) { |
| 160 UMA_HISTOGRAM_CUSTOM_COUNTS("Ash.StationaryTouchDuration", | 158 UMA_HISTOGRAM_CUSTOM_COUNTS("Ash.StationaryTouchDuration", |
| 161 duration.InSeconds(), | 159 duration.InSeconds(), |
| 162 kLongStationaryTouchDuration, | 160 kLongStationaryTouchDuration, 1000, 20); |
| 163 1000, | |
| 164 20); | |
| 165 } | 161 } |
| 166 } | 162 } |
| 167 } | 163 } |
| 168 details->last_start_time_.erase(event.touch_id()); | 164 details->last_start_time_.erase(event.touch_id()); |
| 169 details->last_move_time_.erase(event.touch_id()); | 165 details->last_move_time_.erase(event.touch_id()); |
| 170 details->start_touch_position_.erase(event.touch_id()); | 166 details->start_touch_position_.erase(event.touch_id()); |
| 171 details->last_touch_position_.erase(event.touch_id()); | 167 details->last_touch_position_.erase(event.touch_id()); |
| 172 details->last_release_time_ = event.time_stamp(); | 168 details->last_release_time_ = event.time_stamp(); |
| 173 } else if (event.type() == ui::ET_TOUCH_MOVED) { | 169 } else if (event.type() == ui::ET_TOUCH_MOVED) { |
| 174 int distance = 0; | 170 int distance = 0; |
| 175 if (details->last_touch_position_.count(event.touch_id())) { | 171 if (details->last_touch_position_.count(event.touch_id())) { |
| 176 gfx::Point lastpos = details->last_touch_position_[event.touch_id()]; | 172 gfx::Point lastpos = details->last_touch_position_[event.touch_id()]; |
| 177 distance = | 173 distance = |
| 178 std::abs(lastpos.x() - event.x()) + std::abs(lastpos.y() - event.y()); | 174 std::abs(lastpos.x() - event.x()) + std::abs(lastpos.y() - event.y()); |
| 179 } | 175 } |
| 180 | 176 |
| 181 if (details->last_move_time_.count(event.touch_id())) { | 177 if (details->last_move_time_.count(event.touch_id())) { |
| 182 base::TimeDelta move_delay = event.time_stamp() - | 178 base::TimeDelta move_delay = |
| 183 details->last_move_time_[event.touch_id()]; | 179 event.time_stamp() - details->last_move_time_[event.touch_id()]; |
| 184 UMA_HISTOGRAM_CUSTOM_COUNTS("Ash.TouchMoveInterval", | 180 UMA_HISTOGRAM_CUSTOM_COUNTS("Ash.TouchMoveInterval", |
| 185 move_delay.InMilliseconds(), | 181 move_delay.InMilliseconds(), 1, 50, 25); |
| 186 1, 50, 25); | |
| 187 } | 182 } |
| 188 | 183 |
| 189 UMA_HISTOGRAM_CUSTOM_COUNTS("Ash.TouchMoveSteps", distance, 1, 1000, 50); | 184 UMA_HISTOGRAM_CUSTOM_COUNTS("Ash.TouchMoveSteps", distance, 1, 1000, 50); |
| 190 | 185 |
| 191 details->last_move_time_[event.touch_id()] = event.time_stamp(); | 186 details->last_move_time_[event.touch_id()] = event.time_stamp(); |
| 192 details->last_touch_position_[event.touch_id()] = event.location(); | 187 details->last_touch_position_[event.touch_id()] = event.location(); |
| 193 } | 188 } |
| 194 } | 189 } |
| 195 | 190 |
| 196 TouchUMA::TouchUMA() | 191 TouchUMA::TouchUMA() |
| 197 : is_single_finger_gesture_(false), | 192 : is_single_finger_gesture_(false), |
| 198 touch_in_progress_(false), | 193 touch_in_progress_(false), |
| 199 burst_length_(0) { | 194 burst_length_(0) {} |
| 200 } | |
| 201 | 195 |
| 202 TouchUMA::~TouchUMA() { | 196 TouchUMA::~TouchUMA() {} |
| 203 } | |
| 204 | 197 |
| 205 void TouchUMA::UpdateTouchState(const ui::TouchEvent& event) { | 198 void TouchUMA::UpdateTouchState(const ui::TouchEvent& event) { |
| 206 if (event.type() == ui::ET_TOUCH_PRESSED) { | 199 if (event.type() == ui::ET_TOUCH_PRESSED) { |
| 207 if (!touch_in_progress_) { | 200 if (!touch_in_progress_) { |
| 208 is_single_finger_gesture_ = true; | 201 is_single_finger_gesture_ = true; |
| 209 base::TimeDelta difference = event.time_stamp() - last_touch_down_time_; | 202 base::TimeDelta difference = event.time_stamp() - last_touch_down_time_; |
| 210 if (difference > base::TimeDelta::FromMilliseconds(250)) { | 203 if (difference > base::TimeDelta::FromMilliseconds(250)) { |
| 211 if (burst_length_) { | 204 if (burst_length_) { |
| 212 UMA_HISTOGRAM_COUNTS_100("Ash.TouchStartBurst", | 205 UMA_HISTOGRAM_COUNTS_100("Ash.TouchStartBurst", |
| 213 std::min(burst_length_, 100)); | 206 std::min(burst_length_, 100)); |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 265 return GESTURE_UNKNOWN; | 258 return GESTURE_UNKNOWN; |
| 266 | 259 |
| 267 // |widget| may be in the process of destroying if it has ownership | 260 // |widget| may be in the process of destroying if it has ownership |
| 268 // views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET and |event| was | 261 // views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET and |event| was |
| 269 // dispatched as part of gesture state cleanup. In this case the RootView | 262 // dispatched as part of gesture state cleanup. In this case the RootView |
| 270 // of |widget| may no longer exist, so check before calling into any | 263 // of |widget| may no longer exist, so check before calling into any |
| 271 // RootView methods. | 264 // RootView methods. |
| 272 if (!widget->GetRootView()) | 265 if (!widget->GetRootView()) |
| 273 return GESTURE_UNKNOWN; | 266 return GESTURE_UNKNOWN; |
| 274 | 267 |
| 275 views::View* view = widget->GetRootView()-> | 268 views::View* view = |
| 276 GetEventHandlerForPoint(event.location()); | 269 widget->GetRootView()->GetEventHandlerForPoint(event.location()); |
| 277 if (!view) | 270 if (!view) |
| 278 return GESTURE_UNKNOWN; | 271 return GESTURE_UNKNOWN; |
| 279 | 272 |
| 280 name = view->GetClassName(); | 273 name = view->GetClassName(); |
| 281 | 274 |
| 282 const char kTabStrip[] = "TabStrip"; | 275 const char kTabStrip[] = "TabStrip"; |
| 283 const char kTab[] = "BrowserTab"; | 276 const char kTab[] = "BrowserTab"; |
| 284 if (name == kTabStrip || name == kTab) { | 277 if (name == kTabStrip || name == kTab) { |
| 285 if (event.type() == ui::ET_GESTURE_SCROLL_BEGIN) | 278 if (event.type() == ui::ET_GESTURE_SCROLL_BEGIN) |
| 286 return GESTURE_TABSTRIP_SCROLL; | 279 return GESTURE_TABSTRIP_SCROLL; |
| (...skipping 10 matching lines...) Expand all Loading... |
| 297 return GESTURE_OMNIBOX_SCROLL; | 290 return GESTURE_OMNIBOX_SCROLL; |
| 298 if (event.type() == ui::ET_GESTURE_PINCH_BEGIN) | 291 if (event.type() == ui::ET_GESTURE_PINCH_BEGIN) |
| 299 return GESTURE_OMNIBOX_PINCH; | 292 return GESTURE_OMNIBOX_PINCH; |
| 300 return GESTURE_UNKNOWN; | 293 return GESTURE_UNKNOWN; |
| 301 } | 294 } |
| 302 | 295 |
| 303 return GESTURE_UNKNOWN; | 296 return GESTURE_UNKNOWN; |
| 304 } | 297 } |
| 305 | 298 |
| 306 } // namespace ash | 299 } // namespace ash |
| OLD | NEW |