| // Copyright 2013 The Chromium Authors |
| // 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/synthetic_gesture_target_android.h" |
| |
| #include "base/trace_event/trace_event.h" |
| #include "content/browser/renderer_host/compositor_impl_android.h" |
| #include "content/browser/renderer_host/render_widget_host_impl.h" |
| #include "content/browser/renderer_host/render_widget_host_view_android.h" |
| #include "content/browser/renderer_host/render_widget_host_view_base.h" |
| #include "third_party/blink/public/common/input/web_input_event.h" |
| #include "third_party/blink/public/common/input/web_mouse_event.h" |
| #include "third_party/blink/public/common/input/web_mouse_wheel_event.h" |
| #include "third_party/blink/public/common/input/web_touch_event.h" |
| #include "ui/android/view_android.h" |
| #include "ui/compositor/compositor.h" |
| #include "ui/gfx/android/view_configuration.h" |
| |
| // Must come after all headers that specialize FromJniType() / ToJniType(). |
| #include "content/public/android/content_jni_headers/SyntheticGestureTarget_jni.h" |
| |
| using base::android::JavaParamRef; |
| using base::android::ScopedJavaLocalRef; |
| using blink::WebGestureEvent; |
| using blink::WebInputEvent; |
| using blink::WebMouseEvent; |
| using blink::WebMouseWheelEvent; |
| using blink::WebTouchEvent; |
| |
| namespace content { |
| |
| SyntheticGestureTargetAndroid::SyntheticGestureTargetAndroid( |
| RenderWidgetHostImpl* host, |
| ui::ViewAndroid* view) |
| : SyntheticGestureTargetBase(host), view_(view) { |
| JNIEnv* env = base::android::AttachCurrentThread(); |
| java_ref_.Reset( |
| Java_SyntheticGestureTarget_create(env, view->GetContainerView())); |
| |
| ui::WindowAndroid* window = view_->GetWindowAndroid(); |
| if (!window) { |
| return; |
| } |
| |
| observed_compositor_ = static_cast<CompositorImpl*>(window->GetCompositor()); |
| if (observed_compositor_) { |
| observed_compositor_->AddSimpleBeginFrameObserver(this); |
| } |
| } |
| |
| SyntheticGestureTargetAndroid::~SyntheticGestureTargetAndroid() { |
| if (observed_compositor_) { |
| observed_compositor_->RemoveSimpleBeginFrameObserver(this); |
| observed_compositor_ = nullptr; |
| } |
| } |
| |
| void SyntheticGestureTargetAndroid::TouchSetPointer(int index, |
| float x, |
| float y, |
| int id) { |
| TRACE_EVENT0("input", "SyntheticGestureTargetAndroid::TouchSetPointer"); |
| JNIEnv* env = base::android::AttachCurrentThread(); |
| float scale_factor = view_->GetDipScale(); |
| Java_SyntheticGestureTarget_setPointer( |
| env, java_ref_, index, x * scale_factor, y * scale_factor, id); |
| } |
| |
| void SyntheticGestureTargetAndroid::TouchSetScrollDeltas(float x, |
| float y, |
| float dx, |
| float dy) { |
| TRACE_EVENT0("input", "SyntheticGestureTargetAndroid::TouchSetScrollDeltas"); |
| JNIEnv* env = base::android::AttachCurrentThread(); |
| |
| // Android motion events work by passing the number of wheel ticks and pixels |
| // per tick so the deltas should be passed in as number of ticks. |
| int wheel_ticks_multiplier = |
| render_widget_host()->GetView()->GetMouseWheelMinimumGranularity(); |
| if (wheel_ticks_multiplier) { |
| dx /= wheel_ticks_multiplier; |
| dy /= wheel_ticks_multiplier; |
| } |
| |
| Java_SyntheticGestureTarget_setScrollDeltas(env, java_ref_, x, y, dx, dy); |
| } |
| |
| void SyntheticGestureTargetAndroid::TouchInject(MotionEventAction action, |
| int pointer_count, |
| int pointer_index, |
| base::TimeTicks time) { |
| TRACE_EVENT0("input", "SyntheticGestureTargetAndroid::TouchInject"); |
| JNIEnv* env = base::android::AttachCurrentThread(); |
| Java_SyntheticGestureTarget_inject(env, java_ref_, action, pointer_count, |
| pointer_index, |
| time.since_origin().InMilliseconds()); |
| } |
| |
| void SyntheticGestureTargetAndroid::DispatchWebTouchEventToPlatform( |
| const WebTouchEvent& web_touch, |
| const ui::LatencyInfo&) { |
| MotionEventAction action = MOTION_EVENT_ACTION_INVALID; |
| switch (web_touch.GetType()) { |
| case WebInputEvent::Type::kTouchStart: |
| action = MOTION_EVENT_ACTION_START; |
| break; |
| case WebInputEvent::Type::kTouchMove: |
| action = MOTION_EVENT_ACTION_MOVE; |
| break; |
| case WebInputEvent::Type::kTouchCancel: |
| action = MOTION_EVENT_ACTION_CANCEL; |
| break; |
| case WebInputEvent::Type::kTouchEnd: |
| action = MOTION_EVENT_ACTION_END; |
| break; |
| default: |
| NOTREACHED(); |
| } |
| const unsigned num_touches = web_touch.touches_length; |
| int touch_index = -1; |
| DCHECK_LE(num_touches, 2u); |
| for (unsigned i = 0; i < num_touches; ++i) { |
| const blink::WebTouchPoint* point = &web_touch.touches[i]; |
| if (point->state != blink::WebTouchPoint::State::kStateStationary) { |
| if (action == MOTION_EVENT_ACTION_START || |
| action == MOTION_EVENT_ACTION_END) { |
| // We should have only one non-stationary touch for start/end. |
| DCHECK_EQ(touch_index, -1); |
| } |
| touch_index = i; |
| } |
| TouchSetPointer(i, point->PositionInWidget().x(), |
| point->PositionInWidget().y(), point->id); |
| } |
| DCHECK_GE(touch_index, 0); |
| |
| TouchInject(action, num_touches, touch_index, web_touch.TimeStamp()); |
| } |
| |
| void SyntheticGestureTargetAndroid::DispatchWebMouseWheelEventToPlatform( |
| const WebMouseWheelEvent& web_wheel, |
| const ui::LatencyInfo&) { |
| TouchSetScrollDeltas(web_wheel.PositionInWidget().x(), |
| web_wheel.PositionInWidget().y(), web_wheel.delta_x, |
| web_wheel.delta_y); |
| // We only have a single pointer. |
| const unsigned pointer_index = 0; |
| const unsigned num_pointers = 1; |
| TouchInject(MOTION_EVENT_ACTION_SCROLL, num_pointers, pointer_index, |
| web_wheel.TimeStamp()); |
| } |
| |
| void SyntheticGestureTargetAndroid::DispatchWebGestureEventToPlatform( |
| const WebGestureEvent& web_gesture, |
| const ui::LatencyInfo& latency_info) { |
| DCHECK_EQ(blink::WebGestureDevice::kTouchpad, web_gesture.SourceDevice()); |
| DCHECK(blink::WebInputEvent::IsPinchGestureEventType(web_gesture.GetType()) || |
| blink::WebInputEvent::IsFlingGestureEventType(web_gesture.GetType())); |
| GetView()->SendGestureEvent(web_gesture); |
| } |
| |
| void SyntheticGestureTargetAndroid::DispatchWebMouseEventToPlatform( |
| const WebMouseEvent& web_mouse, |
| const ui::LatencyInfo& info) { |
| GetView()->SendMouseEvent(web_mouse, info); |
| } |
| |
| content::mojom::GestureSourceType |
| SyntheticGestureTargetAndroid::GetDefaultSyntheticGestureSourceType() const { |
| return content::mojom::GestureSourceType::kTouchInput; |
| } |
| |
| void SyntheticGestureTargetAndroid::GetVSyncParameters( |
| base::TimeTicks& timebase, |
| base::TimeDelta& interval) const { |
| timebase = vsync_timebase_; |
| interval = vsync_interval_; |
| } |
| |
| void SyntheticGestureTargetAndroid::OnBeginFrame( |
| base::TimeTicks frame_begin_time, |
| base::TimeDelta frame_interval, |
| std::optional<base::TimeTicks> first_coalesced_frame_begin_time) { |
| vsync_timebase_ = frame_begin_time; |
| vsync_interval_ = frame_interval; |
| } |
| |
| void SyntheticGestureTargetAndroid::OnBeginFrameSourceShuttingDown() { |
| if (observed_compositor_) { |
| observed_compositor_->RemoveSimpleBeginFrameObserver(this); |
| observed_compositor_ = nullptr; |
| } |
| } |
| |
| float SyntheticGestureTargetAndroid::GetTouchSlopInDips() const { |
| // TODO(jdduke): Have all targets use the same ui::GestureConfiguration |
| // codepath. |
| return gfx::ViewConfiguration::GetTouchSlopInDips(); |
| } |
| |
| float SyntheticGestureTargetAndroid::GetMinScalingSpanInDips() const { |
| // TODO(jdduke): Have all targets use the same ui::GestureConfiguration |
| // codepath. |
| return gfx::ViewConfiguration::GetMinScalingSpanInDips(); |
| } |
| |
| RenderWidgetHostViewAndroid* SyntheticGestureTargetAndroid::GetView() const { |
| auto* view = static_cast<RenderWidgetHostViewAndroid*>( |
| render_widget_host()->GetView()); |
| DCHECK(view); |
| return view; |
| } |
| |
| } // namespace content |