summaryrefslogtreecommitdiffstats
path: root/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/NativePanZoomController.java
diff options
context:
space:
mode:
Diffstat (limited to 'mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/NativePanZoomController.java')
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/NativePanZoomController.java300
1 files changed, 300 insertions, 0 deletions
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/NativePanZoomController.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/NativePanZoomController.java
new file mode 100644
index 000000000..6b643c85b
--- /dev/null
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/NativePanZoomController.java
@@ -0,0 +1,300 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.gecko.gfx;
+
+import org.mozilla.gecko.GeckoAppShell;
+import org.mozilla.gecko.GeckoThread;
+import org.mozilla.gecko.PrefsHelper;
+import org.mozilla.gecko.annotation.WrapForJNI;
+import org.mozilla.gecko.gfx.DynamicToolbarAnimator.PinReason;
+import org.mozilla.gecko.mozglue.JNIObject;
+import org.mozilla.gecko.util.ThreadUtils;
+
+import org.json.JSONObject;
+
+import android.graphics.PointF;
+import android.util.TypedValue;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.InputDevice;
+
+class NativePanZoomController extends JNIObject implements PanZoomController {
+ private final PanZoomTarget mTarget;
+ private final LayerView mView;
+ private boolean mDestroyed;
+ private Overscroll mOverscroll;
+ boolean mNegateWheelScroll;
+ private float mPointerScrollFactor;
+ private PrefsHelper.PrefHandler mPrefsObserver;
+ private long mLastDownTime;
+ private static final float MAX_SCROLL = 0.075f * GeckoAppShell.getDpi();
+
+ @WrapForJNI(calledFrom = "ui")
+ private native boolean handleMotionEvent(
+ int action, int actionIndex, long time, int metaState,
+ int pointerId[], float x[], float y[], float orientation[], float pressure[],
+ float toolMajor[], float toolMinor[]);
+
+ @WrapForJNI(calledFrom = "ui")
+ private native boolean handleScrollEvent(
+ long time, int metaState,
+ float x, float y,
+ float hScroll, float vScroll);
+
+ @WrapForJNI(calledFrom = "ui")
+ private native boolean handleMouseEvent(
+ int action, long time, int metaState,
+ float x, float y, int buttons);
+
+ @WrapForJNI(calledFrom = "ui")
+ private native void handleMotionEventVelocity(long time, float ySpeed);
+
+ private boolean handleMotionEvent(MotionEvent event) {
+ if (mDestroyed) {
+ return false;
+ }
+
+ final int action = event.getActionMasked();
+ final int count = event.getPointerCount();
+
+ if (action == MotionEvent.ACTION_DOWN) {
+ mLastDownTime = event.getDownTime();
+ } else if (mLastDownTime != event.getDownTime()) {
+ return false;
+ }
+
+ final int[] pointerId = new int[count];
+ final float[] x = new float[count];
+ final float[] y = new float[count];
+ final float[] orientation = new float[count];
+ final float[] pressure = new float[count];
+ final float[] toolMajor = new float[count];
+ final float[] toolMinor = new float[count];
+
+ final MotionEvent.PointerCoords coords = new MotionEvent.PointerCoords();
+
+ for (int i = 0; i < count; i++) {
+ pointerId[i] = event.getPointerId(i);
+ event.getPointerCoords(i, coords);
+
+ x[i] = coords.x;
+ y[i] = coords.y;
+
+ orientation[i] = coords.orientation;
+ pressure[i] = coords.pressure;
+
+ // If we are converting to CSS pixels, we should adjust the radii as well.
+ toolMajor[i] = coords.toolMajor;
+ toolMinor[i] = coords.toolMinor;
+ }
+
+ return handleMotionEvent(action, event.getActionIndex(), event.getEventTime(),
+ event.getMetaState(), pointerId, x, y, orientation, pressure,
+ toolMajor, toolMinor);
+ }
+
+ private boolean handleScrollEvent(MotionEvent event) {
+ if (mDestroyed) {
+ return false;
+ }
+
+ final int count = event.getPointerCount();
+
+ if (count <= 0) {
+ return false;
+ }
+
+ final MotionEvent.PointerCoords coords = new MotionEvent.PointerCoords();
+ event.getPointerCoords(0, coords);
+ final float x = coords.x;
+ final float y = coords.y;
+
+ final float flipFactor = mNegateWheelScroll ? -1.0f : 1.0f;
+ final float hScroll = event.getAxisValue(MotionEvent.AXIS_HSCROLL) * flipFactor * mPointerScrollFactor;
+ final float vScroll = event.getAxisValue(MotionEvent.AXIS_VSCROLL) * flipFactor * mPointerScrollFactor;
+
+ return handleScrollEvent(event.getEventTime(), event.getMetaState(), x, y, hScroll, vScroll);
+ }
+
+ private boolean handleMouseEvent(MotionEvent event) {
+ if (mDestroyed) {
+ return false;
+ }
+
+ final int count = event.getPointerCount();
+
+ if (count <= 0) {
+ return false;
+ }
+
+ final MotionEvent.PointerCoords coords = new MotionEvent.PointerCoords();
+ event.getPointerCoords(0, coords);
+ final float x = coords.x;
+ final float y = coords.y;
+
+ return handleMouseEvent(event.getActionMasked(), event.getEventTime(), event.getMetaState(), x, y, event.getButtonState());
+ }
+
+
+ NativePanZoomController(PanZoomTarget target, View view) {
+ mTarget = target;
+ mView = (LayerView) view;
+ mDestroyed = true;
+
+ String[] prefs = { "ui.scrolling.negate_wheel_scroll" };
+ mPrefsObserver = new PrefsHelper.PrefHandlerBase() {
+ @Override public void prefValue(String pref, boolean value) {
+ if (pref.equals("ui.scrolling.negate_wheel_scroll")) {
+ mNegateWheelScroll = value;
+ }
+ }
+ };
+ PrefsHelper.addObserver(prefs, mPrefsObserver);
+
+ TypedValue outValue = new TypedValue();
+ if (view.getContext().getTheme().resolveAttribute(android.R.attr.listPreferredItemHeight, outValue, true)) {
+ mPointerScrollFactor = outValue.getDimension(view.getContext().getResources().getDisplayMetrics());
+ } else {
+ mPointerScrollFactor = MAX_SCROLL;
+ }
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+// NOTE: This commented out block of code allows Fennec to generate
+// mouse event instead of converting them to touch events.
+// This gives Fennec similar behaviour to desktop when using
+// a mouse.
+//
+// if (event.getToolType(0) == MotionEvent.TOOL_TYPE_MOUSE) {
+// return handleMouseEvent(event);
+// } else {
+// return handleMotionEvent(event);
+// }
+ return handleMotionEvent(event);
+ }
+
+ @Override
+ public boolean onMotionEvent(MotionEvent event) {
+ final int action = event.getActionMasked();
+ if (action == MotionEvent.ACTION_SCROLL) {
+ if (event.getDownTime() >= mLastDownTime) {
+ mLastDownTime = event.getDownTime();
+ } else if ((InputDevice.getDevice(event.getDeviceId()).getSources() & InputDevice.SOURCE_TOUCHPAD) == InputDevice.SOURCE_TOUCHPAD) {
+ return false;
+ }
+ return handleScrollEvent(event);
+ } else if ((action == MotionEvent.ACTION_HOVER_MOVE) ||
+ (action == MotionEvent.ACTION_HOVER_ENTER) ||
+ (action == MotionEvent.ACTION_HOVER_EXIT)) {
+ return handleMouseEvent(event);
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public void onMotionEventVelocity(final long aEventTime, final float aSpeedY) {
+ handleMotionEventVelocity(aEventTime, aSpeedY);
+ }
+
+ @Override @WrapForJNI(calledFrom = "ui") // PanZoomController
+ public void destroy() {
+ if (mPrefsObserver != null) {
+ PrefsHelper.removeObserver(mPrefsObserver);
+ mPrefsObserver = null;
+ }
+ if (mDestroyed || !mTarget.isGeckoReady()) {
+ return;
+ }
+ mDestroyed = true;
+ disposeNative();
+ }
+
+ @Override
+ public void attach() {
+ mDestroyed = false;
+ }
+
+ @WrapForJNI(calledFrom = "ui", dispatchTo = "gecko") @Override // JNIObject
+ protected native void disposeNative();
+
+ @Override
+ public void setOverscrollHandler(final Overscroll handler) {
+ mOverscroll = handler;
+ }
+
+ @WrapForJNI(stubName = "SetIsLongpressEnabled") // Called from test thread.
+ private native void nativeSetIsLongpressEnabled(boolean isLongpressEnabled);
+
+ @Override // PanZoomController
+ public void setIsLongpressEnabled(boolean isLongpressEnabled) {
+ if (!mDestroyed) {
+ nativeSetIsLongpressEnabled(isLongpressEnabled);
+ }
+ }
+
+ @WrapForJNI(calledFrom = "ui")
+ private native void adjustScrollForSurfaceShift(float aX, float aY);
+
+ @Override // PanZoomController
+ public ImmutableViewportMetrics adjustScrollForSurfaceShift(ImmutableViewportMetrics aMetrics, PointF aShift) {
+ adjustScrollForSurfaceShift(aShift.x, aShift.y);
+ return aMetrics.offsetViewportByAndClamp(aShift.x, aShift.y);
+ }
+
+ @WrapForJNI
+ private void updateOverscrollVelocity(final float x, final float y) {
+ if (mOverscroll != null) {
+ if (ThreadUtils.isOnUiThread() == true) {
+ mOverscroll.setVelocity(x * 1000.0f, Overscroll.Axis.X);
+ mOverscroll.setVelocity(y * 1000.0f, Overscroll.Axis.Y);
+ } else {
+ ThreadUtils.postToUiThread(new Runnable() {
+ @Override
+ public void run() {
+ // Multiply the velocity by 1000 to match what was done in JPZ.
+ mOverscroll.setVelocity(x * 1000.0f, Overscroll.Axis.X);
+ mOverscroll.setVelocity(y * 1000.0f, Overscroll.Axis.Y);
+ }
+ });
+ }
+ }
+ }
+
+ @WrapForJNI
+ private void updateOverscrollOffset(final float x, final float y) {
+ if (mOverscroll != null) {
+ if (ThreadUtils.isOnUiThread() == true) {
+ mOverscroll.setDistance(x, Overscroll.Axis.X);
+ mOverscroll.setDistance(y, Overscroll.Axis.Y);
+ } else {
+ ThreadUtils.postToUiThread(new Runnable() {
+ @Override
+ public void run() {
+ mOverscroll.setDistance(x, Overscroll.Axis.X);
+ mOverscroll.setDistance(y, Overscroll.Axis.Y);
+ }
+ });
+ }
+ }
+ }
+
+ @WrapForJNI(calledFrom = "ui")
+ private void setScrollingRootContent(final boolean isRootContent) {
+ mTarget.setScrollingRootContent(isRootContent);
+ }
+
+ /**
+ * Active SelectionCaretDrag requires DynamicToolbarAnimator to be pinned
+ * to avoid unwanted scroll interactions.
+ */
+ @WrapForJNI(calledFrom = "gecko")
+ private void onSelectionDragState(boolean state) {
+ mView.getDynamicToolbarAnimator().setPinned(state, PinReason.CARET_DRAG);
+ }
+}