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. 4 5 #include "content/browser/renderer_host/input/motion_event_android.h" 6 7 #include "base/android/jni_android.h" 8 #include "jni/MotionEvent_jni.h" 9 10 using base::android::AttachCurrentThread; 11 using namespace JNI_MotionEvent; 12 13 namespace content { 14 namespace { 15 16 int ToAndroidAction(MotionEventAndroid::Action action) { 17 switch (action) { 18 case MotionEventAndroid::ACTION_DOWN: 19 return ACTION_DOWN; 20 case MotionEventAndroid::ACTION_UP: 21 return ACTION_UP; 22 case MotionEventAndroid::ACTION_MOVE: 23 return ACTION_MOVE; 24 case MotionEventAndroid::ACTION_CANCEL: 25 return ACTION_CANCEL; 26 case MotionEventAndroid::ACTION_POINTER_DOWN: 27 return ACTION_POINTER_DOWN; 28 case MotionEventAndroid::ACTION_POINTER_UP: 29 return ACTION_POINTER_UP; 30 }; 31 NOTREACHED() << "Invalid Android MotionEvent type for gesture detection: " 32 << action; 33 return ACTION_CANCEL; 34 } 35 36 MotionEventAndroid::Action FromAndroidAction(int android_action) { 37 switch (android_action) { 38 case ACTION_DOWN: 39 return MotionEventAndroid::ACTION_DOWN; 40 case ACTION_UP: 41 return MotionEventAndroid::ACTION_UP; 42 case ACTION_MOVE: 43 return MotionEventAndroid::ACTION_MOVE; 44 case ACTION_CANCEL: 45 return MotionEventAndroid::ACTION_CANCEL; 46 case ACTION_POINTER_DOWN: 47 return MotionEventAndroid::ACTION_POINTER_DOWN; 48 case ACTION_POINTER_UP: 49 return MotionEventAndroid::ACTION_POINTER_UP; 50 default: 51 NOTREACHED() << "Invalid Android MotionEvent type for gesture detection: " 52 << android_action; 53 }; 54 return MotionEventAndroid::ACTION_CANCEL; 55 } 56 57 MotionEventAndroid::ToolType FromAndroidToolType(int android_tool_type) { 58 switch (android_tool_type) { 59 case TOOL_TYPE_UNKNOWN: 60 return MotionEventAndroid::TOOL_TYPE_UNKNOWN; 61 case TOOL_TYPE_FINGER: 62 return MotionEventAndroid::TOOL_TYPE_FINGER; 63 case TOOL_TYPE_STYLUS: 64 return MotionEventAndroid::TOOL_TYPE_STYLUS; 65 case TOOL_TYPE_MOUSE: 66 return MotionEventAndroid::TOOL_TYPE_MOUSE; 67 default: 68 NOTREACHED() << "Invalid Android MotionEvent tool type: " 69 << android_tool_type; 70 }; 71 return MotionEventAndroid::TOOL_TYPE_UNKNOWN; 72 } 73 74 int FromAndroidButtonState(int button_state) { 75 int result = 0; 76 if ((button_state & BUTTON_BACK) != 0) 77 result |= MotionEventAndroid::BUTTON_BACK; 78 if ((button_state & BUTTON_FORWARD) != 0) 79 result |= MotionEventAndroid::BUTTON_FORWARD; 80 if ((button_state & BUTTON_PRIMARY) != 0) 81 result |= MotionEventAndroid::BUTTON_PRIMARY; 82 if ((button_state & BUTTON_SECONDARY) != 0) 83 result |= MotionEventAndroid::BUTTON_SECONDARY; 84 if ((button_state & BUTTON_TERTIARY) != 0) 85 result |= MotionEventAndroid::BUTTON_TERTIARY; 86 return result; 87 } 88 89 int64 ToAndroidTime(base::TimeTicks time) { 90 return (time - base::TimeTicks()).InMilliseconds(); 91 } 92 93 base::TimeTicks FromAndroidTime(int64 time_ms) { 94 return base::TimeTicks() + base::TimeDelta::FromMilliseconds(time_ms); 95 } 96 97 } // namespace 98 99 MotionEventAndroid::MotionEventAndroid(float pix_to_dip, 100 JNIEnv* env, 101 jobject event, 102 jlong time_ms, 103 jint android_action, 104 jint pointer_count, 105 jint history_size, 106 jint action_index, 107 jfloat pos_x_0_pixels, 108 jfloat pos_y_0_pixels, 109 jfloat pos_x_1_pixels, 110 jfloat pos_y_1_pixels, 111 jint pointer_id_0, 112 jint pointer_id_1, 113 jfloat touch_major_0_pixels, 114 jfloat touch_major_1_pixels, 115 jfloat raw_pos_x_pixels, 116 jfloat raw_pos_y_pixels, 117 jint android_tool_type_0, 118 jint android_tool_type_1, 119 jint android_button_state) 120 : cached_time_(FromAndroidTime(time_ms)), 121 cached_action_(FromAndroidAction(android_action)), 122 cached_pointer_count_(pointer_count), 123 cached_history_size_(history_size), 124 cached_action_index_(action_index), 125 cached_button_state_(FromAndroidButtonState(android_button_state)), 126 pix_to_dip_(pix_to_dip), 127 should_recycle_(false) { 128 DCHECK_GT(pointer_count, 0); 129 DCHECK_GE(history_size, 0); 130 131 event_.Reset(env, event); 132 DCHECK(event_.obj()); 133 134 cached_positions_[0] = ToDips(gfx::PointF(pos_x_0_pixels, pos_y_0_pixels)); 135 cached_positions_[1] = ToDips(gfx::PointF(pos_x_1_pixels, pos_y_1_pixels)); 136 cached_pointer_ids_[0] = pointer_id_0; 137 cached_pointer_ids_[1] = pointer_id_1; 138 cached_touch_majors_[0] = ToDips(touch_major_0_pixels); 139 cached_touch_majors_[1] = ToDips(touch_major_1_pixels); 140 cached_raw_position_offset_ = 141 ToDips(gfx::PointF(raw_pos_x_pixels, raw_pos_y_pixels)) - 142 cached_positions_[0]; 143 cached_tool_types_[0] = FromAndroidToolType(android_tool_type_0); 144 cached_tool_types_[1] = FromAndroidToolType(android_tool_type_1); 145 } 146 147 MotionEventAndroid::MotionEventAndroid(float pix_to_dip, 148 JNIEnv* env, 149 jobject event) 150 : cached_time_(FromAndroidTime(Java_MotionEvent_getEventTime(env, event))), 151 cached_action_( 152 FromAndroidAction(Java_MotionEvent_getActionMasked(env, event))), 153 cached_pointer_count_(Java_MotionEvent_getPointerCount(env, event)), 154 cached_history_size_(Java_MotionEvent_getHistorySize(env, event)), 155 cached_action_index_(Java_MotionEvent_getActionIndex(env, event)), 156 cached_button_state_( 157 FromAndroidButtonState(Java_MotionEvent_getButtonState(env, event))), 158 pix_to_dip_(pix_to_dip), 159 should_recycle_(true) { 160 event_.Reset(env, event); 161 DCHECK(event_.obj()); 162 163 for (size_t i = 0; i < MAX_POINTERS_TO_CACHE; ++i) { 164 if (i < cached_pointer_count_) { 165 cached_positions_[i] = 166 ToDips(gfx::PointF(Java_MotionEvent_getXF_I(env, event, i), 167 Java_MotionEvent_getYF_I(env, event, i))); 168 cached_pointer_ids_[i] = Java_MotionEvent_getPointerId(env, event, i); 169 cached_touch_majors_[i] = 170 ToDips(Java_MotionEvent_getTouchMajorF_I(env, event, i)); 171 cached_tool_types_[i] = 172 FromAndroidToolType(Java_MotionEvent_getToolType(env, event, i)); 173 } else { 174 cached_pointer_ids_[i] = 0; 175 cached_touch_majors_[i] = 0.f; 176 cached_tool_types_[i] = MotionEvent::TOOL_TYPE_UNKNOWN; 177 } 178 } 179 180 cached_raw_position_offset_ = 181 ToDips(gfx::PointF(Java_MotionEvent_getRawX(env, event), 182 Java_MotionEvent_getRawY(env, event))) - 183 cached_positions_[0]; 184 } 185 186 MotionEventAndroid::MotionEventAndroid(const MotionEventAndroid& other) 187 : event_(Obtain(other)), 188 cached_time_(other.cached_time_), 189 cached_action_(other.cached_action_), 190 cached_pointer_count_(other.cached_pointer_count_), 191 cached_history_size_(other.cached_history_size_), 192 cached_action_index_(other.cached_action_index_), 193 cached_raw_position_offset_(other.cached_raw_position_offset_), 194 cached_button_state_(other.cached_button_state_), 195 pix_to_dip_(other.pix_to_dip_), 196 should_recycle_(true) { 197 DCHECK(event_.obj()); 198 for (size_t i = 0; i < MAX_POINTERS_TO_CACHE; ++i) { 199 cached_positions_[i] = other.cached_positions_[i]; 200 cached_pointer_ids_[i] = other.cached_pointer_ids_[i]; 201 cached_touch_majors_[i] = other.cached_touch_majors_[i]; 202 cached_tool_types_[i] = other.cached_tool_types_[i]; 203 } 204 } 205 206 MotionEventAndroid::~MotionEventAndroid() { 207 if (should_recycle_) 208 Java_MotionEvent_recycle(AttachCurrentThread(), event_.obj()); 209 } 210 211 int MotionEventAndroid::GetId() const { 212 return 0; 213 } 214 215 MotionEventAndroid::Action MotionEventAndroid::GetAction() const { 216 return cached_action_; 217 } 218 219 int MotionEventAndroid::GetActionIndex() const { 220 return cached_action_index_; 221 } 222 223 size_t MotionEventAndroid::GetPointerCount() const { 224 return cached_pointer_count_; 225 } 226 227 int MotionEventAndroid::GetPointerId(size_t pointer_index) const { 228 DCHECK_LT(pointer_index, cached_pointer_count_); 229 if (pointer_index < MAX_POINTERS_TO_CACHE) 230 return cached_pointer_ids_[pointer_index]; 231 return Java_MotionEvent_getPointerId( 232 AttachCurrentThread(), event_.obj(), pointer_index); 233 } 234 235 float MotionEventAndroid::GetX(size_t pointer_index) const { 236 DCHECK_LT(pointer_index, cached_pointer_count_); 237 if (pointer_index < MAX_POINTERS_TO_CACHE) 238 return cached_positions_[pointer_index].x(); 239 return ToDips(Java_MotionEvent_getXF_I( 240 AttachCurrentThread(), event_.obj(), pointer_index)); 241 } 242 243 float MotionEventAndroid::GetY(size_t pointer_index) const { 244 DCHECK_LT(pointer_index, cached_pointer_count_); 245 if (pointer_index < MAX_POINTERS_TO_CACHE) 246 return cached_positions_[pointer_index].y(); 247 return ToDips(Java_MotionEvent_getYF_I( 248 AttachCurrentThread(), event_.obj(), pointer_index)); 249 } 250 251 float MotionEventAndroid::GetRawX(size_t pointer_index) const { 252 return GetX(pointer_index) + cached_raw_position_offset_.x(); 253 } 254 255 float MotionEventAndroid::GetRawY(size_t pointer_index) const { 256 return GetY(pointer_index) + cached_raw_position_offset_.y(); 257 } 258 259 float MotionEventAndroid::GetTouchMajor(size_t pointer_index) const { 260 DCHECK_LT(pointer_index, cached_pointer_count_); 261 if (pointer_index < MAX_POINTERS_TO_CACHE) 262 return cached_touch_majors_[pointer_index]; 263 return ToDips(Java_MotionEvent_getTouchMajorF_I( 264 AttachCurrentThread(), event_.obj(), pointer_index)); 265 } 266 267 float MotionEventAndroid::GetPressure(size_t pointer_index) const { 268 DCHECK_LT(pointer_index, cached_pointer_count_); 269 return Java_MotionEvent_getPressureF_I( 270 AttachCurrentThread(), event_.obj(), pointer_index); 271 } 272 273 base::TimeTicks MotionEventAndroid::GetEventTime() const { 274 return cached_time_; 275 } 276 277 size_t MotionEventAndroid::GetHistorySize() const { 278 return cached_history_size_; 279 } 280 281 base::TimeTicks MotionEventAndroid::GetHistoricalEventTime( 282 size_t historical_index) const { 283 return FromAndroidTime(Java_MotionEvent_getHistoricalEventTime( 284 AttachCurrentThread(), event_.obj(), historical_index)); 285 } 286 287 float MotionEventAndroid::GetHistoricalTouchMajor( 288 size_t pointer_index, 289 size_t historical_index) const { 290 return ToDips(Java_MotionEvent_getHistoricalTouchMajorF_I_I( 291 AttachCurrentThread(), event_.obj(), pointer_index, historical_index)); 292 } 293 294 float MotionEventAndroid::GetHistoricalX(size_t pointer_index, 295 size_t historical_index) const { 296 return ToDips(Java_MotionEvent_getHistoricalXF_I_I( 297 AttachCurrentThread(), event_.obj(), pointer_index, historical_index)); 298 } 299 300 float MotionEventAndroid::GetHistoricalY(size_t pointer_index, 301 size_t historical_index) const { 302 return ToDips(Java_MotionEvent_getHistoricalYF_I_I( 303 AttachCurrentThread(), event_.obj(), pointer_index, historical_index)); 304 } 305 306 ui::MotionEvent::ToolType MotionEventAndroid::GetToolType( 307 size_t pointer_index) const { 308 DCHECK_LT(pointer_index, cached_pointer_count_); 309 if (pointer_index < MAX_POINTERS_TO_CACHE) 310 return cached_tool_types_[pointer_index]; 311 return FromAndroidToolType(Java_MotionEvent_getToolType( 312 AttachCurrentThread(), event_.obj(), pointer_index)); 313 } 314 315 int MotionEventAndroid::GetButtonState() const { 316 return cached_button_state_; 317 } 318 319 scoped_ptr<ui::MotionEvent> MotionEventAndroid::Clone() const { 320 return scoped_ptr<MotionEvent>(new MotionEventAndroid(*this)); 321 } 322 323 scoped_ptr<ui::MotionEvent> MotionEventAndroid::Cancel() const { 324 // The input coordinates to |MotionEventAndroid| are always in device pixels, 325 // but the cached coordinates are in DIPs. 326 const gfx::PointF position_pixels = 327 gfx::ScalePoint(cached_positions_[0], 1.f / pix_to_dip_); 328 return scoped_ptr<MotionEvent>( 329 new MotionEventAndroid(pix_to_dip_, 330 AttachCurrentThread(), 331 Obtain(GetDownTime(), 332 GetEventTime(), 333 MotionEventAndroid::ACTION_CANCEL, 334 position_pixels.x(), 335 position_pixels.y()).obj())); 336 } 337 338 float MotionEventAndroid::GetTouchMinor(size_t pointer_index) const { 339 return ToDips(Java_MotionEvent_getTouchMinorF_I( 340 AttachCurrentThread(), event_.obj(), pointer_index)); 341 } 342 343 float MotionEventAndroid::GetOrientation() const { 344 return Java_MotionEvent_getOrientationF(AttachCurrentThread(), event_.obj()); 345 } 346 347 base::TimeTicks MotionEventAndroid::GetDownTime() const { 348 return FromAndroidTime( 349 Java_MotionEvent_getDownTime(AttachCurrentThread(), event_.obj())); 350 } 351 352 float MotionEventAndroid::ToDips(float pixels) const { 353 return pixels * pix_to_dip_; 354 } 355 356 gfx::PointF MotionEventAndroid::ToDips(const gfx::PointF& point_pixels) const { 357 return gfx::ScalePoint(point_pixels, pix_to_dip_); 358 } 359 360 // static 361 bool MotionEventAndroid::RegisterMotionEventAndroid(JNIEnv* env) { 362 return JNI_MotionEvent::RegisterNativesImpl(env); 363 } 364 365 // static 366 base::android::ScopedJavaLocalRef<jobject> MotionEventAndroid::Obtain( 367 const MotionEventAndroid& event) { 368 return Java_MotionEvent_obtainAVME_AVME(AttachCurrentThread(), 369 event.event_.obj()); 370 } 371 372 // static 373 base::android::ScopedJavaLocalRef<jobject> MotionEventAndroid::Obtain( 374 base::TimeTicks down_time, 375 base::TimeTicks event_time, 376 Action action, 377 float x_pixels, 378 float y_pixels) { 379 return Java_MotionEvent_obtainAVME_J_J_I_F_F_I(AttachCurrentThread(), 380 ToAndroidTime(down_time), 381 ToAndroidTime(event_time), 382 ToAndroidAction(action), 383 x_pixels, 384 y_pixels, 385 0); 386 } 387 388 } // namespace content 389