Android Inputs handling improvements

Hey monkeys,

for those developing projects on android, i’ve just fixed some issues and added some more features to the touch input handling.



First the DOUBLETAP event is now correctly dispatched to JME.

Second, the AndroidInput now supports multi touch event correctly.

For now only the scale complex gesture is handled (it was already before), but now all subsequent events are sent to JME (DOWN, UP, MOVE).

This allow you to handle any gesture.

I plan to implement a way to add your own GestureDetection classes to the AndroidInput.



For now, to detect how many fingers touching the screen you just have to count how many DOWN events you had without UP events…

One event is sent by pointers.



Also there is one move event generated by pointer so if you MOVE 3 fingers on the screen at the same time, 3 MOVE events will be generated (each with the corresponding coordinates).



Also, FLING, SCROLL, LONGPRESS and SHOWPRESS are now triggered even if there is multiple fingers.

This allow you to handle for example, 2 fingers Scroll events or 3 fingers fling events and so on…



Go ahead and try this if you feel like it, any feedback is appreciated.

4 Likes

When using multi-input second input doesn’t register when second event is up. In the example when letting go of input2 it displays “event1up” instead of “event2up”



ex.



if(evt.getPointerId() == 0){

switch(evt.getType()){

case MOVE:

System.out.println(“event1move”);

break;

case UP:

System.out.println(“event1up”);

break;

}

}

if(evt.getPointerId() == 1){

switch(evt.getType()){

case MOVE:

System.out.println(“event2move”);

break;

case UP:

System.out.println(“event2up”);

break;

}

}

I’ll test that, but i noticed some strangeness tow with the way android handles multi contact inputs.

I’m afraid i won’t be able to change this though because that’s what android is sending…



I’ll look into maybe there is an issue on our side.

Hello, First post so be gentle.



@nehon



Are you passing the pointerIndex through the MotionEvent.getPointerId()?

yes, I do.

but maybe something is messed up



the code for this is in Android input in the onTouchEvent method

http://code.google.com/p/jmonkeyengine/source/browse/trunk/engine/src/android/com/jme3/input/android/AndroidInput.java

On the TouchEvent method for the switch. I think you also have to add the MotionEvent.ACTION_MASK.



event.getAction() & MotionEvent.ACTION_MASK



This masks the pointer index out and gives you the pure event type.

@nehon



I forgot to mention that ACTION_POINTER_1_DOWN, ACTION_POINTER_2_DOWN, ACTION_POINTER_3_DOWN constants are deprecated.



Take a look at this code, it might help you out.



float[] x = new float[10];

float[] y = new float[10];



