summaryrefslogtreecommitdiffstats
path: root/content/browser/renderer_host/input/motion_event_android.cc
blob: 5a437f4e4694a5f4f3c52527de2e2f91de20627c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/browser/renderer_host/input/motion_event_android.h"

#include <android/input.h>

#include <cmath>

#include "base/android/jni_android.h"
#include "jni/MotionEvent_jni.h"
#include "ui/events/event_constants.h"

using base::android::AttachCurrentThread;
using namespace JNI_MotionEvent;

namespace content {
namespace {

MotionEventAndroid::Action FromAndroidAction(int android_action) {
  switch (android_action) {
    case ACTION_DOWN:
      return MotionEventAndroid::ACTION_DOWN;
    case ACTION_UP:
      return MotionEventAndroid::ACTION_UP;
    case ACTION_MOVE:
      return MotionEventAndroid::ACTION_MOVE;
    case ACTION_CANCEL:
      return MotionEventAndroid::ACTION_CANCEL;
    case ACTION_POINTER_DOWN:
      return MotionEventAndroid::ACTION_POINTER_DOWN;
    case ACTION_POINTER_UP:
      return MotionEventAndroid::ACTION_POINTER_UP;
    default:
      NOTREACHED() << "Invalid Android MotionEvent type for gesture detection: "
                   << android_action;
  };
  return MotionEventAndroid::ACTION_CANCEL;
}

MotionEventAndroid::ToolType FromAndroidToolType(int android_tool_type) {
  switch (android_tool_type) {
    case TOOL_TYPE_UNKNOWN:
      return MotionEventAndroid::TOOL_TYPE_UNKNOWN;
    case TOOL_TYPE_FINGER:
      return MotionEventAndroid::TOOL_TYPE_FINGER;
    case TOOL_TYPE_STYLUS:
      return MotionEventAndroid::TOOL_TYPE_STYLUS;
    case TOOL_TYPE_MOUSE:
      return MotionEventAndroid::TOOL_TYPE_MOUSE;
    case TOOL_TYPE_ERASER:
      return MotionEventAndroid::TOOL_TYPE_ERASER;
    default:
      NOTREACHED() << "Invalid Android MotionEvent tool type: "
                   << android_tool_type;
  };
  return MotionEventAndroid::TOOL_TYPE_UNKNOWN;
}

int FromAndroidButtonState(int button_state) {
  int result = 0;
  if ((button_state & BUTTON_BACK) != 0)
    result |= MotionEventAndroid::BUTTON_BACK;
  if ((button_state & BUTTON_FORWARD) != 0)
    result |= MotionEventAndroid::BUTTON_FORWARD;
  if ((button_state & BUTTON_PRIMARY) != 0)
    result |= MotionEventAndroid::BUTTON_PRIMARY;
  if ((button_state & BUTTON_SECONDARY) != 0)
    result |= MotionEventAndroid::BUTTON_SECONDARY;
  if ((button_state & BUTTON_TERTIARY) != 0)
    result |= MotionEventAndroid::BUTTON_TERTIARY;
  return result;
}

int FromAndroidMetaState(int meta_state) {
  int flags = ui::EF_NONE;
  if ((meta_state & AMETA_SHIFT_ON) != 0)
    flags |= ui::EF_SHIFT_DOWN;
  if ((meta_state & AMETA_CTRL_ON) != 0)
    flags |= ui::EF_CONTROL_DOWN;
  if ((meta_state & AMETA_ALT_ON) != 0)
    flags |= ui::EF_ALT_DOWN;
  if ((meta_state & AMETA_META_ON) != 0)
    flags |= ui::EF_COMMAND_DOWN;
  if ((meta_state & AMETA_CAPS_LOCK_ON) != 0)
    flags |= ui::EF_CAPS_LOCK_DOWN;
  return flags;
}

base::TimeTicks FromAndroidTime(int64 time_ms) {
  return base::TimeTicks() + base::TimeDelta::FromMilliseconds(time_ms);
}

float ToValidFloat(float x) {
  if (std::isnan(x))
    return 0.f;

  // Wildly large orientation values have been observed in the wild after device
  // rotation. There's not much we can do in that case other than simply
  // sanitize results beyond an absurd and arbitrary threshold.
  if (std::abs(x) > 1e5f)
    return 0.f;

  return x;
}

size_t ToValidHistorySize(jint history_size, ui::MotionEvent::Action action) {
  DCHECK_GE(history_size, 0);
  // While the spec states that only ACTION_MOVE events should contain
  // historical entries, it's possible that an embedder could repurpose an
  // ACTION_MOVE event into a different kind of event. In that case, the
  // historical values are meaningless, and should not be exposed.
  if (action != ui::MotionEvent::ACTION_MOVE)
    return 0;
  return history_size;
}

}  // namespace

MotionEventAndroid::Pointer::Pointer(jint id,
                                     jfloat pos_x_pixels,
                                     jfloat pos_y_pixels,
                                     jfloat touch_major_pixels,
                                     jfloat touch_minor_pixels,
                                     jfloat orientation_rad,
                                     jint tool_type)
    : id(id),
      pos_x_pixels(pos_x_pixels),
      pos_y_pixels(pos_y_pixels),
      touch_major_pixels(touch_major_pixels),
      touch_minor_pixels(touch_minor_pixels),
      orientation_rad(orientation_rad),
      tool_type(tool_type) {
}

MotionEventAndroid::CachedPointer::CachedPointer()
    : id(0),
      touch_major(0),
      touch_minor(0),
      orientation(0),
      tool_type(TOOL_TYPE_UNKNOWN) {
}

MotionEventAndroid::MotionEventAndroid(float pix_to_dip,
                                       JNIEnv* env,
                                       jobject event,
                                       jlong time_ms,
                                       jint android_action,
                                       jint pointer_count,
                                       jint history_size,
                                       jint action_index,
                                       jint android_button_state,
                                       jint meta_state,
                                       jfloat raw_offset_x_pixels,
                                       jfloat raw_offset_y_pixels,
                                       const Pointer& pointer0,
                                       const Pointer& pointer1)
    : pix_to_dip_(pix_to_dip),
      cached_time_(FromAndroidTime(time_ms)),
      cached_action_(FromAndroidAction(android_action)),
      cached_pointer_count_(pointer_count),
      cached_history_size_(ToValidHistorySize(history_size, cached_action_)),
      cached_action_index_(action_index),
      cached_button_state_(FromAndroidButtonState(android_button_state)),
      cached_flags_(FromAndroidMetaState(meta_state)),
      cached_raw_position_offset_(ToDips(raw_offset_x_pixels),
                                  ToDips(raw_offset_y_pixels)) {
  DCHECK_GT(pointer_count, 0);

  event_.Reset(env, event);
  if (cached_pointer_count_ > MAX_POINTERS_TO_CACHE || cached_history_size_ > 0)
    DCHECK(event_.obj());

  cached_pointers_[0] = FromAndroidPointer(pointer0);
  cached_pointers_[1] = FromAndroidPointer(pointer1);
}

MotionEventAndroid::~MotionEventAndroid() {
}

int MotionEventAndroid::GetId() const {
  return 0;
}

MotionEventAndroid::Action MotionEventAndroid::GetAction() const {
  return cached_action_;
}

int MotionEventAndroid::GetActionIndex() const {
  DCHECK(cached_action_ == ACTION_POINTER_UP ||
         cached_action_ == ACTION_POINTER_DOWN)
      << "Invalid action for GetActionIndex(): " << cached_action_;
  DCHECK_GE(cached_action_index_, 0);
  DCHECK_LT(cached_action_index_, static_cast<int>(cached_pointer_count_));
  return cached_action_index_;
}

size_t MotionEventAndroid::GetPointerCount() const {
  return cached_pointer_count_;
}

int MotionEventAndroid::GetPointerId(size_t pointer_index) const {
  DCHECK_LT(pointer_index, cached_pointer_count_);
  if (pointer_index < MAX_POINTERS_TO_CACHE)
    return cached_pointers_[pointer_index].id;
  return Java_MotionEvent_getPointerId(
      AttachCurrentThread(), event_.obj(), pointer_index);
}

float MotionEventAndroid::GetX(size_t pointer_index) const {
  DCHECK_LT(pointer_index, cached_pointer_count_);
  if (pointer_index < MAX_POINTERS_TO_CACHE)
    return cached_pointers_[pointer_index].position.x();
  return ToDips(Java_MotionEvent_getXF_I(
      AttachCurrentThread(), event_.obj(), pointer_index));
}

float MotionEventAndroid::GetY(size_t pointer_index) const {
  DCHECK_LT(pointer_index, cached_pointer_count_);
  if (pointer_index < MAX_POINTERS_TO_CACHE)
    return cached_pointers_[pointer_index].position.y();
  return ToDips(Java_MotionEvent_getYF_I(
      AttachCurrentThread(), event_.obj(), pointer_index));
}

float MotionEventAndroid::GetRawX(size_t pointer_index) const {
  return GetX(pointer_index) + cached_raw_position_offset_.x();
}

float MotionEventAndroid::GetRawY(size_t pointer_index) const {
  return GetY(pointer_index) + cached_raw_position_offset_.y();
}

float MotionEventAndroid::GetTouchMajor(size_t pointer_index) const {
  DCHECK_LT(pointer_index, cached_pointer_count_);
  if (pointer_index < MAX_POINTERS_TO_CACHE)
    return cached_pointers_[pointer_index].touch_major;
  return ToDips(Java_MotionEvent_getTouchMajorF_I(
      AttachCurrentThread(), event_.obj(), pointer_index));
}

float MotionEventAndroid::GetTouchMinor(size_t pointer_index) const {
  DCHECK_LT(pointer_index, cached_pointer_count_);
  if (pointer_index < MAX_POINTERS_TO_CACHE)
    return cached_pointers_[pointer_index].touch_minor;
  return ToDips(Java_MotionEvent_getTouchMinorF_I(
      AttachCurrentThread(), event_.obj(), pointer_index));
}

float MotionEventAndroid::GetOrientation(size_t pointer_index) const {
  DCHECK_LT(pointer_index, cached_pointer_count_);
  if (pointer_index < MAX_POINTERS_TO_CACHE)
    return cached_pointers_[pointer_index].orientation;
  return ToValidFloat(Java_MotionEvent_getOrientationF_I(
      AttachCurrentThread(), event_.obj(), pointer_index));
}

float MotionEventAndroid::GetPressure(size_t pointer_index) const {
  DCHECK_LT(pointer_index, cached_pointer_count_);
  // Note that this early return is a special case exercised only in testing, as
  // caching the pressure values is not a worthwhile optimization (they're
  // accessed at most once per event instance).
  if (!event_.obj())
    return 0.f;
  return Java_MotionEvent_getPressureF_I(
      AttachCurrentThread(), event_.obj(), pointer_index);
}

base::TimeTicks MotionEventAndroid::GetEventTime() const {
  return cached_time_;
}

size_t MotionEventAndroid::GetHistorySize() const {
  return cached_history_size_;
}

base::TimeTicks MotionEventAndroid::GetHistoricalEventTime(
    size_t historical_index) const {
  return FromAndroidTime(Java_MotionEvent_getHistoricalEventTime(
      AttachCurrentThread(), event_.obj(), historical_index));
}

float MotionEventAndroid::GetHistoricalTouchMajor(
    size_t pointer_index,
    size_t historical_index) const {
  return ToDips(Java_MotionEvent_getHistoricalTouchMajorF_I_I(
      AttachCurrentThread(), event_.obj(), pointer_index, historical_index));
}

float MotionEventAndroid::GetHistoricalX(size_t pointer_index,
                                         size_t historical_index) const {
  return ToDips(Java_MotionEvent_getHistoricalXF_I_I(
      AttachCurrentThread(), event_.obj(), pointer_index, historical_index));
}

float MotionEventAndroid::GetHistoricalY(size_t pointer_index,
                                         size_t historical_index) const {
  return ToDips(Java_MotionEvent_getHistoricalYF_I_I(
      AttachCurrentThread(), event_.obj(), pointer_index, historical_index));
}

ui::MotionEvent::ToolType MotionEventAndroid::GetToolType(
    size_t pointer_index) const {
  DCHECK_LT(pointer_index, cached_pointer_count_);
  if (pointer_index < MAX_POINTERS_TO_CACHE)
    return cached_pointers_[pointer_index].tool_type;
  return FromAndroidToolType(Java_MotionEvent_getToolType(
      AttachCurrentThread(), event_.obj(), pointer_index));
}

int MotionEventAndroid::GetButtonState() const {
  return cached_button_state_;
}

int MotionEventAndroid::GetFlags() const {
  return cached_flags_;
}

float MotionEventAndroid::ToDips(float pixels) const {
  return pixels * pix_to_dip_;
}

MotionEventAndroid::CachedPointer MotionEventAndroid::FromAndroidPointer(
    const Pointer& pointer) const {
  CachedPointer result;
  result.id = pointer.id;
  result.position =
      gfx::PointF(ToDips(pointer.pos_x_pixels), ToDips(pointer.pos_y_pixels));
  result.touch_major = ToDips(pointer.touch_major_pixels);
  result.touch_minor = ToDips(pointer.touch_minor_pixels);
  result.orientation = ToValidFloat(pointer.orientation_rad);
  result.tool_type = FromAndroidToolType(pointer.tool_type);
  return result;
}

// static
bool MotionEventAndroid::RegisterMotionEventAndroid(JNIEnv* env) {
  return JNI_MotionEvent::RegisterNativesImpl(env);
}

}  // namespace content