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.
5 #include "content/browser/renderer_host/input/motion_event_android.h"
7 #include <android/input.h>
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"
14 using base::android::AttachCurrentThread;
15 using namespace JNI_MotionEvent;
20 MotionEventAndroid::Action FromAndroidAction(int android_action) {
21 switch (android_action) {
23 return MotionEventAndroid::ACTION_DOWN;
25 return MotionEventAndroid::ACTION_UP;
27 return MotionEventAndroid::ACTION_MOVE;
29 return MotionEventAndroid::ACTION_CANCEL;
30 case ACTION_POINTER_DOWN:
31 return MotionEventAndroid::ACTION_POINTER_DOWN;
32 case ACTION_POINTER_UP:
33 return MotionEventAndroid::ACTION_POINTER_UP;
35 NOTREACHED() << "Invalid Android MotionEvent type for gesture detection: "
38 return MotionEventAndroid::ACTION_CANCEL;
41 MotionEventAndroid::ToolType FromAndroidToolType(int android_tool_type) {
42 switch (android_tool_type) {
43 case TOOL_TYPE_UNKNOWN:
44 return MotionEventAndroid::TOOL_TYPE_UNKNOWN;
45 case TOOL_TYPE_FINGER:
46 return MotionEventAndroid::TOOL_TYPE_FINGER;
47 case TOOL_TYPE_STYLUS:
48 return MotionEventAndroid::TOOL_TYPE_STYLUS;
50 return MotionEventAndroid::TOOL_TYPE_MOUSE;
51 case TOOL_TYPE_ERASER:
52 return MotionEventAndroid::TOOL_TYPE_ERASER;
54 NOTREACHED() << "Invalid Android MotionEvent tool type: "
57 return MotionEventAndroid::TOOL_TYPE_UNKNOWN;
60 int FromAndroidButtonState(int button_state) {
62 if ((button_state & BUTTON_BACK) != 0)
63 result |= MotionEventAndroid::BUTTON_BACK;
64 if ((button_state & BUTTON_FORWARD) != 0)
65 result |= MotionEventAndroid::BUTTON_FORWARD;
66 if ((button_state & BUTTON_PRIMARY) != 0)
67 result |= MotionEventAndroid::BUTTON_PRIMARY;
68 if ((button_state & BUTTON_SECONDARY) != 0)
69 result |= MotionEventAndroid::BUTTON_SECONDARY;
70 if ((button_state & BUTTON_TERTIARY) != 0)
71 result |= MotionEventAndroid::BUTTON_TERTIARY;
75 int FromAndroidMetaState(int meta_state) {
76 int flags = ui::EF_NONE;
77 if ((meta_state & AMETA_SHIFT_ON) != 0)
78 flags |= ui::EF_SHIFT_DOWN;
79 if ((meta_state & AMETA_CTRL_ON) != 0)
80 flags |= ui::EF_CONTROL_DOWN;
81 if ((meta_state & AMETA_ALT_ON) != 0)
82 flags |= ui::EF_ALT_DOWN;
83 if ((meta_state & AMETA_META_ON) != 0)
84 flags |= ui::EF_COMMAND_DOWN;
85 if ((meta_state & AMETA_CAPS_LOCK_ON) != 0)
86 flags |= ui::EF_CAPS_LOCK_DOWN;
90 base::TimeTicks FromAndroidTime(int64 time_ms) {
91 return base::TimeTicks() + base::TimeDelta::FromMilliseconds(time_ms);
94 float ToValidFloat(float x) {
98 // Wildly large orientation values have been observed in the wild after device
99 // rotation. There's not much we can do in that case other than simply
100 // sanitize results beyond an absurd and arbitrary threshold.
101 if (std::abs(x) > 1e5f)
109 MotionEventAndroid::Pointer::Pointer(jint id,
112 jfloat touch_major_pixels,
113 jfloat touch_minor_pixels,
114 jfloat orientation_rad,
117 pos_x_pixels(pos_x_pixels),
118 pos_y_pixels(pos_y_pixels),
119 touch_major_pixels(touch_major_pixels),
120 touch_minor_pixels(touch_minor_pixels),
121 orientation_rad(orientation_rad),
122 tool_type(tool_type) {
125 MotionEventAndroid::CachedPointer::CachedPointer()
130 tool_type(TOOL_TYPE_UNKNOWN) {
133 MotionEventAndroid::MotionEventAndroid(float pix_to_dip,
141 jint android_button_state,
143 jfloat raw_offset_x_pixels,
144 jfloat raw_offset_y_pixels,
145 const Pointer& pointer0,
146 const Pointer& pointer1)
147 : pix_to_dip_(pix_to_dip),
148 cached_time_(FromAndroidTime(time_ms)),
149 cached_action_(FromAndroidAction(android_action)),
150 cached_pointer_count_(pointer_count),
151 cached_history_size_(history_size),
152 cached_action_index_(action_index),
153 cached_button_state_(FromAndroidButtonState(android_button_state)),
154 cached_flags_(FromAndroidMetaState(meta_state)),
155 cached_raw_position_offset_(ToDips(raw_offset_x_pixels),
156 ToDips(raw_offset_y_pixels)) {
157 DCHECK_GT(pointer_count, 0);
158 DCHECK_GE(history_size, 0);
160 event_.Reset(env, event);
161 if (cached_pointer_count_ > MAX_POINTERS_TO_CACHE || history_size > 0)
162 DCHECK(event_.obj());
164 cached_pointers_[0] = FromAndroidPointer(pointer0);
165 cached_pointers_[1] = FromAndroidPointer(pointer1);
168 MotionEventAndroid::~MotionEventAndroid() {
171 int MotionEventAndroid::GetId() const {
175 MotionEventAndroid::Action MotionEventAndroid::GetAction() const {
176 return cached_action_;
179 int MotionEventAndroid::GetActionIndex() const {
180 return cached_action_index_;
183 size_t MotionEventAndroid::GetPointerCount() const {
184 return cached_pointer_count_;
187 int MotionEventAndroid::GetPointerId(size_t pointer_index) const {
188 DCHECK_LT(pointer_index, cached_pointer_count_);
189 if (pointer_index < MAX_POINTERS_TO_CACHE)
190 return cached_pointers_[pointer_index].id;
191 return Java_MotionEvent_getPointerId(
192 AttachCurrentThread(), event_.obj(), pointer_index);
195 float MotionEventAndroid::GetX(size_t pointer_index) const {
196 DCHECK_LT(pointer_index, cached_pointer_count_);
197 if (pointer_index < MAX_POINTERS_TO_CACHE)
198 return cached_pointers_[pointer_index].position.x();
199 return ToDips(Java_MotionEvent_getXF_I(
200 AttachCurrentThread(), event_.obj(), pointer_index));
203 float MotionEventAndroid::GetY(size_t pointer_index) const {
204 DCHECK_LT(pointer_index, cached_pointer_count_);
205 if (pointer_index < MAX_POINTERS_TO_CACHE)
206 return cached_pointers_[pointer_index].position.y();
207 return ToDips(Java_MotionEvent_getYF_I(
208 AttachCurrentThread(), event_.obj(), pointer_index));
211 float MotionEventAndroid::GetRawX(size_t pointer_index) const {
212 return GetX(pointer_index) + cached_raw_position_offset_.x();
215 float MotionEventAndroid::GetRawY(size_t pointer_index) const {
216 return GetY(pointer_index) + cached_raw_position_offset_.y();
219 float MotionEventAndroid::GetTouchMajor(size_t pointer_index) const {
220 DCHECK_LT(pointer_index, cached_pointer_count_);
221 if (pointer_index < MAX_POINTERS_TO_CACHE)
222 return cached_pointers_[pointer_index].touch_major;
223 return ToDips(Java_MotionEvent_getTouchMajorF_I(
224 AttachCurrentThread(), event_.obj(), pointer_index));
227 float MotionEventAndroid::GetTouchMinor(size_t pointer_index) const {
228 DCHECK_LT(pointer_index, cached_pointer_count_);
229 if (pointer_index < MAX_POINTERS_TO_CACHE)
230 return cached_pointers_[pointer_index].touch_minor;
231 return ToDips(Java_MotionEvent_getTouchMinorF_I(
232 AttachCurrentThread(), event_.obj(), pointer_index));
235 float MotionEventAndroid::GetOrientation(size_t pointer_index) const {
236 DCHECK_LT(pointer_index, cached_pointer_count_);
237 if (pointer_index < MAX_POINTERS_TO_CACHE)
238 return cached_pointers_[pointer_index].orientation;
239 return ToValidFloat(Java_MotionEvent_getOrientationF_I(
240 AttachCurrentThread(), event_.obj(), pointer_index));
243 float MotionEventAndroid::GetPressure(size_t pointer_index) const {
244 DCHECK_LT(pointer_index, cached_pointer_count_);
245 // Note that this early return is a special case exercised only in testing, as
246 // caching the pressure values is not a worthwhile optimization (they're
247 // accessed at most once per event instance).
250 return Java_MotionEvent_getPressureF_I(
251 AttachCurrentThread(), event_.obj(), pointer_index);
254 base::TimeTicks MotionEventAndroid::GetEventTime() const {
258 size_t MotionEventAndroid::GetHistorySize() const {
259 return cached_history_size_;
262 base::TimeTicks MotionEventAndroid::GetHistoricalEventTime(
263 size_t historical_index) const {
264 return FromAndroidTime(Java_MotionEvent_getHistoricalEventTime(
265 AttachCurrentThread(), event_.obj(), historical_index));
268 float MotionEventAndroid::GetHistoricalTouchMajor(
269 size_t pointer_index,
270 size_t historical_index) const {
271 return ToDips(Java_MotionEvent_getHistoricalTouchMajorF_I_I(
272 AttachCurrentThread(), event_.obj(), pointer_index, historical_index));
275 float MotionEventAndroid::GetHistoricalX(size_t pointer_index,
276 size_t historical_index) const {
277 return ToDips(Java_MotionEvent_getHistoricalXF_I_I(
278 AttachCurrentThread(), event_.obj(), pointer_index, historical_index));
281 float MotionEventAndroid::GetHistoricalY(size_t pointer_index,
282 size_t historical_index) const {
283 return ToDips(Java_MotionEvent_getHistoricalYF_I_I(
284 AttachCurrentThread(), event_.obj(), pointer_index, historical_index));
287 ui::MotionEvent::ToolType MotionEventAndroid::GetToolType(
288 size_t pointer_index) const {
289 DCHECK_LT(pointer_index, cached_pointer_count_);
290 if (pointer_index < MAX_POINTERS_TO_CACHE)
291 return cached_pointers_[pointer_index].tool_type;
292 return FromAndroidToolType(Java_MotionEvent_getToolType(
293 AttachCurrentThread(), event_.obj(), pointer_index));
296 int MotionEventAndroid::GetButtonState() const {
297 return cached_button_state_;
300 int MotionEventAndroid::GetFlags() const {
301 return cached_flags_;
304 float MotionEventAndroid::ToDips(float pixels) const {
305 return pixels * pix_to_dip_;
308 MotionEventAndroid::CachedPointer MotionEventAndroid::FromAndroidPointer(
309 const Pointer& pointer) const {
310 CachedPointer result;
311 result.id = pointer.id;
313 gfx::PointF(ToDips(pointer.pos_x_pixels), ToDips(pointer.pos_y_pixels));
314 result.touch_major = ToDips(pointer.touch_major_pixels);
315 result.touch_minor = ToDips(pointer.touch_minor_pixels);
316 result.orientation = ToValidFloat(pointer.orientation_rad);
317 result.tool_type = FromAndroidToolType(pointer.tool_type);
322 bool MotionEventAndroid::RegisterMotionEventAndroid(JNIEnv* env) {
323 return JNI_MotionEvent::RegisterNativesImpl(env);
326 } // namespace content