public boolean onTouch(View v, MotionEvent event) {



int action = event.getAction() & MotionEvent.ACTION_MASK;

int pointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >>

MotionEvent.ACTION_POINTER_INDEX_SHIFT;

int pointerId = event.getPointerId(pointerIndex);



switch (action) {

case MotionEvent.ACTION_DOWN:

case MotionEvent.ACTION_POINTER_DOWN:

x[pointerId] = (int)event.getX(pointerIndex);

y[pointerId] = (int)event.getY(pointerIndex);

break;



return true;

}

1 Like

mhh true…i guess they were not when this was done…

Thanks for the help, i’ll look into your code.

@nehon



this is what I did to the AndroidInput of the onTouchEvent method.



[java]

public boolean onTouchEvent(MotionEvent event) {

boolean bWasHandled = false;

TouchEvent touch;

// System.out.println("native : " + event.getAction());



// My own declaration for input

int action = event.getAction() & MotionEvent.ACTION_MASK;

int pointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >>

MotionEvent.ACTION_POINTER_INDEX_SHIFT;

int pointerId = event.getPointerId(pointerIndex);



// final int historySize = event.getHistorySize(); // This only applies to ACTION_MOVE events – all other actions will have a size of 0.

final int pointerCount = event.getPointerCount();



// switch (event.getAction()) {

switch (action) {



case MotionEvent.ACTION_DOWN:

case MotionEvent.ACTION_POINTER_DOWN:



// None of the bottom code will execute since historySize is 0.

// Only applies to the ACTION_MOVE

// if (!dontSendHistory) {

// // Process history

// for (int h = 0; h < historySize; h++) {

// // Convert all pointers into events

// for (int p = 0; p < pointerCount; p++) {

// touch = getNextFreeTouchEvent();

// touch.set(Type.DOWN, event.getHistoricalX(p, h), this.getHeight() - event.getHistoricalY(p, h), 0, 0);

// touch.setPointerId(event.getPointerId§);

// touch.setTime(event.getHistoricalEventTime(h));

// touch.setPressure(event.getHistoricalPressure(p, h));

// processEvent(touch);

// }

//

// }

// }







// only considering the primary pointer event. other pointers will have their own event

//for (int p = 0; p < pointerCount; p++) {

touch = getNextFreeTouchEvent();

// touch.set(Type.DOWN, event.getX(0), this.getHeight() - event.getY(0), 0, 0);

touch.set(Type.DOWN, event.getX(pointerIndex), this.getHeight() - event.getY(pointerIndex), 0, 0);

// touch.setPointerId(event.getPointerId(0));

touch.setPointerId(pointerId);

touch.setTime(event.getEventTime());

// touch.setPressure(event.getPressure(0));

touch.setPressure(event.getPressure(pointerIndex));

processEvent(touch);

//}



// used for testing

System.out.println(pointerId + " ACTION_DOWN with coordinates of X: " + event.getX(pointerIndex) +

"Y: " + (this.getHeight() - event.getY(pointerIndex)));



bWasHandled = true;

break;



case MotionEvent.ACTION_UP:

case MotionEvent.ACTION_POINTER_UP:

case MotionEvent.ACTION_CANCEL:



// None of the bottom code will execute since historySize is 0.

// Only applies to the ACTION_MOVE

// if (!dontSendHistory) {

// // Process history

// for (int h = 0; h < historySize; h++) {

// // Convert all pointers into events

// for (int p = 0; p < pointerCount; p++) {

// touch = getNextFreeTouchEvent();

// touch.set(Type.UP, event.getHistoricalX(p, h), this.getHeight() - event.getHistoricalY(p, h), 0, 0);

// touch.setPointerId(event.getPointerId§);

// touch.setTime(event.getHistoricalEventTime(h));

// touch.setPressure(event.getHistoricalPressure(p, h));

// processEvent(touch);

// }

//

// }

// }





// only considering the primary pointer event. other pointers will have their own event

//for (int p = 0; p < pointerCount; p++) {

touch = getNextFreeTouchEvent();

// touch.set(Type.UP, event.getX(0), this.getHeight() - event.getY(0), 0, 0);

touch.set(Type.UP, event.getX(pointerIndex), this.getHeight() - event.getY(pointerIndex), 0, 0);

// touch.setPointerId(event.getPointerId(0));

touch.setPointerId(pointerId);

touch.setTime(event.getEventTime());

touch.setPressure(event.getPressure(pointerIndex));

processEvent(touch);

//}



// used for testing

System.out.println(pointerId + " ACTION_UP with coordinates of X: " + event.getX(pointerIndex) +

"Y: " + (this.getHeight() - event.getY(pointerIndex)));



bWasHandled = true;

break;



case MotionEvent.ACTION_MOVE:



// if (!dontSendHistory) {

// // Process history

// for (int h = 0; h < historySize; h++) {

// // Convert all pointers into events

// for (int p = 0; p < pointerCount; p++) {

// Vector2f lastPos = lastPositions.get(event.getPointerId§);

// if (lastPos == null) {

// lastPos = new Vector2f(event.getHistoricalX(pointerIndex, h), this.getHeight() - event.getHistoricalY(p, h));

// lastPositions.put(event.getPointerId§, lastPos);

// }

//

// touch = getNextFreeTouchEvent();

// touch.set(Type.MOVE, event.getHistoricalX(p, h), this.getHeight() - event.getHistoricalY(p, h),

// event.getHistoricalX(p, h) - lastPos.x, this.getHeight() - event.getHistoricalY(p, h) - lastPos.y);

// touch.setPointerId(event.getPointerId§);

// touch.setTime(event.getHistoricalEventTime(h));

// touch.setPressure(event.getHistoricalPressure(p, h));

// processEvent(touch);

// lastPos.set(event.getHistoricalX(p, h), this.getHeight() - event.getHistoricalY(p, h));

// }

//

// }

// }

// if (event.getPointerCount() > 1) {

// System.out.println("MOVE : " + event.getPointerCount());

// }

// Convert all pointers into events

for (int p = 0; p < event.getPointerCount(); p++) {

// Vector2f lastPos = lastPositions.get(event.getPointerId§);

Vector2f lastPos = lastPositions.get(pointerIndex);

if (lastPos == null) {

// lastPos = new Vector2f(event.getX§, this.getHeight() - event.getY§);

lastPos = new Vector2f(event.getX(pointerIndex), this.getHeight() - event.getY(pointerIndex));

// lastPositions.put(event.getPointerId§, lastPos);

lastPositions.put(pointerId, lastPos);

}

touch = getNextFreeTouchEvent();

// touch.set(Type.MOVE, event.getX§, this.getHeight() - event.getY§, event.getX§ - lastPos.x, this.getHeight() - event.getY§ - lastPos.y);

touch.set(Type.MOVE, event.getX(pointerIndex), this.getHeight() - event.getY(pointerIndex), event.getX(pointerIndex) - lastPos.x, this.getHeight() - event.getY(pointerIndex) - lastPos.y);

touch.setPointerId(pointerId);

touch.setTime(event.getEventTime());

touch.setPressure(event.getPressure(pointerIndex));

processEvent(touch);

lastPos.set(event.getX(pointerIndex), this.getHeight() - event.getY(pointerIndex));





}



// used for testing

System.out.println(pointerId + " ACTION_MOVE with coordinates of X: " + event.getX(pointerIndex) +

"Y: " + (this.getHeight() - event.getY(pointerIndex)));



bWasHandled = true;

break;



// // TODO: implement motion events

// case MotionEvent.ACTION_POINTER_UP:

// break;

//

// case MotionEvent.ACTION_POINTER_DOWN:

// break;



// case MotionEvent.ACTION_OUTSIDE:

// break;

//

// case MotionEvent.ACTION_CANCEL:

// break;

}



// Try to detect gestures

this.detector.onTouchEvent(event);

this.scaledetector.onTouchEvent(event);



return bWasHandled;

}

[/java]



I didn’t use the historySize because they don’t work for the ACTION_DOWN or ACTION_UP since for those events they will become 0 and the for loop never executes. Can you test this out since I don’t do any svn stuff.

3 Likes

I’ve been testing the code and works nicely. The camera doesn’t jump to the next touch, but both fingers still register. As when the first finger hits, the camera responds to that. When you put another finger down, the camera will still keep responding to the first finger that was down, even if you move the other finger on the screen which is good, maybe. When you release the first finger, the ponterId that was 0 will transfer to the second finger making 0 and won’t cause the camera to jitter to the other location of the finger. You should probably comment out the System.out.println(), its just there for testing.

3 Likes

Nice!

Thanks for this

Tomorrow is my day off so I’ll test this

It’s working like a charm, it’s committed in last SVN



Thanks again.

2 Likes

I am not sure about this but I am never getting event.getPointerID() to be 1



OnTouch(Event event)



if(evt.getPointerId() == 0){

switch(evt.getType()){

case MOVE:

System.out.println(“event1move”);

break;

case UP:

System.out.println(“event1up”);

break;

}

}

if(evt.getPointerId() == 1){

switch(evt.getType()){

case MOVE:

System.out.println(“event2move”);

break;

case UP:

System.out.println(“event2up”);

break;

}

}

@funbox



In order to get pointerId() you have to pass the pointerIndex.



evt.getPointerId(pointerIndex)

@techsonic nope he talking about the JME event. there is one event per pointer.



@funbox that’s strange my testcase is almost the same as you and it works fine. Are you using the latest nightly?

I believe so. I just notice that when i put my first finger on the screen it register. When I put my second finger on the screen it doesn’t register. All movement is stuck to the first finger. As soon as I release the first finger. The second finger register finally registers.

My mistake. I reran the code and all is good. Sorry for the confusion

So i was playing around and I noticed that you can’t get any x,y input value for the second touch event. When the second finger is on the screen I noticed that it get a starting x/y value, however if you move the second finger around, the second fingers x/y value doesn’t change. It is only when the first finger is lifted that the x/y value of the second touch event is recorded and changed.



if(evt.getPointerId() == 0){

switch(evt.getType()){

case MOVE:

System.out.println("first finger x: "+evt.getX());

break;

case UP:

System.out.println(“event1up”);

break;

}

}

if(evt.getPointerId() == 1){

switch(evt.getType()){

case MOVE:

System.out.println(“second finger x:” +evt.getX());

break;

case UP:

System.out.println(“event2up”);

break;

}

}

I’ll look into it

@nehon



I have the same problem as @funbox in multitouch events.

When I try to ‘listen’ the events generated by the second finger the only event that I can ‘listen’ is DOWN, after it I move the second finger and I only get events (MOVE or SCALE_MOVE) from the first finger and I can’t manage when I move the second finger.



Why it happens? Is there any possible solution?



Cheers!