Tuesday, September 6, 2011

[android-developers] MultiTouch Implementation Below Froyo

I managed to implement multitouch feature for the project I'm
currently porting to Android at our Studio. I followed the exquisite
example written by Tim Bray on the Android Dev Blogspot (http://
android-developers.blogspot.com/2010/06/making-sense-of-
multitouch.html
) and adapted the code to our needs (there was no need
to add the Gesture Detector section and had to call native engine
methods). Everything worked marvelously once I booted the application.
Then I went back to the section I just coded in order to make sure the
project would be compatible with API level 6. I found myself a little
bit puzzled once I found out that the technique used to get the
pointer index through the action flags
(MotionEvent.ACTION_POINTER_INDEX_MASK >>
MotionEvent.ACTION_POINTER_INDEX_SHIFT) hadn't been implemented until
Froyo.

What troubles me is that I verify Multitouch features through:
aPackageManager.hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH);
which was implemented in API 7, but I am unable to correctly track the
multiple pointer IDs unless the application runs on API8++.

There was even another post relating to reflection and backward
compatibility using the very same touchexample project (http://android-
developers.blogspot.com/2010/07/how-to-have-your-cupcake-and-eat-it-
too.html
) in which those methods were kept inside the Eclair specific
code:

private static class EclairDetector extends CupcakeDetector {
private static final int INVALID_POINTER_ID = -1;
private int mActivePointerId = INVALID_POINTER_ID;
private int mActivePointerIndex = 0;

@Override
float getActiveX(MotionEvent ev) {
return ev.getX(mActivePointerIndex);
}

@Override
float getActiveY(MotionEvent ev) {
return ev.getY(mActivePointerIndex);
}

@Override
public boolean onTouchEvent(MotionEvent ev) {
final int action = ev.getAction();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
mActivePointerId = ev.getPointerId(0);
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
mActivePointerId = INVALID_POINTER_ID;
break;
case MotionEvent.ACTION_POINTER_UP:
final int pointerIndex = (ev.getAction() &
MotionEvent.ACTION_POINTER_INDEX_MASK)
>> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
final int pointerId = ev.getPointerId(pointerIndex);
if (pointerId == mActivePointerId) {
// This was our active pointer going up. Choose a new
// active pointer and adjust accordingly.
final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
mActivePointerId = ev.getPointerId(newPointerIndex);
mLastTouchX = ev.getX(newPointerIndex);
mLastTouchY = ev.getY(newPointerIndex);
}
break;
}

mActivePointerIndex = ev.findPointerIndex(mActivePointerId);
return super.onTouchEvent(ev);
}
}

I am now wondering if the Index Mask methods will be compatible
running on Eclair or do I have to find a workaround (I have no clue
how I could track the pointers without these) or even if I would need
to raise the bar on multitouch support so that only users using Froyo
or higher will be getting the goods?

Thank you!
Any help will be greatly appreciated!

Below is my current code snippet:
@Override public boolean onTouchEvent(MotionEvent event)
{
boolean result = false;

final int action = event.getAction();
switch (action & MotionEvent.ACTION_MASK)
{
case MotionEvent.ACTION_DOWN:
{
if(mCanMultitouch)
{
// Save the ID of this pointer for later ACTION_POINTER_UP
comparison
mPrimaryPointerId = event.getPointerId(0);
/*NativeTouchDownFunction*/(mPrimaryPointerId,
(int)event.getY(), (int)event.getX());
}
else
{
/*NativeTouchDownFunction*/(0, (int)event.getRawY(),
(int)event.getRawX());
}

result = true;
break;
}

case MotionEvent.ACTION_MOVE:
{
if(mCanMultitouch)
{
// Find the index of the active pointer and fetch its
position
final int pointerIndex =
event.findPointerIndex(mPrimaryPointerId);
/*NativeTouchMoveFunction*/(mPrimaryPointerId,
(int)event.getY(pointerIndex), (int)event.getX(pointerIndex));
}
else
{
/*NativeTouchMoveFunction*/(0, (int)event.getRawY(),
(int)event.getRawX());
}

result = true;
break;
}

case MotionEvent.ACTION_UP:
{
if(mCanMultitouch)
{
// Find the index of the active pointer and fetch its
position
final int pointerIndex =
event.findPointerIndex(mPrimaryPointerId);
/*NativeTouchUpFunction*/(mPrimaryPointerId,
(int)event.getY(pointerIndex), (int)event.getX(pointerIndex));
mPrimaryPointerId = INVALID_POINTER_ID;
}
else
{
/*NativeTouchUpFunction*/(0, (int)event.getRawY(),
(int)event.getRawX());
}

result = true;
break;
}

case MotionEvent.ACTION_CANCEL:
{
if(mCanMultitouch)
{
// Find the index of the active pointer and fetch its
position
final int pointerIndex =
event.findPointerIndex(mPrimaryPointerId);
/*NativeTouchUpFunction*/(mPrimaryPointerId,
(int)event.getY(pointerIndex), (int)event.getX(pointerIndex));
mPrimaryPointerId = INVALID_POINTER_ID;
}
else
{
/*NativeTouchUpFunction*/(0, (int)event.getRawY(),
(int)event.getRawX());
}

result = true;
break;
}

// Event only called when a pointer is already present and
the view catches an additional pointer.
case MotionEvent.ACTION_POINTER_DOWN:
{
// Extract the index of the pointer that entered the
touch sensor
final int pointerIndex = (action &
MotionEvent.ACTION_POINTER_INDEX_MASK)
>> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
final int pointerId = event.getPointerId(pointerIndex);
/*NativeTouchDownFunction*/(pointerId,
(int)event.getY(pointerId), (int)event.getX(pointerId));

result = true;
break;
}

// Event only called when multiple pointer are detected and
only one of them is released while the others remain on-screen.
case MotionEvent.ACTION_POINTER_UP:
{
// Extract the index of the pointer that left the touch
sensor
final int pointerIndex = (action &
MotionEvent.ACTION_POINTER_INDEX_MASK)
>> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
final int pointerId = event.getPointerId(pointerIndex);
if (pointerId == mPrimaryPointerId)
{
// This was our active pointer going up.
/*NativeTouchUpFunction*/(mPrimaryPointerId,
(int)event.getY(pointerId), (int)event.getX(pointerId));
//Choose a new active pointer and adjust accordingly.
final int newPointerIndex = pointerIndex == 0 ? 1 :
0;
mPrimaryPointerId =
event.getPointerId(newPointerIndex);
}
else
{
/*NativeTouchUpFunction*/(pointerId,
(int)event.getY(pointerId), (int)event.getX(pointerId));
}

result = true;
break;
}
}

return result;
}

--
You received this message because you are subscribed to the Google
Groups "Android Developers" group.
To post to this group, send email to android-developers@googlegroups.com
To unsubscribe from this group, send email to
android-developers+unsubscribe@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/android-developers?hl=en

No comments:

Post a Comment