Bug 603008 - Android widget multitouch implementation. r=blassey,kats

This commit is contained in:
Wes Johnston 2012-01-25 01:31:33 +01:00
parent 3627f3a4b8
commit a02904d162
16 changed files with 509 additions and 104 deletions

View File

@ -148,6 +148,7 @@ MOZ_SPELLCHECK = @MOZ_SPELLCHECK@
MOZ_ANDROID_HISTORY = @MOZ_ANDROID_HISTORY@
MOZ_WEBSMS_BACKEND = @MOZ_WEBSMS_BACKEND@
MOZ_JAVA_COMPOSITOR = @MOZ_JAVA_COMPOSITOR@
MOZ_ONLY_TOUCH_EVENTS = @MOZ_ONLY_TOUCH_EVENTS@
MOZ_TOUCH = @MOZ_TOUCH@
MOZ_PROFILELOCKING = @MOZ_PROFILELOCKING@
MOZ_FEEDS = @MOZ_FEEDS@

View File

@ -1790,4 +1790,7 @@ public class GeckoAppShell
public static void disableNetworkNotifications() {
GeckoNetworkManager.getInstance().disableNotifications();
}
// This is only used in Native Fennec.
public static void preventPanning() { }
}

View File

@ -46,6 +46,7 @@ import android.widget.*;
import android.hardware.*;
import android.location.*;
import android.util.FloatMath;
import android.util.DisplayMetrics;
import android.util.Log;
@ -101,7 +102,12 @@ public class GeckoEvent {
public int mType;
public int mAction;
public long mTime;
public Point mP0, mP1, mP2;
public Point[] mPoints;
public int[] mPointIndicies;
public int mPointerIndex;
public float[] mOrientations;
public float[] mPressures;
public Point[] mPointRadii;
public Rect mRect;
public double mX, mY, mZ;
public double mAlpha, mBeta, mGamma;
@ -144,10 +150,76 @@ public class GeckoEvent {
mAction = m.getAction();
mTime = m.getEventTime();
mMetaState = m.getMetaState();
mP0 = new Point((int)m.getX(0), (int)m.getY(0));
switch (mAction & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
case MotionEvent.ACTION_POINTER_DOWN:
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE: {
mCount = m.getPointerCount();
if (mCount > 1)
mP1 = new Point((int)m.getX(1), (int)m.getY(1));
mPoints = new Point[mCount];
mPointIndicies = new int[mCount];
mOrientations = new float[mCount];
mPressures = new float[mCount];
mPointRadii = new Point[mCount];
mPointerIndex = (mAction & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
for (int i = 0; i < mCount; i++) {
addMotionPoint(i, i, m);
}
break;
}
default: {
mCount = 0;
mPointerIndex = -1;
mPoints = new Point[mCount];
mPointIndicies = new int[mCount];
mOrientations = new float[mCount];
mPressures = new float[mCount];
mPointRadii = new Point[mCount];
}
}
}
public void addMotionPoint(int index, int eventIndex, MotionEvent event) {
PointF geckoPoint = new PointF(event.getX(eventIndex), event.getY(eventIndex));
mPoints[index] = new Point((int)Math.round(geckoPoint.x), (int)Math.round(geckoPoint.y));
mPointIndicies[index] = event.getPointerId(eventIndex);
// getToolMajor, getToolMinor and getOrientation are API Level 9 features
if (Build.VERSION.SDK_INT >= 9) {
double radians = event.getOrientation(eventIndex);
mOrientations[index] = (float) Math.toDegrees(radians);
// w3c touchevents spec does not allow orientations == 90
// this shifts it to -90, which will be shifted to zero below
if (mOrientations[index] == 90)
mOrientations[index] = -90;
// w3c touchevent radius are given by an orientation between 0 and 90
// the radius is found by removing the orientation and measuring the x and y
// radius of the resulting ellipse
// for android orientations >= 0 and < 90, the major axis should correspond to
// just reporting the y radius as the major one, and x as minor
// however, for a radius < 0, we have to shift the orientation by adding 90, and
// reverse which radius is major and minor
if (mOrientations[index] < 0) {
mOrientations[index] += 90;
mPointRadii[index] = new Point((int)event.getToolMajor(eventIndex)/2,
(int)event.getToolMinor(eventIndex)/2);
} else {
mPointRadii[index] = new Point((int)event.getToolMinor(eventIndex)/2,
(int)event.getToolMajor(eventIndex)/2);
}
} else {
float size = event.getSize(eventIndex);
DisplayMetrics displaymetrics = new DisplayMetrics();
GeckoApp.mAppContext.getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
size = size*Math.min(displaymetrics.heightPixels, displaymetrics.widthPixels);
mPointRadii[index] = new Point((int)size,(int)size);
mOrientations[index] = 0;
}
mPressures[index] = event.getPressure(eventIndex);
}
public GeckoEvent(SensorEvent s) {
@ -227,9 +299,10 @@ public class GeckoEvent {
mType = etype;
mP0 = new Point(w, h);
mP1 = new Point(screenw, screenh);
mP2 = new Point(0, 0);
mPoints = new Point[3];
mPoints[0] = new Point(w, h);
mPoints[1] = new Point(screenw, screenh);
mPoints[2] = new Point(0, 0);
}
public GeckoEvent(String subject, String data) {

View File

@ -503,6 +503,9 @@ public class GeckoAppShell
layerController.setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View view, MotionEvent event) {
if (event == null)
return true;
GeckoAppShell.sendEventToGecko(new GeckoEvent(event));
return true;
}
});
@ -1117,6 +1120,15 @@ public class GeckoAppShell
});
}
public static void preventPanning() {
getMainHandler().post(new Runnable() {
public void run() {
LayerController layerController = GeckoApp.mAppContext.getLayerController();
layerController.preventPanning(true);
}
});
}
public static boolean isNetworkLinkUp() {
ConnectivityManager cm = (ConnectivityManager)
GeckoApp.mAppContext.getSystemService(Context.CONNECTIVITY_SERVICE);

View File

@ -48,6 +48,11 @@ import android.widget.*;
import android.hardware.*;
import android.location.*;
import android.util.FloatMath;
import android.util.DisplayMetrics;
import android.graphics.PointF;
import android.text.format.Time;
import android.os.SystemClock;
import java.lang.System;
import android.util.Log;
@ -104,7 +109,12 @@ public class GeckoEvent {
public int mType;
public int mAction;
public long mTime;
public Point mP0, mP1, mP2;
public Point[] mPoints;
public int[] mPointIndicies;
public int mPointerIndex; // index of the point that has changed
public float[] mOrientations;
public float[] mPressures;
public Point[] mPointRadii;
public Rect mRect;
public double mX, mY, mZ;
public double mAlpha, mBeta, mGamma;
@ -145,12 +155,79 @@ public class GeckoEvent {
public GeckoEvent(MotionEvent m) {
mType = MOTION_EVENT;
mAction = m.getAction();
mTime = m.getEventTime();
mTime = (System.currentTimeMillis() - SystemClock.elapsedRealtime()) + m.getEventTime();
mMetaState = m.getMetaState();
mP0 = new Point((int)m.getX(0), (int)m.getY(0));
switch (mAction & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
case MotionEvent.ACTION_POINTER_DOWN:
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE: {
mCount = m.getPointerCount();
if (mCount > 1)
mP1 = new Point((int)m.getX(1), (int)m.getY(1));
mPoints = new Point[mCount];
mPointIndicies = new int[mCount];
mOrientations = new float[mCount];
mPressures = new float[mCount];
mPointRadii = new Point[mCount];
mPointerIndex = (mAction & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
for (int i = 0; i < mCount; i++) {
addMotionPoint(i, i, m);
}
break;
}
default: {
mCount = 0;
mPointerIndex = -1;
mPoints = new Point[mCount];
mPointIndicies = new int[mCount];
mOrientations = new float[mCount];
mPressures = new float[mCount];
mPointRadii = new Point[mCount];
}
}
}
public void addMotionPoint(int index, int eventIndex, MotionEvent event) {
PointF geckoPoint = new PointF(event.getX(eventIndex), event.getY(eventIndex));
geckoPoint = GeckoApp.mAppContext.getLayerController().convertViewPointToLayerPoint(geckoPoint);
mPoints[index] = new Point((int)Math.round(geckoPoint.x), (int)Math.round(geckoPoint.y));
mPointIndicies[index] = event.getPointerId(eventIndex);
// getToolMajor, getToolMinor and getOrientation are API Level 9 features
if (Build.VERSION.SDK_INT >= 9) {
double radians = event.getOrientation(eventIndex);
mOrientations[index] = (float) Math.toDegrees(radians);
// w3c touchevents spec does not allow orientations == 90
// this shifts it to -90, which will be shifted to zero below
if (mOrientations[index] == 90)
mOrientations[index] = -90;
// w3c touchevent radius are given by an orientation between 0 and 90
// the radius is found by removing the orientation and measuring the x and y
// radius of the resulting ellipse
// for android orientations >= 0 and < 90, the major axis should correspond to
// just reporting the y radius as the major one, and x as minor
// however, for a radius < 0, we have to shift the orientation by adding 90, and
// reverse which radius is major and minor
if (mOrientations[index] < 0) {
mOrientations[index] += 90;
mPointRadii[index] = new Point((int)event.getToolMajor(eventIndex)/2,
(int)event.getToolMinor(eventIndex)/2);
} else {
mPointRadii[index] = new Point((int)event.getToolMinor(eventIndex)/2,
(int)event.getToolMajor(eventIndex)/2);
}
} else {
float size = event.getSize(eventIndex);
DisplayMetrics displaymetrics = new DisplayMetrics();
GeckoApp.mAppContext.getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
size = size*Math.min(displaymetrics.heightPixels, displaymetrics.widthPixels);
mPointRadii[index] = new Point((int)size,(int)size);
mOrientations[index] = 0;
}
mPressures[index] = event.getPressure(eventIndex);
}
public GeckoEvent(SensorEvent s) {
@ -230,9 +307,10 @@ public class GeckoEvent {
mType = etype;
mP0 = new Point(w, h);
mP1 = new Point(screenw, screenh);
mP2 = new Point(tilew, tileh);
mPoints = new Point[3];
mPoints[0] = new Point(w, h);
mPoints[1] = new Point(screenw, screenh);
mPoints[2] = new Point(tilew, tileh);
}
public GeckoEvent(String subject, String data) {

View File

@ -45,10 +45,12 @@ import org.mozilla.gecko.gfx.LayerView;
import org.mozilla.gecko.ui.PanZoomController;
import org.mozilla.gecko.ui.SimpleScaleGestureDetector;
import org.mozilla.gecko.GeckoApp;
import org.mozilla.gecko.GeckoEvent;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
@ -59,6 +61,8 @@ import android.view.GestureDetector;
import android.view.ScaleGestureDetector;
import android.view.View.OnTouchListener;
import java.lang.Math;
import java.util.Timer;
import java.util.TimerTask;
/**
* The layer controller manages a tile that represents the visible page. It does panning and
@ -100,6 +104,14 @@ public class LayerController {
private static final int DANGER_ZONE_X = 75;
private static final int DANGER_ZONE_Y = 150;
/* The time limit for pages to respond with preventDefault on touchevents
* before we begin panning the page */
private static final int PREVENT_DEFAULT_TIMEOUT = 200;
private boolean allowDefaultActions = true;
private Timer allowDefaultTimer = null;
private boolean inTouchSession = false;
public LayerController(Context context) {
mContext = context;
@ -149,6 +161,7 @@ public class LayerController {
public Bitmap getBackgroundPattern() { return getDrawable("background"); }
public Bitmap getShadowPattern() { return getDrawable("shadow"); }
public PanZoomController getPanZoomController() { return mPanZoomController; }
public GestureDetector.OnGestureListener getGestureListener() { return mPanZoomController; }
public SimpleScaleGestureDetector.SimpleScaleGestureListener getScaleGestureListener() {
return mPanZoomController;
@ -347,11 +360,58 @@ public class LayerController {
* pan/zoom controller to do the dirty work.
*/
public boolean onTouchEvent(MotionEvent event) {
if (mPanZoomController.onTouchEvent(event))
return true;
int action = event.getAction();
if ((action & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_DOWN) {
post(new Runnable() {
public void run() {
mView.clearEventQueue();
preventPanning(true);
}
});
}
if (mOnTouchListener != null)
return mOnTouchListener.onTouch(mView, event);
return false;
mOnTouchListener.onTouch(mView, event);
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_MOVE: {
if (!inTouchSession && allowDefaultTimer == null) {
inTouchSession = true;
allowDefaultTimer = new Timer();
allowDefaultTimer.schedule(new TimerTask() {
public void run() {
post(new Runnable() {
public void run() {
preventPanning(false);
}
});
}
}, PREVENT_DEFAULT_TIMEOUT);
}
break;
}
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP: {
inTouchSession = false;
}
}
return !allowDefaultActions;
}
public void preventPanning(boolean aValue) {
if (allowDefaultTimer != null) {
allowDefaultTimer.cancel();
allowDefaultTimer.purge();
allowDefaultTimer = null;
}
allowDefaultActions = !aValue;
if (aValue) {
mView.clearEventQueue();
mPanZoomController.cancelTouch();
} else {
mView.processEventQueue();
}
}
/** Retrieves the color that the checkerboard should be. */

View File

@ -48,6 +48,8 @@ import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.util.Log;
import java.util.LinkedList;
/**
* A view rendered by the layer compositor.
@ -64,6 +66,10 @@ public class LayerView extends GLSurfaceView {
private SimpleScaleGestureDetector mScaleGestureDetector;
private long mRenderTime;
private boolean mRenderTimeReset;
private static String LOGTAG = "GeckoLayerView";
/* List of events to be processed if the page does not prevent them. Should only be touched on the main thread */
private LinkedList<MotionEvent> mEventQueue = new LinkedList<MotionEvent>();
public LayerView(Context context, LayerController controller) {
super(context);
@ -83,14 +89,40 @@ public class LayerView extends GLSurfaceView {
setFocusableInTouchMode(true);
}
private void addToEventQueue(MotionEvent event) {
MotionEvent copy = MotionEvent.obtain(event);
mEventQueue.add(copy);
}
public void processEventQueue() {
MotionEvent event = mEventQueue.poll();
while(event != null) {
processEvent(event);
event = mEventQueue.poll();
}
}
public void clearEventQueue() {
mEventQueue.clear();
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (mController.onTouchEvent(event)) {
addToEventQueue(event);
return true;
}
return processEvent(event);
}
private boolean processEvent(MotionEvent event) {
if (mGestureDetector.onTouchEvent(event))
return true;
mScaleGestureDetector.onTouchEvent(event);
if (mScaleGestureDetector.isInProgress())
return true;
return mController.onTouchEvent(event);
mController.getPanZoomController().onTouchEvent(event);
return true;
}
public LayerController getController() { return mController; }

View File

@ -843,7 +843,7 @@ public class PanZoomController
return true;
}
private void cancelTouch() {
public void cancelTouch() {
GeckoEvent e = new GeckoEvent("Gesture:CancelTouch", "");
GeckoAppShell.sendEventToGecko(e);
}

View File

@ -67,6 +67,9 @@ MOZ_APP_COMPONENT_INCLUDE=nsBrowserComponents.h
# use custom widget for html:select
MOZ_USE_NATIVE_POPUP_WINDOWS=1
# dispatch only touch events (no mouse events)
MOZ_ONLY_TOUCH_EVENTS=1
MOZ_APP_ID={aa3c5121-dab2-40e2-81ca-7ea25febc110}
MOZ_JAVA_COMPOSITOR=1

View File

@ -139,6 +139,7 @@ AndroidBridge::Init(JNIEnv *jEnv,
jGetDpi = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "getDpi", "()I");
jSetFullScreen = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "setFullScreen", "(Z)V");
jShowInputMethodPicker = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "showInputMethodPicker", "()V");
jPreventPanning = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "preventPanning", "()V");
jHideProgressDialog = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "hideProgressDialog", "()V");
jPerformHapticFeedback = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "performHapticFeedback", "(Z)V");
jVibrate1 = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "vibrate", "(J)V");
@ -1612,3 +1613,9 @@ NS_IMETHODIMP nsAndroidBridge::SetDrawMetadataProvider(nsIAndroidDrawMetadataPro
return NS_OK;
}
void
AndroidBridge::PreventPanning() {
ALOG_BRIDGE("AndroidBridge::PreventPanning");
mJNIEnv->CallStaticVoidMethod(mGeckoAppShellClass, jPreventPanning);
}

View File

@ -218,6 +218,8 @@ public:
void ShowInputMethodPicker();
void PreventPanning();
void HideProgressDialogOnce();
bool IsNetworkLinkUp();
@ -413,6 +415,7 @@ protected:
jmethodID jGetDpi;
jmethodID jSetFullScreen;
jmethodID jShowInputMethodPicker;
jmethodID jPreventPanning;
jmethodID jHideProgressDialog;
jmethodID jPerformHapticFeedback;
jmethodID jVibrate1;

View File

@ -44,9 +44,11 @@ jclass AndroidGeckoEvent::jGeckoEventClass = 0;
jfieldID AndroidGeckoEvent::jActionField = 0;
jfieldID AndroidGeckoEvent::jTypeField = 0;
jfieldID AndroidGeckoEvent::jTimeField = 0;
jfieldID AndroidGeckoEvent::jP0Field = 0;
jfieldID AndroidGeckoEvent::jP1Field = 0;
jfieldID AndroidGeckoEvent::jP2Field = 0;
jfieldID AndroidGeckoEvent::jPoints = 0;
jfieldID AndroidGeckoEvent::jPointIndicies = 0;
jfieldID AndroidGeckoEvent::jPressures = 0;
jfieldID AndroidGeckoEvent::jPointRadii = 0;
jfieldID AndroidGeckoEvent::jOrientations = 0;
jfieldID AndroidGeckoEvent::jAlphaField = 0;
jfieldID AndroidGeckoEvent::jBetaField = 0;
jfieldID AndroidGeckoEvent::jGammaField = 0;
@ -64,6 +66,7 @@ jfieldID AndroidGeckoEvent::jFlagsField = 0;
jfieldID AndroidGeckoEvent::jUnicodeCharField = 0;
jfieldID AndroidGeckoEvent::jOffsetField = 0;
jfieldID AndroidGeckoEvent::jCountField = 0;
jfieldID AndroidGeckoEvent::jPointerIndexField = 0;
jfieldID AndroidGeckoEvent::jRangeTypeField = 0;
jfieldID AndroidGeckoEvent::jRangeStylesField = 0;
jfieldID AndroidGeckoEvent::jRangeForeColorField = 0;
@ -157,9 +160,11 @@ AndroidGeckoEvent::InitGeckoEventClass(JNIEnv *jEnv)
jActionField = getField("mAction", "I");
jTypeField = getField("mType", "I");
jTimeField = getField("mTime", "J");
jP0Field = getField("mP0", "Landroid/graphics/Point;");
jP1Field = getField("mP1", "Landroid/graphics/Point;");
jP2Field = getField("mP2", "Landroid/graphics/Point;");
jPoints = getField("mPoints", "[Landroid/graphics/Point;");
jPointIndicies = getField("mPointIndicies", "[I");
jOrientations = getField("mOrientations", "[F");
jPressures = getField("mPressures", "[F");
jPointRadii = getField("mPointRadii", "[Landroid/graphics/Point;");
jAlphaField = getField("mAlpha", "D");
jBetaField = getField("mBeta", "D");
jGammaField = getField("mGamma", "D");
@ -176,6 +181,7 @@ AndroidGeckoEvent::InitGeckoEventClass(JNIEnv *jEnv)
jUnicodeCharField = getField("mUnicodeChar", "I");
jOffsetField = getField("mOffset", "I");
jCountField = getField("mCount", "I");
jPointerIndexField = getField("mPointerIndex", "I");
jRangeTypeField = getField("mRangeType", "I");
jRangeStylesField = getField("mRangeStyles", "I");
jRangeForeColorField = getField("mRangeForeColor", "I");
@ -338,27 +344,47 @@ AndroidGeckoSoftwareLayerClient::InitGeckoSoftwareLayerClientClass(JNIEnv *jEnv)
#undef getMethod
void
AndroidGeckoEvent::ReadP0Field(JNIEnv *jenv)
AndroidGeckoEvent::ReadPointArray(nsTArray<nsIntPoint> &points,
JNIEnv *jenv,
jfieldID field,
PRUint32 count)
{
AndroidPoint p0(jenv, jenv->GetObjectField(wrappedObject(), jP0Field));
mP0.x = p0.X();
mP0.y = p0.Y();
jobjectArray jObjArray = (jobjectArray)jenv->GetObjectField(wrapped_obj, field);
for (PRInt32 i = 0; i < count; i++) {
jobject jObj = jenv->GetObjectArrayElement(jObjArray, i);
AndroidPoint jpoint(jenv, jObj);
nsIntPoint p(jpoint.X(), jpoint.Y());
points.AppendElement(p);
}
}
void
AndroidGeckoEvent::ReadP1Field(JNIEnv *jenv)
AndroidGeckoEvent::ReadIntArray(nsTArray<int> &aVals,
JNIEnv *jenv,
jfieldID field,
PRUint32 count)
{
AndroidPoint p1(jenv, jenv->GetObjectField(wrappedObject(), jP1Field));
mP1.x = p1.X();
mP1.y = p1.Y();
jintArray jIntArray = (jintArray)jenv->GetObjectField(wrapped_obj, field);
jint *vals = jenv->GetIntArrayElements(jIntArray, false);
for (PRInt32 i = 0; i < count; i++) {
aVals.AppendElement(vals[i]);
}
jenv->ReleaseIntArrayElements(jIntArray, vals, JNI_ABORT);
}
void
AndroidGeckoEvent::ReadP2Field(JNIEnv *jenv)
AndroidGeckoEvent::ReadFloatArray(nsTArray<float> &aVals,
JNIEnv *jenv,
jfieldID field,
PRUint32 count)
{
AndroidPoint p2(jenv, jenv->GetObjectField(wrappedObject(), jP2Field));
mP2.x = p2.X();
mP2.y = p2.Y();
jfloatArray jFloatArray = (jfloatArray)jenv->GetObjectField(wrapped_obj, field);
jfloat *vals = jenv->GetFloatArrayElements(jFloatArray, false);
for (PRInt32 i = 0; i < count; i++) {
aVals.AppendElement(vals[i]);
}
jenv->ReleaseFloatArrayElements(jFloatArray, vals, JNI_ABORT);
}
void
@ -425,9 +451,7 @@ AndroidGeckoEvent::Init(JNIEnv *jenv, jobject jobj)
switch (mType) {
case SIZE_CHANGED:
ReadP0Field(jenv);
ReadP1Field(jenv);
ReadP2Field(jenv);
ReadPointArray(mPoints, jenv, jPoints, 3);
break;
case KEY_EVENT:
@ -443,9 +467,14 @@ AndroidGeckoEvent::Init(JNIEnv *jenv, jobject jobj)
mTime = jenv->GetLongField(jobj, jTimeField);
mMetaState = jenv->GetIntField(jobj, jMetaStateField);
mCount = jenv->GetIntField(jobj, jCountField);
ReadP0Field(jenv);
if (mCount > 1)
ReadP1Field(jenv);
mPointerIndex = jenv->GetIntField(jobj, jPointerIndexField);
ReadPointArray(mPointRadii, jenv, jPointRadii, mCount);
ReadFloatArray(mOrientations, jenv, jOrientations, mCount);
ReadFloatArray(mPressures, jenv, jPressures, mCount);
ReadPointArray(mPoints, jenv, jPoints, mCount);
ReadIntArray(mPointIndicies, jenv, jPointIndicies, mCount);
break;
case IME_EVENT:
@ -544,10 +573,7 @@ AndroidGeckoEvent::Init(AndroidGeckoEvent *aResizeEvent)
mType = FORCED_RESIZE;
mTime = aResizeEvent->mTime;
mP0.x = aResizeEvent->mP0.x;
mP0.y = aResizeEvent->mP0.y;
mP1.x = aResizeEvent->mP1.x;
mP1.y = aResizeEvent->mP1.y;
mPoints = aResizeEvent->mPoints; // x,y coordinates
}
void

View File

@ -434,9 +434,11 @@ public:
int Action() { return mAction; }
int Type() { return mType; }
int64_t Time() { return mTime; }
const nsIntPoint& P0() { return mP0; }
const nsIntPoint& P1() { return mP1; }
const nsIntPoint& P2() { return mP2; }
nsTArray<nsIntPoint> Points() { return mPoints; }
nsTArray<int> PointIndicies() { return mPointIndicies; }
nsTArray<float> Pressures() { return mPressures; }
nsTArray<float> Orientations() { return mOrientations; }
nsTArray<nsIntPoint> PointRadii() { return mPointRadii; }
double Alpha() { return mAlpha; }
double Beta() { return mBeta; }
double Gamma() { return mGamma; }
@ -452,6 +454,7 @@ public:
int UnicodeChar() { return mUnicodeChar; }
int Offset() { return mOffset; }
int Count() { return mCount; }
int PointerIndex() { return mPointerIndex; }
int RangeType() { return mRangeType; }
int RangeStyles() { return mRangeStyles; }
int RangeForeColor() { return mRangeForeColor; }
@ -465,9 +468,11 @@ protected:
int mAction;
int mType;
int64_t mTime;
nsIntPoint mP0;
nsIntPoint mP1;
nsIntPoint mP2;
nsTArray<nsIntPoint> mPoints;
nsTArray<nsIntPoint> mPointRadii;
nsTArray<int> mPointIndicies;
nsTArray<float> mOrientations;
nsTArray<float> mPressures;
nsIntRect mRect;
int mFlags, mMetaState;
int mKeyCode, mUnicodeChar;
@ -476,15 +481,25 @@ protected:
int mRangeForeColor, mRangeBackColor;
double mAlpha, mBeta, mGamma;
double mX, mY, mZ;
int mPointerIndex;
nsString mCharacters, mCharactersExtra;
nsRefPtr<nsGeoPosition> mGeoPosition;
nsRefPtr<nsGeoPositionAddress> mGeoAddress;
double mBandwidth;
bool mCanBeMetered;
void ReadP0Field(JNIEnv *jenv);
void ReadP1Field(JNIEnv *jenv);
void ReadP2Field(JNIEnv *jenv);
void ReadIntArray(nsTArray<int> &aVals,
JNIEnv *jenv,
jfieldID field,
PRUint32 count);
void ReadFloatArray(nsTArray<float> &aVals,
JNIEnv *jenv,
jfieldID field,
PRUint32 count);
void ReadPointArray(nsTArray<nsIntPoint> &mPoints,
JNIEnv *jenv,
jfieldID field,
PRUint32 count);
void ReadRectField(JNIEnv *jenv);
void ReadCharactersField(JNIEnv *jenv);
void ReadCharactersExtraField(JNIEnv *jenv);
@ -493,9 +508,11 @@ protected:
static jfieldID jActionField;
static jfieldID jTypeField;
static jfieldID jTimeField;
static jfieldID jP0Field;
static jfieldID jP1Field;
static jfieldID jP2Field;
static jfieldID jPoints;
static jfieldID jPointIndicies;
static jfieldID jOrientations;
static jfieldID jPressures;
static jfieldID jPointRadii;
static jfieldID jAlphaField;
static jfieldID jBetaField;
static jfieldID jGammaField;
@ -512,6 +529,7 @@ protected:
static jfieldID jFlagsField;
static jfieldID jOffsetField;
static jfieldID jCountField;
static jfieldID jPointerIndexField;
static jfieldID jUnicodeCharField;
static jfieldID jRangeTypeField;
static jfieldID jRangeStylesField;

View File

@ -103,6 +103,7 @@ LOCAL_INCLUDES += \
-I$(topsrcdir)/dom/system/android \
-I$(topsrcdir)/toolkit/components/places \
-I$(topsrcdir)/docshell/base \
-I$(topsrcdir)/content/events/src \
-I$(srcdir) \
$(NULL)

View File

@ -58,6 +58,7 @@ using mozilla::unused;
#include "nsRenderingContext.h"
#include "nsIDOMSimpleGestureEvent.h"
#include "nsDOMTouchEvent.h"
#include "nsGkAtoms.h"
#include "nsWidgetsCID.h"
@ -895,8 +896,11 @@ nsWindow::OnGlobalAndroidEvent(AndroidGeckoEvent *ae)
win->mChildren[i]->mBounds.height = 0;
}
case AndroidGeckoEvent::SIZE_CHANGED: {
int nw = ae->P0().x;
int nh = ae->P0().y;
nsTArray<nsIntPoint> points = ae->Points();
NS_ASSERTION(points.Length() != 3, "Size changed does not have enough coordinates");
int nw = points[0].x;
int nh = points[0].y;
if (ae->Type() == AndroidGeckoEvent::FORCED_RESIZE || nw != gAndroidBounds.width ||
nh != gAndroidBounds.height) {
@ -913,11 +917,11 @@ nsWindow::OnGlobalAndroidEvent(AndroidGeckoEvent *ae)
}
}
gAndroidTileSize.width = ae->P2().x;
gAndroidTileSize.height = ae->P2().y;
gAndroidTileSize.width = points[2].x;
gAndroidTileSize.height = points[2].y;
int newScreenWidth = ae->P1().x;
int newScreenHeight = ae->P1().y;
int newScreenWidth = points[1].x;
int newScreenHeight = points[1].y;
if (newScreenWidth == gAndroidScreenBounds.width &&
newScreenHeight == gAndroidScreenBounds.height)
@ -951,29 +955,36 @@ nsWindow::OnGlobalAndroidEvent(AndroidGeckoEvent *ae)
else
obs->RemoveObserver(notifier, "ipc:content-created");
}
break;
}
case AndroidGeckoEvent::MOTION_EVENT: {
win->UserActivity();
if (!gTopLevelWindows.IsEmpty()) {
nsIntPoint pt(ae->P0());
nsIntPoint pt(0,0);
nsTArray<nsIntPoint> points = ae->Points();
if (points.Length() > 0) {
pt = points[0];
}
pt.x = clamped(pt.x, 0, gAndroidBounds.width - 1);
pt.y = clamped(pt.y, 0, gAndroidBounds.height - 1);
nsWindow *target = win->FindWindowForPoint(pt);
#if 0
ALOG("MOTION_EVENT %f,%f -> %p (visible: %d children: %d)", ae->P0().x, ae->P0().y, (void*)target,
ALOG("MOTION_EVENT %f,%f -> %p (visible: %d children: %d)", pt.x, pt.y, (void*)target,
target ? target->mIsVisible : 0,
target ? target->mChildren.Length() : 0);
DumpWindows();
#endif
if (target) {
if (ae->Count() > 1)
target->OnMultitouchEvent(ae);
else
bool preventDefaultActions = target->OnMultitouchEvent(ae);
if (!preventDefaultActions && ae->Count() == 2) {
target->OnGestureEvent(ae);
}
#ifndef MOZ_ONLY_TOUCH_EVENTS
if (!preventDefaultActions && ae->Count() < 2)
target->OnMotionEvent(ae);
#endif
}
}
break;
@ -1470,30 +1481,11 @@ nsWindow::OnMotionEvent(AndroidGeckoEvent *ae)
return;
}
nsRefPtr<nsWindow> kungFuDeathGrip(this);
nsIntPoint pt(ae->P0());
nsIntPoint offset = WidgetToScreenOffset();
//ALOG("#### motion pt: %d %d offset: %d %d", pt.x, pt.y, offset.x, offset.y);
pt.x -= offset.x;
pt.y -= offset.y;
// XXX possibly bound the range of pt here. some code may get confused.
send_again:
nsMouseEvent event(true,
msg, this,
nsMouseEvent::eReal, nsMouseEvent::eNormal);
InitEvent(event, &pt);
event.time = ae->Time();
event.isShift = !!(ae->MetaState() & AndroidKeyEvent::META_SHIFT_ON);
event.isControl = false;
event.isMeta = false;
event.isAlt = !!(ae->MetaState() & AndroidKeyEvent::META_ALT_ON);
// XXX can we synthesize different buttons?
event.button = nsMouseEvent::eLeftButton;
@ -1501,8 +1493,8 @@ send_again:
event.clickCount = 1;
// XXX add the double-click handling logic here
DispatchEvent(&event);
if (ae->Points().Length() > 0)
DispatchMotionEvent(event, ae, ae->Points()[0]);
if (Destroyed())
return;
@ -1520,16 +1512,87 @@ getDistance(const nsIntPoint &p1, const nsIntPoint &p2)
return sqrt(deltaX*deltaX + deltaY*deltaY);
}
void nsWindow::OnMultitouchEvent(AndroidGeckoEvent *ae)
bool nsWindow::OnMultitouchEvent(AndroidGeckoEvent *ae)
{
switch (ae->Action() & AndroidMotionEvent::ACTION_MASK) {
case AndroidMotionEvent::ACTION_DOWN:
case AndroidMotionEvent::ACTION_POINTER_DOWN: {
nsTouchEvent event(PR_TRUE, NS_TOUCH_START, this);
return DispatchMultitouchEvent(event, ae);
}
case AndroidMotionEvent::ACTION_MOVE: {
nsTouchEvent event(PR_TRUE, NS_TOUCH_MOVE, this);
return DispatchMultitouchEvent(event, ae);
}
case AndroidMotionEvent::ACTION_UP:
case AndroidMotionEvent::ACTION_POINTER_UP: {
nsTouchEvent event(PR_TRUE, NS_TOUCH_END, this);
return DispatchMultitouchEvent(event, ae);
}
case AndroidMotionEvent::ACTION_OUTSIDE:
case AndroidMotionEvent::ACTION_CANCEL: {
nsTouchEvent event(PR_TRUE, NS_TOUCH_CANCEL, this);
return DispatchMultitouchEvent(event, ae);
}
}
return false;
}
bool
nsWindow::DispatchMultitouchEvent(nsTouchEvent &event, AndroidGeckoEvent *ae)
{
nsIntPoint offset = WidgetToScreenOffset();
event.isShift = false;
event.isControl = false;
event.isMeta = false;
event.isAlt = false;
event.time = ae->Time();
int action = ae->Action() & AndroidMotionEvent::ACTION_MASK;
if (action == AndroidMotionEvent::ACTION_UP ||
action == AndroidMotionEvent::ACTION_POINTER_UP) {
event.touches.SetCapacity(1);
int pointerIndex = ae->PointerIndex();
nsCOMPtr<nsIDOMTouch> t(new nsDOMTouch(ae->PointIndicies()[pointerIndex],
ae->Points()[pointerIndex] - offset,
ae->PointRadii()[pointerIndex],
ae->Orientations()[pointerIndex],
ae->Pressures()[pointerIndex]));
event.touches.AppendElement(t);
} else {
int count = ae->Count();
event.touches.SetCapacity(count);
for (int i = 0; i < count; i++) {
nsCOMPtr<nsIDOMTouch> t(new nsDOMTouch(ae->PointIndicies()[i],
ae->Points()[i] - offset,
ae->PointRadii()[i],
ae->Orientations()[i],
ae->Pressures()[i]));
event.touches.AppendElement(t);
}
}
nsEventStatus status;
DispatchEvent(&event, status);
if (status == nsEventStatus_eConsumeNoDefault) {
AndroidBridge::Bridge()->PreventPanning();
return true;
}
return false;
}
void
nsWindow::OnGestureEvent(AndroidGeckoEvent *ae)
{
PRUint32 msg = 0;
nsIntPoint midPoint;
midPoint.x = ((ae->P0().x + ae->P1().x) / 2);
midPoint.y = ((ae->P0().y + ae->P1().y) / 2);
midPoint.x = ((ae->Points()[0].x + ae->Points()[1].x) / 2);
midPoint.y = ((ae->Points()[0].y + ae->Points()[1].y) / 2);
nsIntPoint refPoint = midPoint - WidgetToScreenOffset();
double pinchDist = getDistance(ae->P0(), ae->P1());
double pinchDist = getDistance(ae->Points()[0], ae->Points()[1]);
double pinchDelta = 0;
switch (ae->Action() & AndroidMotionEvent::ACTION_MASK) {
@ -1612,6 +1675,26 @@ nsWindow::DispatchGestureEvent(PRUint32 msg, PRUint32 direction, double delta,
DispatchEvent(&event);
}
void
nsWindow::DispatchMotionEvent(nsInputEvent &event, AndroidGeckoEvent *ae,
const nsIntPoint &refPoint)
{
nsIntPoint offset = WidgetToScreenOffset();
event.isShift = PR_FALSE;
event.isControl = PR_FALSE;
event.isMeta = PR_FALSE;
event.isAlt = PR_FALSE;
event.time = ae->Time();
// XXX possibly bound the range of event.refPoint here.
// some code may get confused.
event.refPoint = refPoint - offset;
DispatchEvent(&event);
}
void
nsWindow::InitKeyEvent(nsKeyEvent& event, AndroidGeckoEvent& key)
{

View File

@ -73,8 +73,9 @@ public:
void OnAndroidEvent(mozilla::AndroidGeckoEvent *ae);
void OnDraw(mozilla::AndroidGeckoEvent *ae);
bool OnMultitouchEvent(mozilla::AndroidGeckoEvent *ae);
void OnGestureEvent(mozilla::AndroidGeckoEvent *ae);
void OnMotionEvent(mozilla::AndroidGeckoEvent *ae);
void OnMultitouchEvent(mozilla::AndroidGeckoEvent *ae);
void OnKeyEvent(mozilla::AndroidGeckoEvent *ae);
void OnIMEEvent(mozilla::AndroidGeckoEvent *ae);
@ -221,7 +222,11 @@ protected:
private:
void InitKeyEvent(nsKeyEvent& event, mozilla::AndroidGeckoEvent& key);
void DispatchGestureEvent(mozilla::AndroidGeckoEvent *ae);
bool DispatchMultitouchEvent(nsTouchEvent &event,
mozilla::AndroidGeckoEvent *ae);
void DispatchMotionEvent(nsInputEvent &event,
mozilla::AndroidGeckoEvent *ae,
const nsIntPoint &refPoint);
void DispatchGestureEvent(PRUint32 msg, PRUint32 direction, double delta,
const nsIntPoint &refPoint, PRUint64 time);
void HandleSpecialKey(mozilla::AndroidGeckoEvent *ae);