| // Copyright (c) 2012 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. |
| |
| package org.chromium.content.browser; |
| |
| import android.animation.TimeAnimator; |
| import android.animation.TimeAnimator.TimeListener; |
| import android.os.Build; |
| import android.os.Handler; |
| import android.os.Looper; |
| import android.os.SystemClock; |
| import android.util.Log; |
| import android.view.MotionEvent; |
| |
| import org.chromium.base.CalledByNative; |
| import org.chromium.base.JNINamespace; |
| import org.chromium.base.ThreadUtils; |
| |
| /** |
| * Provides a Java-side implementation for chrome.gpuBenchmarking.smoothScrollBy. |
| */ |
| @JNINamespace("content") |
| public class SmoothScroller { |
| private final ContentViewCore mContentViewCore; |
| private final boolean mScrollDown; |
| private final float mMouseEventX; |
| private final float mMouseEventY; |
| |
| private final Handler mHandler = new Handler(ThreadUtils.getUiThreadLooper()); |
| |
| private TimeAnimator mTimeAnimator; |
| |
| private int mNativePtr; |
| private long mDownTime; |
| private float mCurrentY; |
| |
| private final byte STATE_INITIAL = 0; |
| private final byte STATE_MOVING = 1; |
| private final byte STATE_PENDING_UP = 2; |
| private final byte STATE_FINAL = 3; |
| private byte state = STATE_INITIAL; |
| |
| SmoothScroller(ContentViewCore contentViewCore, boolean scrollDown, |
| int mouseEventX, int mouseEventY) { |
| mContentViewCore = contentViewCore; |
| mScrollDown = scrollDown; |
| float scale = mContentViewCore.getRenderCoordinates().getDeviceScaleFactor(); |
| mMouseEventX = mouseEventX * scale; |
| mMouseEventY = mouseEventY * scale; |
| mCurrentY = mMouseEventY; |
| } |
| |
| @CalledByNative |
| void start(int nativePtr) { |
| assert mNativePtr == 0; |
| mNativePtr = nativePtr; |
| |
| Runnable runnable = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) ? |
| createJBRunnable() : createPreJBRunnable(); |
| mHandler.post(runnable); |
| } |
| |
| boolean sendEvent(long time) { |
| switch (state) { |
| case STATE_INITIAL: { |
| mDownTime = SystemClock.uptimeMillis(); |
| MotionEvent event = MotionEvent.obtain(mDownTime, mDownTime, |
| MotionEvent.ACTION_DOWN, mMouseEventX, mCurrentY, 0); |
| mContentViewCore.onTouchEvent(event); |
| event.recycle(); |
| state = STATE_MOVING; |
| break; |
| } |
| case STATE_MOVING: { |
| double delta = nativeGetScrollDelta( |
| mNativePtr, mContentViewCore.getRenderCoordinates().getDeviceScaleFactor()); |
| if (delta != 0) { |
| mCurrentY += mScrollDown ? -delta : delta; |
| |
| MotionEvent event = MotionEvent.obtain(mDownTime, time, |
| MotionEvent.ACTION_MOVE, mMouseEventX, mCurrentY, 0); |
| mContentViewCore.onTouchEvent(event); |
| event.recycle(); |
| } |
| if (nativeHasFinished(mNativePtr)) |
| state = STATE_PENDING_UP; |
| break; |
| } |
| case STATE_PENDING_UP: { |
| MotionEvent event = MotionEvent.obtain(mDownTime, time, |
| MotionEvent.ACTION_UP, mMouseEventX, mCurrentY, 0); |
| mContentViewCore.onTouchEvent(event); |
| event.recycle(); |
| nativeSetHasSentMotionUp(mNativePtr); |
| state = STATE_FINAL; |
| break; |
| } |
| } |
| return state != STATE_FINAL; |
| } |
| |
| private Runnable createJBRunnable() { |
| // On JB, we rely on TimeAnimator to send events tied with vsync. |
| return new Runnable() { |
| @Override |
| public void run() { |
| mTimeAnimator = new TimeAnimator(); |
| mTimeAnimator.setTimeListener(new TimeListener() { |
| @Override |
| public void onTimeUpdate(TimeAnimator animation, long totalTime, |
| long deltaTime) { |
| if (!sendEvent(mDownTime + totalTime)) { |
| mTimeAnimator.end(); |
| } |
| } |
| }); |
| mTimeAnimator.start(); |
| } |
| }; |
| } |
| |
| private Runnable createPreJBRunnable() { |
| // Pre-JB there's no TimeAnimator, so we keep posting messages. |
| return new Runnable() { |
| @Override |
| public void run() { |
| if (sendEvent(SystemClock.uptimeMillis())) { |
| mHandler.post(this); |
| } |
| } |
| }; |
| } |
| |
| private native double nativeGetScrollDelta( |
| int nativeTouchSmoothScrollGestureAndroid, double scale); |
| private native boolean nativeHasFinished(int nativeTouchSmoothScrollGestureAndroid); |
| private native void nativeSetHasSentMotionUp(int nativeTouchSmoothScrollGestureAndroid); |
| } |