OLD | NEW |
| (Empty) |
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 <cmath> | |
10 | |
11 #include "base/android/jni_android.h" | |
12 #include "jni/MotionEvent_jni.h" | |
13 #include "ui/events/base_event_utils.h" | |
14 #include "ui/events/event_constants.h" | |
15 | |
16 using base::android::AttachCurrentThread; | |
17 using namespace JNI_MotionEvent; | |
18 | |
19 namespace content { | |
20 namespace { | |
21 | |
22 MotionEventAndroid::Action FromAndroidAction(int android_action) { | |
23 switch (android_action) { | |
24 case ACTION_DOWN: | |
25 return MotionEventAndroid::ACTION_DOWN; | |
26 case ACTION_UP: | |
27 return MotionEventAndroid::ACTION_UP; | |
28 case ACTION_MOVE: | |
29 return MotionEventAndroid::ACTION_MOVE; | |
30 case ACTION_CANCEL: | |
31 return MotionEventAndroid::ACTION_CANCEL; | |
32 case ACTION_POINTER_DOWN: | |
33 return MotionEventAndroid::ACTION_POINTER_DOWN; | |
34 case ACTION_POINTER_UP: | |
35 return MotionEventAndroid::ACTION_POINTER_UP; | |
36 default: | |
37 NOTREACHED() << "Invalid Android MotionEvent type for gesture detection: " | |
38 << android_action; | |
39 }; | |
40 return MotionEventAndroid::ACTION_CANCEL; | |
41 } | |
42 | |
43 MotionEventAndroid::ToolType FromAndroidToolType(int android_tool_type) { | |
44 switch (android_tool_type) { | |
45 case TOOL_TYPE_UNKNOWN: | |
46 return MotionEventAndroid::TOOL_TYPE_UNKNOWN; | |
47 case TOOL_TYPE_FINGER: | |
48 return MotionEventAndroid::TOOL_TYPE_FINGER; | |
49 case TOOL_TYPE_STYLUS: | |
50 return MotionEventAndroid::TOOL_TYPE_STYLUS; | |
51 case TOOL_TYPE_MOUSE: | |
52 return MotionEventAndroid::TOOL_TYPE_MOUSE; | |
53 case TOOL_TYPE_ERASER: | |
54 return MotionEventAndroid::TOOL_TYPE_ERASER; | |
55 default: | |
56 NOTREACHED() << "Invalid Android MotionEvent tool type: " | |
57 << android_tool_type; | |
58 }; | |
59 return MotionEventAndroid::TOOL_TYPE_UNKNOWN; | |
60 } | |
61 | |
62 int FromAndroidButtonState(int button_state) { | |
63 int result = 0; | |
64 if ((button_state & BUTTON_BACK) != 0) | |
65 result |= MotionEventAndroid::BUTTON_BACK; | |
66 if ((button_state & BUTTON_FORWARD) != 0) | |
67 result |= MotionEventAndroid::BUTTON_FORWARD; | |
68 if ((button_state & BUTTON_PRIMARY) != 0) | |
69 result |= MotionEventAndroid::BUTTON_PRIMARY; | |
70 if ((button_state & BUTTON_SECONDARY) != 0) | |
71 result |= MotionEventAndroid::BUTTON_SECONDARY; | |
72 if ((button_state & BUTTON_TERTIARY) != 0) | |
73 result |= MotionEventAndroid::BUTTON_TERTIARY; | |
74 if ((button_state & BUTTON_STYLUS_PRIMARY) != 0) | |
75 result |= MotionEventAndroid::BUTTON_STYLUS_PRIMARY; | |
76 if ((button_state & BUTTON_STYLUS_SECONDARY) != 0) | |
77 result |= MotionEventAndroid::BUTTON_STYLUS_SECONDARY; | |
78 return result; | |
79 } | |
80 | |
81 int FromAndroidMetaState(int meta_state) { | |
82 int flags = ui::EF_NONE; | |
83 if ((meta_state & AMETA_SHIFT_ON) != 0) | |
84 flags |= ui::EF_SHIFT_DOWN; | |
85 if ((meta_state & AMETA_CTRL_ON) != 0) | |
86 flags |= ui::EF_CONTROL_DOWN; | |
87 if ((meta_state & AMETA_ALT_ON) != 0) | |
88 flags |= ui::EF_ALT_DOWN; | |
89 if ((meta_state & AMETA_META_ON) != 0) | |
90 flags |= ui::EF_COMMAND_DOWN; | |
91 if ((meta_state & AMETA_CAPS_LOCK_ON) != 0) | |
92 flags |= ui::EF_CAPS_LOCK_DOWN; | |
93 return flags; | |
94 } | |
95 | |
96 base::TimeTicks FromAndroidTime(int64 time_ms) { | |
97 return base::TimeTicks() + base::TimeDelta::FromMilliseconds(time_ms); | |
98 } | |
99 | |
100 float ToValidFloat(float x) { | |
101 if (std::isnan(x)) | |
102 return 0.f; | |
103 | |
104 // Wildly large orientation values have been observed in the wild after device | |
105 // rotation. There's not much we can do in that case other than simply | |
106 // sanitize results beyond an absurd and arbitrary threshold. | |
107 if (std::abs(x) > 1e5f) | |
108 return 0.f; | |
109 | |
110 return x; | |
111 } | |
112 | |
113 size_t ToValidHistorySize(jint history_size, ui::MotionEvent::Action action) { | |
114 DCHECK_GE(history_size, 0); | |
115 // While the spec states that only ACTION_MOVE events should contain | |
116 // historical entries, it's possible that an embedder could repurpose an | |
117 // ACTION_MOVE event into a different kind of event. In that case, the | |
118 // historical values are meaningless, and should not be exposed. | |
119 if (action != ui::MotionEvent::ACTION_MOVE) | |
120 return 0; | |
121 return history_size; | |
122 } | |
123 | |
124 } // namespace | |
125 | |
126 MotionEventAndroid::Pointer::Pointer(jint id, | |
127 jfloat pos_x_pixels, | |
128 jfloat pos_y_pixels, | |
129 jfloat touch_major_pixels, | |
130 jfloat touch_minor_pixels, | |
131 jfloat orientation_rad, | |
132 jint tool_type) | |
133 : id(id), | |
134 pos_x_pixels(pos_x_pixels), | |
135 pos_y_pixels(pos_y_pixels), | |
136 touch_major_pixels(touch_major_pixels), | |
137 touch_minor_pixels(touch_minor_pixels), | |
138 orientation_rad(orientation_rad), | |
139 tool_type(tool_type) { | |
140 } | |
141 | |
142 MotionEventAndroid::CachedPointer::CachedPointer() | |
143 : id(0), | |
144 touch_major(0), | |
145 touch_minor(0), | |
146 orientation(0), | |
147 tool_type(TOOL_TYPE_UNKNOWN) { | |
148 } | |
149 | |
150 MotionEventAndroid::MotionEventAndroid(float pix_to_dip, | |
151 JNIEnv* env, | |
152 jobject event, | |
153 jlong time_ms, | |
154 jint android_action, | |
155 jint pointer_count, | |
156 jint history_size, | |
157 jint action_index, | |
158 jint android_button_state, | |
159 jint meta_state, | |
160 jfloat raw_offset_x_pixels, | |
161 jfloat raw_offset_y_pixels, | |
162 const Pointer& pointer0, | |
163 const Pointer& pointer1) | |
164 : pix_to_dip_(pix_to_dip), | |
165 cached_time_(FromAndroidTime(time_ms)), | |
166 cached_action_(FromAndroidAction(android_action)), | |
167 cached_pointer_count_(pointer_count), | |
168 cached_history_size_(ToValidHistorySize(history_size, cached_action_)), | |
169 cached_action_index_(action_index), | |
170 cached_button_state_(FromAndroidButtonState(android_button_state)), | |
171 cached_flags_(FromAndroidMetaState(meta_state)), | |
172 cached_raw_position_offset_(ToDips(raw_offset_x_pixels), | |
173 ToDips(raw_offset_y_pixels)), | |
174 unique_event_id_(ui::GetNextTouchEventId()) { | |
175 DCHECK_GT(pointer_count, 0); | |
176 | |
177 event_.Reset(env, event); | |
178 if (cached_pointer_count_ > MAX_POINTERS_TO_CACHE || cached_history_size_ > 0) | |
179 DCHECK(event_.obj()); | |
180 | |
181 cached_pointers_[0] = FromAndroidPointer(pointer0); | |
182 cached_pointers_[1] = FromAndroidPointer(pointer1); | |
183 } | |
184 | |
185 MotionEventAndroid::~MotionEventAndroid() { | |
186 } | |
187 | |
188 uint32 MotionEventAndroid::GetUniqueEventId() const { | |
189 return unique_event_id_; | |
190 } | |
191 | |
192 MotionEventAndroid::Action MotionEventAndroid::GetAction() const { | |
193 return cached_action_; | |
194 } | |
195 | |
196 int MotionEventAndroid::GetActionIndex() const { | |
197 DCHECK(cached_action_ == ACTION_POINTER_UP || | |
198 cached_action_ == ACTION_POINTER_DOWN) | |
199 << "Invalid action for GetActionIndex(): " << cached_action_; | |
200 DCHECK_GE(cached_action_index_, 0); | |
201 DCHECK_LT(cached_action_index_, static_cast<int>(cached_pointer_count_)); | |
202 return cached_action_index_; | |
203 } | |
204 | |
205 size_t MotionEventAndroid::GetPointerCount() const { | |
206 return cached_pointer_count_; | |
207 } | |
208 | |
209 int MotionEventAndroid::GetPointerId(size_t pointer_index) const { | |
210 DCHECK_LT(pointer_index, cached_pointer_count_); | |
211 if (pointer_index < MAX_POINTERS_TO_CACHE) | |
212 return cached_pointers_[pointer_index].id; | |
213 return Java_MotionEvent_getPointerId( | |
214 AttachCurrentThread(), event_.obj(), pointer_index); | |
215 } | |
216 | |
217 float MotionEventAndroid::GetX(size_t pointer_index) const { | |
218 DCHECK_LT(pointer_index, cached_pointer_count_); | |
219 if (pointer_index < MAX_POINTERS_TO_CACHE) | |
220 return cached_pointers_[pointer_index].position.x(); | |
221 return ToDips(Java_MotionEvent_getXF_I( | |
222 AttachCurrentThread(), event_.obj(), pointer_index)); | |
223 } | |
224 | |
225 float MotionEventAndroid::GetY(size_t pointer_index) const { | |
226 DCHECK_LT(pointer_index, cached_pointer_count_); | |
227 if (pointer_index < MAX_POINTERS_TO_CACHE) | |
228 return cached_pointers_[pointer_index].position.y(); | |
229 return ToDips(Java_MotionEvent_getYF_I( | |
230 AttachCurrentThread(), event_.obj(), pointer_index)); | |
231 } | |
232 | |
233 float MotionEventAndroid::GetRawX(size_t pointer_index) const { | |
234 return GetX(pointer_index) + cached_raw_position_offset_.x(); | |
235 } | |
236 | |
237 float MotionEventAndroid::GetRawY(size_t pointer_index) const { | |
238 return GetY(pointer_index) + cached_raw_position_offset_.y(); | |
239 } | |
240 | |
241 float MotionEventAndroid::GetTouchMajor(size_t pointer_index) const { | |
242 DCHECK_LT(pointer_index, cached_pointer_count_); | |
243 if (pointer_index < MAX_POINTERS_TO_CACHE) | |
244 return cached_pointers_[pointer_index].touch_major; | |
245 return ToDips(Java_MotionEvent_getTouchMajorF_I( | |
246 AttachCurrentThread(), event_.obj(), pointer_index)); | |
247 } | |
248 | |
249 float MotionEventAndroid::GetTouchMinor(size_t pointer_index) const { | |
250 DCHECK_LT(pointer_index, cached_pointer_count_); | |
251 if (pointer_index < MAX_POINTERS_TO_CACHE) | |
252 return cached_pointers_[pointer_index].touch_minor; | |
253 return ToDips(Java_MotionEvent_getTouchMinorF_I( | |
254 AttachCurrentThread(), event_.obj(), pointer_index)); | |
255 } | |
256 | |
257 float MotionEventAndroid::GetOrientation(size_t pointer_index) const { | |
258 DCHECK_LT(pointer_index, cached_pointer_count_); | |
259 if (pointer_index < MAX_POINTERS_TO_CACHE) | |
260 return cached_pointers_[pointer_index].orientation; | |
261 return ToValidFloat(Java_MotionEvent_getOrientationF_I( | |
262 AttachCurrentThread(), event_.obj(), pointer_index)); | |
263 } | |
264 | |
265 float MotionEventAndroid::GetPressure(size_t pointer_index) const { | |
266 DCHECK_LT(pointer_index, cached_pointer_count_); | |
267 // Note that this early return is a special case exercised only in testing, as | |
268 // caching the pressure values is not a worthwhile optimization (they're | |
269 // accessed at most once per event instance). | |
270 if (!event_.obj()) | |
271 return 0.f; | |
272 return Java_MotionEvent_getPressureF_I( | |
273 AttachCurrentThread(), event_.obj(), pointer_index); | |
274 } | |
275 | |
276 base::TimeTicks MotionEventAndroid::GetEventTime() const { | |
277 return cached_time_; | |
278 } | |
279 | |
280 size_t MotionEventAndroid::GetHistorySize() const { | |
281 return cached_history_size_; | |
282 } | |
283 | |
284 base::TimeTicks MotionEventAndroid::GetHistoricalEventTime( | |
285 size_t historical_index) const { | |
286 return FromAndroidTime(Java_MotionEvent_getHistoricalEventTime( | |
287 AttachCurrentThread(), event_.obj(), historical_index)); | |
288 } | |
289 | |
290 float MotionEventAndroid::GetHistoricalTouchMajor( | |
291 size_t pointer_index, | |
292 size_t historical_index) const { | |
293 return ToDips(Java_MotionEvent_getHistoricalTouchMajorF_I_I( | |
294 AttachCurrentThread(), event_.obj(), pointer_index, historical_index)); | |
295 } | |
296 | |
297 float MotionEventAndroid::GetHistoricalX(size_t pointer_index, | |
298 size_t historical_index) const { | |
299 return ToDips(Java_MotionEvent_getHistoricalXF_I_I( | |
300 AttachCurrentThread(), event_.obj(), pointer_index, historical_index)); | |
301 } | |
302 | |
303 float MotionEventAndroid::GetHistoricalY(size_t pointer_index, | |
304 size_t historical_index) const { | |
305 return ToDips(Java_MotionEvent_getHistoricalYF_I_I( | |
306 AttachCurrentThread(), event_.obj(), pointer_index, historical_index)); | |
307 } | |
308 | |
309 ui::MotionEvent::ToolType MotionEventAndroid::GetToolType( | |
310 size_t pointer_index) const { | |
311 DCHECK_LT(pointer_index, cached_pointer_count_); | |
312 if (pointer_index < MAX_POINTERS_TO_CACHE) | |
313 return cached_pointers_[pointer_index].tool_type; | |
314 return FromAndroidToolType(Java_MotionEvent_getToolType( | |
315 AttachCurrentThread(), event_.obj(), pointer_index)); | |
316 } | |
317 | |
318 int MotionEventAndroid::GetButtonState() const { | |
319 return cached_button_state_; | |
320 } | |
321 | |
322 int MotionEventAndroid::GetFlags() const { | |
323 return cached_flags_; | |
324 } | |
325 | |
326 float MotionEventAndroid::ToDips(float pixels) const { | |
327 return pixels * pix_to_dip_; | |
328 } | |
329 | |
330 MotionEventAndroid::CachedPointer MotionEventAndroid::FromAndroidPointer( | |
331 const Pointer& pointer) const { | |
332 CachedPointer result; | |
333 result.id = pointer.id; | |
334 result.position = | |
335 gfx::PointF(ToDips(pointer.pos_x_pixels), ToDips(pointer.pos_y_pixels)); | |
336 result.touch_major = ToDips(pointer.touch_major_pixels); | |
337 result.touch_minor = ToDips(pointer.touch_minor_pixels); | |
338 result.orientation = ToValidFloat(pointer.orientation_rad); | |
339 result.tool_type = FromAndroidToolType(pointer.tool_type); | |
340 return result; | |
341 } | |
342 | |
343 // static | |
344 bool MotionEventAndroid::RegisterMotionEventAndroid(JNIEnv* env) { | |
345 return JNI_MotionEvent::RegisterNativesImpl(env); | |
346 } | |
347 | |
348 } // namespace content | |
OLD | NEW |