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