diff options
author | dominikg@chromium.org <dominikg@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-01-07 15:50:24 +0000 |
---|---|---|
committer | dominikg@chromium.org <dominikg@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-01-07 15:50:24 +0000 |
commit | c8e704e7838993207d532256f64b78d74ccb7119 (patch) | |
tree | 82d90aa4d2bb6f2537eddd346ccbb32701d70bad /content | |
parent | 85c9edcf9b6fc0df5ee859ca7085c6c3f3aa582c (diff) | |
download | chromium_src-c8e704e7838993207d532256f64b78d74ccb7119.zip chromium_src-c8e704e7838993207d532256f64b78d74ccb7119.tar.gz chromium_src-c8e704e7838993207d532256f64b78d74ccb7119.tar.bz2 |
Add timestamps to synthesized events.
With this CL, synthetic gestures add timestamps to the events they generate.
This allows us to have timestamps that are not necessarily aligned to frame
boundaries. The timestamps are converted to native timestamps by the synthetic
gesture targets.
The synthetic gesture controller passes timestamps instead of intervals to the
gestures.
BUG=297980
Review URL: https://codereview.chromium.org/119323007
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@243299 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'content')
14 files changed, 268 insertions, 205 deletions
diff --git a/content/browser/renderer_host/input/synthetic_gesture.cc b/content/browser/renderer_host/input/synthetic_gesture.cc index 8b2b3be..bc887e1 100644 --- a/content/browser/renderer_host/input/synthetic_gesture.cc +++ b/content/browser/renderer_host/input/synthetic_gesture.cc @@ -43,4 +43,10 @@ scoped_ptr<SyntheticGesture> SyntheticGesture::Create( return scoped_ptr<SyntheticGesture>(); } +// static +double SyntheticGesture::ConvertTimestampToSeconds( + const base::TimeTicks& timestamp) { + return (timestamp - base::TimeTicks()).InSecondsF(); +} + } // namespace content diff --git a/content/browser/renderer_host/input/synthetic_gesture.h b/content/browser/renderer_host/input/synthetic_gesture.h index c10c2fe..9452451 100644 --- a/content/browser/renderer_host/input/synthetic_gesture.h +++ b/content/browser/renderer_host/input/synthetic_gesture.h @@ -46,9 +46,11 @@ class CONTENT_EXPORT SyntheticGesture { // platform. This function is called repeatedly by the synthetic gesture // controller until it stops returning GESTURE_RUNNING. virtual Result ForwardInputEvents( - const base::TimeDelta& interval, SyntheticGestureTarget* target) = 0; + const base::TimeTicks& timestamp, SyntheticGestureTarget* target) = 0; + + protected: + static double ConvertTimestampToSeconds(const base::TimeTicks& timestamp); - private: DISALLOW_COPY_AND_ASSIGN(SyntheticGesture); }; diff --git a/content/browser/renderer_host/input/synthetic_gesture_controller.cc b/content/browser/renderer_host/input/synthetic_gesture_controller.cc index 0529354..fe39987 100644 --- a/content/browser/renderer_host/input/synthetic_gesture_controller.cc +++ b/content/browser/renderer_host/input/synthetic_gesture_controller.cc @@ -33,16 +33,8 @@ void SyntheticGestureController::Flush(base::TimeTicks timestamp) { if (pending_gesture_queue_.empty()) return; - if (last_tick_time_.is_null()) { - last_tick_time_ = timestamp; - gesture_target_->SetNeedsFlush(); - return; - } - - base::TimeDelta interval = timestamp - last_tick_time_; - last_tick_time_ = timestamp; SyntheticGesture::Result result = - pending_gesture_queue_.front()->ForwardInputEvents(interval, + pending_gesture_queue_.front()->ForwardInputEvents(timestamp, gesture_target_.get()); if (result == SyntheticGesture::GESTURE_RUNNING) { @@ -53,13 +45,8 @@ void SyntheticGestureController::Flush(base::TimeTicks timestamp) { StopGesture(*pending_gesture_queue_.front(), result); pending_gesture_queue_.erase(pending_gesture_queue_.begin()); - if (!pending_gesture_queue_.empty()) { + if (!pending_gesture_queue_.empty()) StartGesture(*pending_gesture_queue_.front()); - } else { - // Reset last_tick_time_ so that we don't use an old value when a new - // gestures is queued. - last_tick_time_ = base::TimeTicks(); - } } void SyntheticGestureController::StartGesture(const SyntheticGesture& gesture) { diff --git a/content/browser/renderer_host/input/synthetic_gesture_controller.h b/content/browser/renderer_host/input/synthetic_gesture_controller.h index ed9cec7..837b28f 100644 --- a/content/browser/renderer_host/input/synthetic_gesture_controller.h +++ b/content/browser/renderer_host/input/synthetic_gesture_controller.h @@ -39,8 +39,6 @@ class CONTENT_EXPORT SyntheticGestureController { scoped_ptr<SyntheticGestureTarget> gesture_target_; ScopedVector<SyntheticGesture> pending_gesture_queue_; - base::TimeTicks last_tick_time_; - DISALLOW_COPY_AND_ASSIGN(SyntheticGestureController); }; diff --git a/content/browser/renderer_host/input/synthetic_gesture_controller_unittest.cc b/content/browser/renderer_host/input/synthetic_gesture_controller_unittest.cc index f449e57..7f8321b 100644 --- a/content/browser/renderer_host/input/synthetic_gesture_controller_unittest.cc +++ b/content/browser/renderer_host/input/synthetic_gesture_controller_unittest.cc @@ -43,7 +43,7 @@ class MockSyntheticGesture : public SyntheticGesture { } virtual ~MockSyntheticGesture() {} - virtual Result ForwardInputEvents(const base::TimeDelta& interval, + virtual Result ForwardInputEvents(const base::TimeTicks& timestamp, SyntheticGestureTarget* target) OVERRIDE { step_count_++; if (step_count_ == num_steps_) { @@ -271,6 +271,7 @@ class MockSyntheticTapGestureTarget : public MockSyntheticGestureTarget { bool GestureFinished() const { return state_ == FINISHED; } gfx::Point position() const { return position_; } + base::TimeDelta GetDuration() const { return stop_time_ - start_time_; } protected: enum GestureState { @@ -280,6 +281,8 @@ class MockSyntheticTapGestureTarget : public MockSyntheticGestureTarget { }; gfx::Point position_; + base::TimeDelta start_time_; + base::TimeDelta stop_time_; GestureState state_; }; @@ -299,11 +302,15 @@ class MockSyntheticTapTouchTarget : public MockSyntheticTapGestureTarget { case NOT_STARTED: EXPECT_EQ(touch_event->type, blink::WebInputEvent::TouchStart); position_ = gfx::Point(touch_event->touches[0].position); + start_time_ = base::TimeDelta::FromMilliseconds( + static_cast<int64>(touch_event->timeStampSeconds * 1000)); state_ = STARTED; break; case STARTED: EXPECT_EQ(touch_event->type, blink::WebInputEvent::TouchEnd); EXPECT_EQ(position_, gfx::Point(touch_event->touches[0].position)); + stop_time_ = base::TimeDelta::FromMilliseconds( + static_cast<int64>(touch_event->timeStampSeconds * 1000)); state_ = FINISHED; break; case FINISHED: @@ -330,6 +337,8 @@ class MockSyntheticTapMouseTarget : public MockSyntheticTapGestureTarget { EXPECT_EQ(mouse_event->button, blink::WebMouseEvent::ButtonLeft); EXPECT_EQ(mouse_event->clickCount, 1); position_ = gfx::Point(mouse_event->x, mouse_event->y); + start_time_ = base::TimeDelta::FromMilliseconds( + static_cast<int64>(mouse_event->timeStampSeconds * 1000)); state_ = STARTED; break; case STARTED: @@ -337,6 +346,8 @@ class MockSyntheticTapMouseTarget : public MockSyntheticTapGestureTarget { EXPECT_EQ(mouse_event->button, blink::WebMouseEvent::ButtonLeft); EXPECT_EQ(mouse_event->clickCount, 1); EXPECT_EQ(position_, gfx::Point(mouse_event->x, mouse_event->y)); + stop_time_ = base::TimeDelta::FromMilliseconds( + static_cast<int64>(mouse_event->timeStampSeconds * 1000)); state_ = FINISHED; break; case FINISHED: @@ -783,6 +794,7 @@ TEST_F(SyntheticGestureControllerTest, TapGestureTouch) { EXPECT_EQ(0, target_->num_failure()); EXPECT_TRUE(tap_target->GestureFinished()); EXPECT_EQ(tap_target->position(), params.position); + EXPECT_EQ(tap_target->GetDuration().InMilliseconds(), params.duration_ms); EXPECT_GE(GetTotalTime(), base::TimeDelta::FromMilliseconds(params.duration_ms)); } @@ -805,6 +817,7 @@ TEST_F(SyntheticGestureControllerTest, TapGestureMouse) { EXPECT_EQ(0, target_->num_failure()); EXPECT_TRUE(tap_target->GestureFinished()); EXPECT_EQ(tap_target->position(), params.position); + EXPECT_EQ(tap_target->GetDuration().InMilliseconds(), params.duration_ms); EXPECT_GE(GetTotalTime(), base::TimeDelta::FromMilliseconds(params.duration_ms)); } diff --git a/content/browser/renderer_host/input/synthetic_gesture_target_android.cc b/content/browser/renderer_host/input/synthetic_gesture_target_android.cc index cf5181a..6139f67 100644 --- a/content/browser/renderer_host/input/synthetic_gesture_target_android.cc +++ b/content/browser/renderer_host/input/synthetic_gesture_target_android.cc @@ -37,9 +37,10 @@ void SyntheticGestureTargetAndroid::TouchSetPointer( } void SyntheticGestureTargetAndroid::TouchInject( - JNIEnv* env, Action action, int pointer_count) { + JNIEnv* env, Action action, int pointer_count, long time_in_ms) { Java_TouchEventSynthesizer_inject(env, touch_event_synthesizer_.obj(), - static_cast<int>(action), pointer_count); + static_cast<int>(action), pointer_count, + time_in_ms); } void SyntheticGestureTargetAndroid::DispatchWebTouchEventToPlatform( @@ -70,7 +71,8 @@ void SyntheticGestureTargetAndroid::DispatchWebTouchEventToPlatform( TouchSetPointer(env, i, point->position.x, point->position.y, point->id); } - TouchInject(env, action, num_touches); + TouchInject(env, action, num_touches, + static_cast<long>(web_touch.timeStampSeconds * 1000.0)); } SyntheticGestureParams::GestureSourceType diff --git a/content/browser/renderer_host/input/synthetic_gesture_target_android.h b/content/browser/renderer_host/input/synthetic_gesture_target_android.h index b8c5a5d..14cbe4a 100644 --- a/content/browser/renderer_host/input/synthetic_gesture_target_android.h +++ b/content/browser/renderer_host/input/synthetic_gesture_target_android.h @@ -46,7 +46,8 @@ class SyntheticGestureTargetAndroid : public SyntheticGestureTargetBase { }; void TouchSetPointer(JNIEnv* env, int index, int x, int y, int id); - void TouchInject(JNIEnv* env, Action action, int pointer_count); + void TouchInject( + JNIEnv* env, Action action, int pointer_count, long time_in_ms); base::android::ScopedJavaGlobalRef<jobject> touch_event_synthesizer_; diff --git a/content/browser/renderer_host/input/synthetic_pinch_gesture.cc b/content/browser/renderer_host/input/synthetic_pinch_gesture.cc index dfe5cf4..d49024b09 100644 --- a/content/browser/renderer_host/input/synthetic_pinch_gesture.cc +++ b/content/browser/renderer_host/input/synthetic_pinch_gesture.cc @@ -15,10 +15,8 @@ namespace content { SyntheticPinchGesture::SyntheticPinchGesture( const SyntheticPinchGestureParams& params) : params_(params), - current_y_0_(0.0f), - current_y_1_(0.0f), - target_y_0_(0.0f), - target_y_1_(0.0f), + start_y_0_(0.0f), + start_y_1_(0.0f), gesture_source_type_(SyntheticGestureParams::DEFAULT_INPUT), state_(SETUP) { DCHECK_GE(params_.total_num_pixels_covered, 0); @@ -27,7 +25,7 @@ SyntheticPinchGesture::SyntheticPinchGesture( SyntheticPinchGesture::~SyntheticPinchGesture() {} SyntheticGesture::Result SyntheticPinchGesture::ForwardInputEvents( - const base::TimeDelta& interval, SyntheticGestureTarget* target) { + const base::TimeTicks& timestamp, SyntheticGestureTarget* target) { if (state_ == SETUP) { gesture_source_type_ = params_.gesture_source_type; if (gesture_source_type_ == SyntheticGestureParams::DEFAULT_INPUT) @@ -37,11 +35,12 @@ SyntheticGesture::Result SyntheticPinchGesture::ForwardInputEvents( return SyntheticGesture::GESTURE_SOURCE_TYPE_NOT_SUPPORTED_BY_PLATFORM; state_ = STARTED; + start_time_ = timestamp; } DCHECK_NE(gesture_source_type_, SyntheticGestureParams::DEFAULT_INPUT); if (gesture_source_type_ == SyntheticGestureParams::TOUCH_INPUT) - ForwardTouchInputEvents(interval, target); + ForwardTouchInputEvents(timestamp, target); else return SyntheticGesture::GESTURE_SOURCE_TYPE_NOT_IMPLEMENTED; @@ -50,7 +49,7 @@ SyntheticGesture::Result SyntheticPinchGesture::ForwardInputEvents( } void SyntheticPinchGesture::ForwardTouchInputEvents( - const base::TimeDelta& interval, SyntheticGestureTarget* target) { + const base::TimeTicks& timestamp, SyntheticGestureTarget* target) { switch (state_) { case STARTED: // Check for an early finish. @@ -58,18 +57,19 @@ void SyntheticPinchGesture::ForwardTouchInputEvents( state_ = DONE; break; } - SetupCoordinates(target); - PressTouchPoints(target); + SetupCoordinatesAndStopTime(target); + PressTouchPoints(target, timestamp); state_ = MOVING; break; - case MOVING: - UpdateTouchPoints(interval); - MoveTouchPoints(target); - if (HasReachedTarget()) { - ReleaseTouchPoints(target); + case MOVING: { + base::TimeTicks event_timestamp = ClampTimestamp(timestamp); + float delta = GetDeltaForPointer0AtTime(event_timestamp); + MoveTouchPoints(target, delta, event_timestamp); + if (HasReachedTarget(event_timestamp)) { + ReleaseTouchPoints(target, event_timestamp); state_ = DONE; } - break; + } break; case SETUP: NOTREACHED() << "State SETUP invalid for synthetic pinch."; case DONE: @@ -77,90 +77,94 @@ void SyntheticPinchGesture::ForwardTouchInputEvents( } } -void SyntheticPinchGesture::UpdateTouchPoints(base::TimeDelta interval) { - // Compute the delta for the first pointer. The other one moves exactly - // the same but in the opposite direction. - float delta = GetDeltaForPointer0(interval); - current_y_0_ += delta; - current_y_1_ -= delta; +void SyntheticPinchGesture::PressTouchPoints(SyntheticGestureTarget* target, + const base::TimeTicks& timestamp) { + touch_event_.PressPoint(params_.anchor.x(), start_y_0_); + touch_event_.PressPoint(params_.anchor.x(), start_y_1_); + ForwardTouchEvent(target, timestamp); } -void SyntheticPinchGesture::PressTouchPoints(SyntheticGestureTarget* target) { - touch_event_.PressPoint(params_.anchor.x(), current_y_0_); - touch_event_.PressPoint(params_.anchor.x(), current_y_1_); - ForwardTouchEvent(target); -} +void SyntheticPinchGesture::MoveTouchPoints(SyntheticGestureTarget* target, + float delta, + const base::TimeTicks& timestamp) { + // The two pointers move in opposite directions. + float current_y_0 = start_y_0_ + delta; + float current_y_1 = start_y_1_ - delta; -void SyntheticPinchGesture::MoveTouchPoints(SyntheticGestureTarget* target) { // The current pointer positions are stored as float but the pointer // coordinates of the input event are integers. Floor both positions so that // in case of an odd distance one of the pointers (the one whose position goes // down) moves one pixel further than the other. The explicit flooring is only // needed for negative values. - touch_event_.MovePoint(0, params_.anchor.x(), floor(current_y_0_)); - touch_event_.MovePoint(1, params_.anchor.x(), floor(current_y_1_)); - ForwardTouchEvent(target); + touch_event_.MovePoint(0, params_.anchor.x(), floor(current_y_0)); + touch_event_.MovePoint(1, params_.anchor.x(), floor(current_y_1)); + ForwardTouchEvent(target, timestamp); } -void SyntheticPinchGesture::ReleaseTouchPoints(SyntheticGestureTarget* target) { +void SyntheticPinchGesture::ReleaseTouchPoints( + SyntheticGestureTarget* target, const base::TimeTicks& timestamp) { touch_event_.ReleasePoint(0); touch_event_.ReleasePoint(1); - ForwardTouchEvent(target); + ForwardTouchEvent(target, timestamp); } - -void SyntheticPinchGesture::ForwardTouchEvent(SyntheticGestureTarget* target) - const { +void SyntheticPinchGesture::ForwardTouchEvent( + SyntheticGestureTarget* target, const base::TimeTicks& timestamp) { + touch_event_.timeStampSeconds = ConvertTimestampToSeconds(timestamp); target->DispatchInputEventToPlatform( InputEvent(touch_event_, ui::LatencyInfo(), false)); } -void SyntheticPinchGesture::SetupCoordinates(SyntheticGestureTarget* target) { - const float kTouchSlopInDips = target->GetTouchSlopInDips(); +void SyntheticPinchGesture::SetupCoordinatesAndStopTime( + SyntheticGestureTarget* target) { + const int kTouchSlopInDips = target->GetTouchSlopInDips(); + params_.total_num_pixels_covered += 2 * kTouchSlopInDips; float inner_distance_to_anchor = 2 * kTouchSlopInDips; - float outer_distance_to_anchor = inner_distance_to_anchor + - params_.total_num_pixels_covered / 2.0f + - kTouchSlopInDips; + float outer_distance_to_anchor = + inner_distance_to_anchor + params_.total_num_pixels_covered / 2.0f; // Move pointers away from each other to zoom in // or towards each other to zoom out. if (params_.zoom_in) { - current_y_0_ = params_.anchor.y() - inner_distance_to_anchor; - current_y_1_ = params_.anchor.y() + inner_distance_to_anchor; - target_y_0_ = params_.anchor.y() - outer_distance_to_anchor; - target_y_1_ = params_.anchor.y() + outer_distance_to_anchor; + start_y_0_ = params_.anchor.y() - inner_distance_to_anchor; + start_y_1_ = params_.anchor.y() + inner_distance_to_anchor; } else { - current_y_0_ = params_.anchor.y() - outer_distance_to_anchor; - current_y_1_ = params_.anchor.y() + outer_distance_to_anchor; - target_y_0_ = params_.anchor.y() - inner_distance_to_anchor; - target_y_1_ = params_.anchor.y() + inner_distance_to_anchor; + start_y_0_ = params_.anchor.y() - outer_distance_to_anchor; + start_y_1_ = params_.anchor.y() + outer_distance_to_anchor; } + + int64 total_duration_in_us = static_cast<int64>( + 1e6 * (static_cast<double>(params_.total_num_pixels_covered) / + params_.relative_pointer_speed_in_pixels_s)); + DCHECK_GT(total_duration_in_us, 0); + stop_time_ = + start_time_ + base::TimeDelta::FromMicroseconds(total_duration_in_us); } -float SyntheticPinchGesture::GetDeltaForPointer0( - const base::TimeDelta& interval) const { - float total_abs_delta = - params_.relative_pointer_speed_in_pixels_s * interval.InSecondsF(); +float SyntheticPinchGesture::GetDeltaForPointer0AtTime( + const base::TimeTicks& timestamp) const { + float total_abs_delta; - // Make sure we're not moving too far in the final step. - total_abs_delta = - std::min(total_abs_delta, ComputeAbsoluteRemainingDistance()); + // Make sure the final delta is correct. Using the computation below can lead + // to issues with floating point precision. + if (HasReachedTarget(timestamp)) + total_abs_delta = params_.total_num_pixels_covered; + else + total_abs_delta = params_.relative_pointer_speed_in_pixels_s * + (timestamp - start_time_).InSecondsF(); - float abs_delta_pointer_0 = total_abs_delta / 2; + float abs_delta_pointer_0 = total_abs_delta / 2.0f; return params_.zoom_in ? -abs_delta_pointer_0 : abs_delta_pointer_0; } -float SyntheticPinchGesture::ComputeAbsoluteRemainingDistance() const { - float distance_0 = params_.zoom_in ? (current_y_0_ - target_y_0_) - : (target_y_0_ - current_y_0_); - DCHECK_GE(distance_0, 0); - - // Both pointers move the same overall distance at the same speed. - return 2 * distance_0; +base::TimeTicks SyntheticPinchGesture::ClampTimestamp( + const base::TimeTicks& timestamp) const { + return std::min(timestamp, stop_time_); } -bool SyntheticPinchGesture::HasReachedTarget() const { - return ComputeAbsoluteRemainingDistance() == 0; +bool SyntheticPinchGesture::HasReachedTarget(const base::TimeTicks& timestamp) + const { + return timestamp >= stop_time_; } } // namespace content diff --git a/content/browser/renderer_host/input/synthetic_pinch_gesture.h b/content/browser/renderer_host/input/synthetic_pinch_gesture.h index 97405ce..77ef011 100644 --- a/content/browser/renderer_host/input/synthetic_pinch_gesture.h +++ b/content/browser/renderer_host/input/synthetic_pinch_gesture.h @@ -5,6 +5,7 @@ #ifndef CONTENT_BROWSER_RENDERER_HOST_INPUT_SYNTHETIC_PINCH_GESTURE_H_ #define CONTENT_BROWSER_RENDERER_HOST_INPUT_SYNTHETIC_PINCH_GESTURE_H_ +#include "base/time/time.h" #include "content/browser/renderer_host/input/synthetic_gesture.h" #include "content/browser/renderer_host/input/synthetic_gesture_target.h" #include "content/common/content_export.h" @@ -20,7 +21,8 @@ class CONTENT_EXPORT SyntheticPinchGesture : public SyntheticGesture { virtual ~SyntheticPinchGesture(); virtual SyntheticGesture::Result ForwardInputEvents( - const base::TimeDelta& interval, SyntheticGestureTarget* target) OVERRIDE; + const base::TimeTicks& timestamp, + SyntheticGestureTarget* target) OVERRIDE; private: enum GestureState { @@ -30,29 +32,34 @@ class CONTENT_EXPORT SyntheticPinchGesture : public SyntheticGesture { DONE }; - void ForwardTouchInputEvents( - const base::TimeDelta& interval, SyntheticGestureTarget* target); + void ForwardTouchInputEvents(const base::TimeTicks& timestamp, + SyntheticGestureTarget* target); - void UpdateTouchPoints(base::TimeDelta interval); - void PressTouchPoints(SyntheticGestureTarget* target); - void MoveTouchPoints(SyntheticGestureTarget* target); - void ReleaseTouchPoints(SyntheticGestureTarget* target); - void ForwardTouchEvent(SyntheticGestureTarget* target) const; + void UpdateTouchPoints(const base::TimeTicks& timestamp); + void PressTouchPoints(SyntheticGestureTarget* target, + const base::TimeTicks& timestamp); + void MoveTouchPoints(SyntheticGestureTarget* target, float delta, + const base::TimeTicks& timestamp); + void ReleaseTouchPoints(SyntheticGestureTarget* target, + const base::TimeTicks& timestamp); + void ForwardTouchEvent(SyntheticGestureTarget* target, + const base::TimeTicks& timestamp); - void SetupCoordinates(SyntheticGestureTarget* target); - float GetDeltaForPointer0(const base::TimeDelta& interval) const; - float ComputeAbsoluteRemainingDistance() const; - bool HasReachedTarget() const; + void SetupCoordinatesAndStopTime(SyntheticGestureTarget* target); + float GetDeltaForPointer0AtTime(const base::TimeTicks& timestamp) const; + base::TimeTicks ClampTimestamp(const base::TimeTicks& timestamp) const; + bool HasReachedTarget(const base::TimeTicks& timestamp) const; SyntheticPinchGestureParams params_; - float current_y_0_; - float current_y_1_; - float target_y_0_; - float target_y_1_; + float start_y_0_; + float start_y_1_; SyntheticGestureParams::GestureSourceType gesture_source_type_; GestureState state_; SyntheticWebTouchEvent touch_event_; + base::TimeTicks start_time_; + base::TimeTicks stop_time_; + private: DISALLOW_COPY_AND_ASSIGN(SyntheticPinchGesture); }; diff --git a/content/browser/renderer_host/input/synthetic_smooth_scroll_gesture.cc b/content/browser/renderer_host/input/synthetic_smooth_scroll_gesture.cc index b2fb4bb..f70ba9b 100644 --- a/content/browser/renderer_host/input/synthetic_smooth_scroll_gesture.cc +++ b/content/browser/renderer_host/input/synthetic_smooth_scroll_gesture.cc @@ -35,7 +35,7 @@ SyntheticSmoothScrollGesture::SyntheticSmoothScrollGesture( SyntheticSmoothScrollGesture::~SyntheticSmoothScrollGesture() {} SyntheticGesture::Result SyntheticSmoothScrollGesture::ForwardInputEvents( - const base::TimeDelta& interval, SyntheticGestureTarget* target) { + const base::TimeTicks& timestamp, SyntheticGestureTarget* target) { if (state_ == SETUP) { gesture_source_type_ = params_.gesture_source_type; if (gesture_source_type_ == SyntheticGestureParams::DEFAULT_INPUT) @@ -45,13 +45,14 @@ SyntheticGesture::Result SyntheticSmoothScrollGesture::ForwardInputEvents( return SyntheticGesture::GESTURE_SOURCE_TYPE_NOT_SUPPORTED_BY_PLATFORM; state_ = STARTED; + start_time_ = timestamp; } DCHECK_NE(gesture_source_type_, SyntheticGestureParams::DEFAULT_INPUT); if (gesture_source_type_ == SyntheticGestureParams::TOUCH_INPUT) - ForwardTouchInputEvents(interval, target); + ForwardTouchInputEvents(timestamp, target); else if (gesture_source_type_ == SyntheticGestureParams::MOUSE_INPUT) - ForwardMouseInputEvents(interval, target); + ForwardMouseInputEvents(timestamp, target); else return SyntheticGesture::GESTURE_SOURCE_TYPE_NOT_IMPLEMENTED; @@ -60,7 +61,8 @@ SyntheticGesture::Result SyntheticSmoothScrollGesture::ForwardInputEvents( } void SyntheticSmoothScrollGesture::ForwardTouchInputEvents( - const base::TimeDelta& interval, SyntheticGestureTarget* target) { + const base::TimeTicks& timestamp, SyntheticGestureTarget* target) { + base::TimeTicks event_timestamp = timestamp; switch (state_) { case STARTED: // Check for an early finish. @@ -69,29 +71,33 @@ void SyntheticSmoothScrollGesture::ForwardTouchInputEvents( break; } AddTouchSlopToDistance(target); - PressTouchPoint(target); + ComputeAndSetStopScrollingTime(); + PressTouchPoint(target, event_timestamp); state_ = MOVING; break; - case MOVING: - total_delta_ += GetPositionDelta(interval); - MoveTouchPoint(target); + case MOVING: { + event_timestamp = ClampTimestamp(timestamp); + gfx::Vector2dF delta = GetPositionDeltaAtTime(event_timestamp); + MoveTouchPoint(target, delta, event_timestamp); - if (HasScrolledEntireDistance()) { + if (HasScrolledEntireDistance(event_timestamp)) { if (params_.prevent_fling) { state_ = STOPPING; } else { - ReleaseTouchPoint(target); + ReleaseTouchPoint(target, event_timestamp); state_ = DONE; } } - break; + } break; case STOPPING: - total_stopping_wait_time_ += interval; - if (total_stopping_wait_time_ >= target->PointerAssumedStoppedTime()) { + if (timestamp - stop_scrolling_time_ >= + target->PointerAssumedStoppedTime()) { + event_timestamp = + stop_scrolling_time_ + target->PointerAssumedStoppedTime(); // Send one last move event, but don't change the location. Without this // we'd still sometimes cause a fling on Android. - MoveTouchPoint(target); - ReleaseTouchPoint(target); + ForwardTouchEvent(target, event_timestamp); + ReleaseTouchPoint(target, event_timestamp); state_ = DONE; } break; @@ -105,7 +111,7 @@ void SyntheticSmoothScrollGesture::ForwardTouchInputEvents( } void SyntheticSmoothScrollGesture::ForwardMouseInputEvents( - const base::TimeDelta& interval, SyntheticGestureTarget* target) { + const base::TimeTicks& timestamp, SyntheticGestureTarget* target) { switch (state_) { case STARTED: // Check for an early finish. @@ -113,24 +119,25 @@ void SyntheticSmoothScrollGesture::ForwardMouseInputEvents( state_ = DONE; break; } + ComputeAndSetStopScrollingTime(); state_ = MOVING; // Fall through to forward the first event. - case MOVING: - { - // Even though WebMouseWheelEvents take floating point deltas, - // internally the scroll position is stored as an integer. We therefore - // keep track of the discrete delta which is consistent with the - // internal scrolling state. This ensures that when the gesture has - // finished we've scrolled exactly the specified distance. - total_delta_ += GetPositionDelta(interval); - gfx::Vector2d delta_discrete = - FloorTowardZero(total_delta_ - total_delta_discrete_); - ForwardMouseWheelEvent(target, delta_discrete); - total_delta_discrete_ += delta_discrete; - } - if (HasScrolledEntireDistance()) + case MOVING: { + // Even though WebMouseWheelEvents take floating point deltas, + // internally the scroll position is stored as an integer. We therefore + // keep track of the discrete delta which is consistent with the + // internal scrolling state. This ensures that when the gesture has + // finished we've scrolled exactly the specified distance. + base::TimeTicks event_timestamp = ClampTimestamp(timestamp); + gfx::Vector2dF total_delta = GetPositionDeltaAtTime(event_timestamp); + gfx::Vector2d delta_discrete = + FloorTowardZero(total_delta - total_delta_discrete_); + ForwardMouseWheelEvent(target, delta_discrete, event_timestamp); + total_delta_discrete_ += delta_discrete; + + if (HasScrolledEntireDistance(event_timestamp)) state_ = DONE; - break; + } break; case SETUP: NOTREACHED() << "State STARTED invalid for synthetic scroll using touch input."; @@ -140,44 +147,52 @@ void SyntheticSmoothScrollGesture::ForwardMouseInputEvents( case DONE: NOTREACHED() << "State DONE invalid for synthetic scroll using touch input."; - } + } } void SyntheticSmoothScrollGesture::ForwardTouchEvent( - SyntheticGestureTarget* target) const { + SyntheticGestureTarget* target, const base::TimeTicks& timestamp) { + touch_event_.timeStampSeconds = ConvertTimestampToSeconds(timestamp); + target->DispatchInputEventToPlatform( InputEvent(touch_event_, ui::LatencyInfo(), false)); } void SyntheticSmoothScrollGesture::ForwardMouseWheelEvent( - SyntheticGestureTarget* target, const gfx::Vector2dF& delta) const { + SyntheticGestureTarget* target, + const gfx::Vector2dF& delta, + const base::TimeTicks& timestamp) const { blink::WebMouseWheelEvent mouse_wheel_event = SyntheticWebMouseWheelEventBuilder::Build(delta.x(), delta.y(), 0, false); mouse_wheel_event.x = params_.anchor.x(); mouse_wheel_event.y = params_.anchor.y(); + mouse_wheel_event.timeStampSeconds = ConvertTimestampToSeconds(timestamp); + target->DispatchInputEventToPlatform( InputEvent(mouse_wheel_event, ui::LatencyInfo(), false)); } void SyntheticSmoothScrollGesture::PressTouchPoint( - SyntheticGestureTarget* target) { + SyntheticGestureTarget* target, const base::TimeTicks& timestamp) { touch_event_.PressPoint(params_.anchor.x(), params_.anchor.y()); - ForwardTouchEvent(target); + ForwardTouchEvent(target, timestamp); } void SyntheticSmoothScrollGesture::MoveTouchPoint( - SyntheticGestureTarget* target) { - gfx::PointF touch_position = params_.anchor + total_delta_; + SyntheticGestureTarget* target, + const gfx::Vector2dF& delta, + const base::TimeTicks& timestamp) { + gfx::PointF touch_position = params_.anchor + delta; touch_event_.MovePoint(0, touch_position.x(), touch_position.y()); - ForwardTouchEvent(target); + ForwardTouchEvent(target, timestamp); } void SyntheticSmoothScrollGesture::ReleaseTouchPoint( - SyntheticGestureTarget* target) { + SyntheticGestureTarget* target, const base::TimeTicks& timestamp) { touch_event_.ReleasePoint(0); - ForwardTouchEvent(target); + ForwardTouchEvent(target, timestamp); } void SyntheticSmoothScrollGesture::AddTouchSlopToDistance( @@ -187,23 +202,21 @@ void SyntheticSmoothScrollGesture::AddTouchSlopToDistance( // distance and round up to the nearest integer. // For vertical and horizontal scrolls (the common case), both methods produce // the same result. - gfx::Vector2dF touch_slop_delta = ProjectLengthOntoScrollDirection( - target->GetTouchSlopInDips()); + gfx::Vector2dF touch_slop_delta = + ProjectLengthOntoScrollDirection(target->GetTouchSlopInDips()); params_.distance += CeilFromZero(touch_slop_delta); } -gfx::Vector2dF SyntheticSmoothScrollGesture::GetPositionDelta( - const base::TimeDelta& interval) const { - float delta_length = params_.speed_in_pixels_s * interval.InSecondsF(); +gfx::Vector2dF SyntheticSmoothScrollGesture::GetPositionDeltaAtTime( + const base::TimeTicks& timestamp) const { + // Make sure the final delta is correct. Using the computation below can lead + // to issues with floating point precision. + if (HasScrolledEntireDistance(timestamp)) + return -params_.distance; - // Make sure we're not scrolling too far. - gfx::Vector2dF remaining_delta = ComputeRemainingDelta(); - if (delta_length > remaining_delta.Length()) - // In order to scroll in a certain direction we need to move the - // touch pointer/mouse wheel in the opposite direction. - return -remaining_delta; - else - return -ProjectLengthOntoScrollDirection(delta_length); + float delta_length = + params_.speed_in_pixels_s * (timestamp - start_time_).InSecondsF(); + return -ProjectLengthOntoScrollDirection(delta_length); } gfx::Vector2dF SyntheticSmoothScrollGesture::ProjectLengthOntoScrollDirection( @@ -212,12 +225,22 @@ gfx::Vector2dF SyntheticSmoothScrollGesture::ProjectLengthOntoScrollDirection( return ScaleVector2d(params_.distance, delta_length / kTotalLength); } -gfx::Vector2dF SyntheticSmoothScrollGesture::ComputeRemainingDelta() const { - return params_.distance + total_delta_; +void SyntheticSmoothScrollGesture::ComputeAndSetStopScrollingTime() { + int64 total_duration_in_us = static_cast<int64>( + 1e6 * (params_.distance.Length() / params_.speed_in_pixels_s)); + DCHECK_GT(total_duration_in_us, 0); + stop_scrolling_time_ = + start_time_ + base::TimeDelta::FromMicroseconds(total_duration_in_us); +} + +base::TimeTicks SyntheticSmoothScrollGesture::ClampTimestamp( + const base::TimeTicks& timestamp) const { + return std::min(timestamp, stop_scrolling_time_); } -bool SyntheticSmoothScrollGesture::HasScrolledEntireDistance() const { - return ComputeRemainingDelta().IsZero(); +bool SyntheticSmoothScrollGesture::HasScrolledEntireDistance( + const base::TimeTicks& timestamp) const { + return timestamp >= stop_scrolling_time_; } } // namespace content diff --git a/content/browser/renderer_host/input/synthetic_smooth_scroll_gesture.h b/content/browser/renderer_host/input/synthetic_smooth_scroll_gesture.h index 53b4da5..b598a18 100644 --- a/content/browser/renderer_host/input/synthetic_smooth_scroll_gesture.h +++ b/content/browser/renderer_host/input/synthetic_smooth_scroll_gesture.h @@ -24,7 +24,8 @@ class CONTENT_EXPORT SyntheticSmoothScrollGesture : public SyntheticGesture { virtual ~SyntheticSmoothScrollGesture(); virtual SyntheticGesture::Result ForwardInputEvents( - const base::TimeDelta& interval, SyntheticGestureTarget* target) OVERRIDE; + const base::TimeTicks& timestamp, + SyntheticGestureTarget* target) OVERRIDE; private: enum GestureState { @@ -36,31 +37,39 @@ class CONTENT_EXPORT SyntheticSmoothScrollGesture : public SyntheticGesture { }; void ForwardTouchInputEvents( - const base::TimeDelta& interval, SyntheticGestureTarget* target); + const base::TimeTicks& timestamp, SyntheticGestureTarget* target); void ForwardMouseInputEvents( - const base::TimeDelta& interval, SyntheticGestureTarget* target); + const base::TimeTicks& timestamp, SyntheticGestureTarget* target); - void ForwardTouchEvent(SyntheticGestureTarget* target) const; + void ForwardTouchEvent(SyntheticGestureTarget* target, + const base::TimeTicks& timestamp); void ForwardMouseWheelEvent(SyntheticGestureTarget* target, - const gfx::Vector2dF& delta) const; + const gfx::Vector2dF& delta, + const base::TimeTicks& timestamp) const; - void PressTouchPoint(SyntheticGestureTarget* target); - void MoveTouchPoint(SyntheticGestureTarget* target); - void ReleaseTouchPoint(SyntheticGestureTarget* target); + void PressTouchPoint(SyntheticGestureTarget* target, + const base::TimeTicks& timestamp); + void MoveTouchPoint(SyntheticGestureTarget* target, + const gfx::Vector2dF& delta, + const base::TimeTicks& timestamp); + void ReleaseTouchPoint(SyntheticGestureTarget* target, + const base::TimeTicks& timestamp); void AddTouchSlopToDistance(SyntheticGestureTarget* target); - gfx::Vector2dF GetPositionDelta(const base::TimeDelta& interval) const; + gfx::Vector2dF GetPositionDeltaAtTime(const base::TimeTicks& timestamp) + const; gfx::Vector2dF ProjectLengthOntoScrollDirection(float delta_length) const; - gfx::Vector2dF ComputeRemainingDelta() const; - bool HasScrolledEntireDistance() const; + void ComputeAndSetStopScrollingTime(); + base::TimeTicks ClampTimestamp(const base::TimeTicks& timestamp) const; + bool HasScrolledEntireDistance(const base::TimeTicks& timestamp) const; SyntheticSmoothScrollGestureParams params_; - gfx::Vector2dF total_delta_; gfx::Vector2d total_delta_discrete_; SyntheticWebTouchEvent touch_event_; SyntheticGestureParams::GestureSourceType gesture_source_type_; GestureState state_; - base::TimeDelta total_stopping_wait_time_; + base::TimeTicks start_time_; + base::TimeTicks stop_scrolling_time_; DISALLOW_COPY_AND_ASSIGN(SyntheticSmoothScrollGesture); }; diff --git a/content/browser/renderer_host/input/synthetic_tap_gesture.cc b/content/browser/renderer_host/input/synthetic_tap_gesture.cc index 1f15d82..ea7e984 100644 --- a/content/browser/renderer_host/input/synthetic_tap_gesture.cc +++ b/content/browser/renderer_host/input/synthetic_tap_gesture.cc @@ -31,7 +31,7 @@ SyntheticTapGesture::SyntheticTapGesture( SyntheticTapGesture::~SyntheticTapGesture() {} SyntheticGesture::Result SyntheticTapGesture::ForwardInputEvents( - const base::TimeDelta& interval, SyntheticGestureTarget* target) { + const base::TimeTicks& timestamp, SyntheticGestureTarget* target) { if (state_ == SETUP) { gesture_source_type_ = params_.gesture_source_type; if (gesture_source_type_ == SyntheticGestureParams::DEFAULT_INPUT) @@ -46,7 +46,7 @@ SyntheticGesture::Result SyntheticTapGesture::ForwardInputEvents( DCHECK_NE(gesture_source_type_, SyntheticGestureParams::DEFAULT_INPUT); if (gesture_source_type_ == SyntheticGestureParams::TOUCH_INPUT || gesture_source_type_ == SyntheticGestureParams::MOUSE_INPUT) - ForwardTouchOrMouseInputEvents(interval, target); + ForwardTouchOrMouseInputEvents(timestamp, target); else return SyntheticGesture::GESTURE_SOURCE_TYPE_NOT_IMPLEMENTED; @@ -55,23 +55,22 @@ SyntheticGesture::Result SyntheticTapGesture::ForwardInputEvents( } void SyntheticTapGesture::ForwardTouchOrMouseInputEvents( - const base::TimeDelta& interval, SyntheticGestureTarget* target) { + const base::TimeTicks& timestamp, SyntheticGestureTarget* target) { switch (state_) { case PRESS: - Press(target); + Press(target, timestamp); // Release immediately if duration is 0. if (params_.duration_ms == 0) { - Release(target); + Release(target, timestamp); state_ = DONE; } else { + start_time_ = timestamp; state_ = WAITING_TO_RELEASE; } break; case WAITING_TO_RELEASE: - total_waiting_time_ += interval; - if (total_waiting_time_ >= - base::TimeDelta::FromMilliseconds(params_.duration_ms)) { - Release(target); + if (timestamp - start_time_ >= GetDuration()) { + Release(target, start_time_ + GetDuration()); state_ = DONE; } break; @@ -82,9 +81,11 @@ void SyntheticTapGesture::ForwardTouchOrMouseInputEvents( } } -void SyntheticTapGesture::Press(SyntheticGestureTarget* target) { +void SyntheticTapGesture::Press(SyntheticGestureTarget* target, + const base::TimeTicks& timestamp) { if (gesture_source_type_ == SyntheticGestureParams::TOUCH_INPUT) { touch_event_.PressPoint(params_.position.x(), params_.position.y()); + touch_event_.timeStampSeconds = ConvertTimestampToSeconds(timestamp); DispatchEventToPlatform(target, touch_event_); } else if (gesture_source_type_ == SyntheticGestureParams::MOUSE_INPUT) { blink::WebMouseEvent mouse_event = @@ -93,15 +94,18 @@ void SyntheticTapGesture::Press(SyntheticGestureTarget* target) { params_.position.y(), 0); mouse_event.clickCount = 1; + mouse_event.timeStampSeconds = ConvertTimestampToSeconds(timestamp); DispatchEventToPlatform(target, mouse_event); } else { NOTREACHED() << "Invalid gesture source type for synthetic tap gesture."; } } -void SyntheticTapGesture::Release(SyntheticGestureTarget* target) { +void SyntheticTapGesture::Release(SyntheticGestureTarget* target, + const base::TimeTicks& timestamp) { if (gesture_source_type_ == SyntheticGestureParams::TOUCH_INPUT) { touch_event_.ReleasePoint(0); + touch_event_.timeStampSeconds = ConvertTimestampToSeconds(timestamp); DispatchEventToPlatform(target, touch_event_); } else if (gesture_source_type_ == SyntheticGestureParams::MOUSE_INPUT) { blink::WebMouseEvent mouse_event = @@ -110,10 +114,15 @@ void SyntheticTapGesture::Release(SyntheticGestureTarget* target) { params_.position.y(), 0); mouse_event.clickCount = 1; + mouse_event.timeStampSeconds = ConvertTimestampToSeconds(timestamp); DispatchEventToPlatform(target, mouse_event); } else { NOTREACHED() << "Invalid gesture source type for synthetic tap gesture."; } } +base::TimeDelta SyntheticTapGesture::GetDuration() const { + return base::TimeDelta::FromMilliseconds(params_.duration_ms); +} + } // namespace content diff --git a/content/browser/renderer_host/input/synthetic_tap_gesture.h b/content/browser/renderer_host/input/synthetic_tap_gesture.h index 22217f9..e67d198 100644 --- a/content/browser/renderer_host/input/synthetic_tap_gesture.h +++ b/content/browser/renderer_host/input/synthetic_tap_gesture.h @@ -19,7 +19,8 @@ class CONTENT_EXPORT SyntheticTapGesture : public SyntheticGesture { virtual ~SyntheticTapGesture(); virtual SyntheticGesture::Result ForwardInputEvents( - const base::TimeDelta& interval, SyntheticGestureTarget* target) OVERRIDE; + const base::TimeTicks& timestamp, + SyntheticGestureTarget* target) OVERRIDE; private: enum GestureState { @@ -29,14 +30,17 @@ class CONTENT_EXPORT SyntheticTapGesture : public SyntheticGesture { DONE }; - void ForwardTouchOrMouseInputEvents(const base::TimeDelta& interval, + void ForwardTouchOrMouseInputEvents(const base::TimeTicks& timestamp, SyntheticGestureTarget* target); - void Press(SyntheticGestureTarget* target); - void Release(SyntheticGestureTarget* target); + void Press(SyntheticGestureTarget* target, const base::TimeTicks& timestamp); + void Release(SyntheticGestureTarget* target, + const base::TimeTicks& timestamp); + + base::TimeDelta GetDuration() const; SyntheticTapGestureParams params_; - base::TimeDelta total_waiting_time_; + base::TimeTicks start_time_; SyntheticWebTouchEvent touch_event_; SyntheticGestureParams::GestureSourceType gesture_source_type_; GestureState state_; diff --git a/content/public/android/java/src/org/chromium/content/browser/TouchEventSynthesizer.java b/content/public/android/java/src/org/chromium/content/browser/TouchEventSynthesizer.java index 2dd88b9..0183821 100644 --- a/content/public/android/java/src/org/chromium/content/browser/TouchEventSynthesizer.java +++ b/content/public/android/java/src/org/chromium/content/browser/TouchEventSynthesizer.java @@ -4,7 +4,6 @@ package org.chromium.content.browser; -import android.os.SystemClock; import android.view.MotionEvent; import android.view.MotionEvent.PointerCoords; import android.view.MotionEvent.PointerProperties; @@ -27,7 +26,7 @@ public class TouchEventSynthesizer { private final ContentViewCore mContentViewCore; private final PointerProperties[] mPointerProperties; private final PointerCoords[] mPointerCoords; - private long mDownTime; + private long mDownTimeInMs; TouchEventSynthesizer(ContentViewCore contentViewCore) { mContentViewCore = contentViewCore; @@ -54,14 +53,12 @@ public class TouchEventSynthesizer { } @CalledByNative - void inject(int action, int pointerCount) { - long time = SystemClock.uptimeMillis(); - + void inject(int action, int pointerCount, long timeInMs) { switch (action) { case ACTION_START: { - mDownTime = time; + mDownTimeInMs = timeInMs; MotionEvent event = MotionEvent.obtain( - mDownTime, time, MotionEvent.ACTION_DOWN, 1, + mDownTimeInMs, timeInMs, MotionEvent.ACTION_DOWN, 1, mPointerProperties, mPointerCoords, 0, 0, 1, 1, 0, 0, 0, 0); mContentViewCore.onTouchEvent(event); @@ -69,8 +66,9 @@ public class TouchEventSynthesizer { if (pointerCount > 1) { event = MotionEvent.obtain( - mDownTime, time, MotionEvent.ACTION_POINTER_DOWN, - pointerCount, mPointerProperties, mPointerCoords, + mDownTimeInMs, timeInMs, + MotionEvent.ACTION_POINTER_DOWN, pointerCount, + mPointerProperties, mPointerCoords, 0, 0, 1, 1, 0, 0, 0, 0); mContentViewCore.onTouchEvent(event); event.recycle(); @@ -78,7 +76,7 @@ public class TouchEventSynthesizer { break; } case ACTION_MOVE: { - MotionEvent event = MotionEvent.obtain(mDownTime, time, + MotionEvent event = MotionEvent.obtain(mDownTimeInMs, timeInMs, MotionEvent.ACTION_MOVE, pointerCount, mPointerProperties, mPointerCoords, 0, 0, 1, 1, 0, 0, 0, 0); @@ -88,7 +86,7 @@ public class TouchEventSynthesizer { } case ACTION_CANCEL: { MotionEvent event = MotionEvent.obtain( - mDownTime, time, MotionEvent.ACTION_CANCEL, 1, + mDownTimeInMs, timeInMs, MotionEvent.ACTION_CANCEL, 1, mPointerProperties, mPointerCoords, 0, 0, 1, 1, 0, 0, 0, 0); mContentViewCore.onTouchEvent(event); @@ -98,7 +96,7 @@ public class TouchEventSynthesizer { case ACTION_END: { if (pointerCount > 1) { MotionEvent event = MotionEvent.obtain( - mDownTime, time, MotionEvent.ACTION_POINTER_UP, + mDownTimeInMs, timeInMs, MotionEvent.ACTION_POINTER_UP, pointerCount, mPointerProperties, mPointerCoords, 0, 0, 1, 1, 0, 0, 0, 0); mContentViewCore.onTouchEvent(event); @@ -106,7 +104,7 @@ public class TouchEventSynthesizer { } MotionEvent event = MotionEvent.obtain( - mDownTime, time, MotionEvent.ACTION_UP, 1, + mDownTimeInMs, timeInMs, MotionEvent.ACTION_UP, 1, mPointerProperties, mPointerCoords, 0, 0, 1, 1, 0, 0, 0, 0); mContentViewCore.onTouchEvent(event); |