Source Code to Multitouch Visible Test

After having the story about the Nexus One multitouch flaws spread all over the blogosphere and seeing so many comments blame my test app for the problems, I figured the best way to kill that whole idea would be to just publish the source code so that people, even non-technical, can see that I do absolutely no manipulation of the touch points. All this code does is figure out what color the touch point is based on the ID and then draws those points exactly where the touch API tells it they are.

This is also a good bit of source to start with if you're interested in developing multitouch code. I still am using multitouch in the new games. This test simply allows me to understand the limitations before I design the control system. Read on for the code.

This is an extremely small and simple app. It only needs two classes which are the Activity and the View.

Here's the Activity, filename MTVisTestActivity.java

import android.app.Activity;
import android.os.Bundle;
import android.view.Window;
import android.view.WindowManager;

public class MTVisTestActivity extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(new MTView(this));
}
}

And now the view, filename MTView.java

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class MTView extends SurfaceView implements SurfaceHolder.Callback {
private static final String TAG = "MTView";

private Paint textPaint = new Paint();
private Paint touch1Paint = new Paint();
private Paint touch2Paint = new Paint();
private int width, height;
private float scale = 1.0f;

public MTView(Context context) {
super(context);
SurfaceHolder holder = getHolder();
holder.addCallback(this);
setFocusable(true); // make sure we get key events
setFocusableInTouchMode(true); // make sure we get touch events
init();
}

private void init() {
textPaint.setColor(Color.WHITE);
touch1Paint.setColor(Color.BLUE);
touch2Paint.setColor(Color.RED);
}

@Override
public boolean onTouchEvent(MotionEvent event) {
int pointerCount = event.getPointerCount();
int id1 = -1;
int id2 = -1;
if (event.getAction() == MotionEvent.ACTION_UP) {
draw(0, 0, 0, 0, false, false, id1, id2);
} else {
id1 = event.getPointerId(0);
if (pointerCount == 1) {
draw((int) (event.getX(0)), (int) (event.getY(0)), 0, 0, true, false, id1, id2);
} else if (pointerCount == 2) {
id2 = event.getPointerId(1);
draw((int) (event.getX(0)), (int) (event.getY(0)), (int) (event.getX(1)), (int) (event.getY(1)),
true, true, id1, id2);
}
}
// don't allow more than 60 motion events per second
try {
Thread.sleep(16);
} catch (InterruptedException e) {
}
return true;
}

public void draw(int x1, int y1, int x2, int y2, boolean draw1, boolean draw2, int id1, int id2) {
Canvas c = getHolder().lockCanvas();
if (c != null) {
// draw
c.drawColor(Color.BLACK);
Paint paint1 = null;
Paint paint2 = null;
if (id1 == 1) {
paint1 = touch2Paint;
paint2 = touch1Paint;
} else {
paint1 = touch1Paint;
paint2 = touch2Paint;
}
if (draw1) {
c.drawCircle(x1, y1, 48, paint1);
c.drawLine(0, y1, width, y1, paint1);
c.drawLine(x1, 0, x1, height, paint1);
}
if (draw2) {
c.drawCircle(x2, y2, 48, paint2);
c.drawLine(0, y2, width, y2, paint2);
c.drawLine(x2, 0, x2, height, paint2);
}
int yPos1 = (int) (15 * scale);
int yPos2 = (int) (35 * scale);
c.drawText("x1=" + (draw1 ? x1 : ""), 10 * scale, yPos1, textPaint);
c.drawText("y1=" + (draw1 ? y1 : ""), 70 * scale, yPos1, textPaint);
c.drawText("x2=" + (draw2 ? x2 : ""), 10 * scale, yPos2, textPaint);
c.drawText("y2=" + (draw2 ? y2 : ""), 70 * scale, yPos2, textPaint);
c.drawText("id1=" + (id1 > -1 ? id1 : ""), width - 55 * scale, yPos1, textPaint);
c.drawText("id2=" + (id2 > -1 ? id2 : ""), width - 55 * scale, yPos2, textPaint);
getHolder().unlockCanvasAndPost(c);
}
}

public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
this.width = width;
this.height = height;
if (width > height) {
this.scale = width / 480f;
} else {
this.scale = height / 480f;
}
textPaint.setTextSize(14 * scale);
draw(0,0,0,0,false,false,-1,-1);
}

public void surfaceCreated(SurfaceHolder holder) {
}

public void surfaceDestroyed(SurfaceHolder holder) {
}
}

And there you have it. That's all there is. Notice in the draw method that the circles are drawn exactly where x1,y1 and x2,y2 are. Those values come directly from event.getX(0), event.getY(0) and event.getX(1), event.getY(1) respectively. No sides were swapped, no values were modified. I couldn't have done anything to the data. It's exactly what the Android motion events give you.

Go ahead and write your own test if you like, but this should prove that it's real and is not a programming error on my part.

6 Comments

Post a comment here or discuss this and other topics in the forums

Have you any report about

Have you any report about some other phone models affected by the same problem ? (HTC Desire ?)

I am currently using HTC

I am currently using HTC Desire and it has the same problem.
Synaptics' "multitouch" is basically crap.

Blame?

Were google engineers among the people that blamed the problem on your code?

I don't believe so. It's

I don't believe so. It's usually non-developers that make such assumptions :)

Download

Can link the Eclipse project for download.
Really good post and I like how you've approached to the problem!
Oliver

Just create a new project,

Just create a new project, put these two files in the source tree and put the activity as the launcher activity. It should take all of 5 minutes to do.

Post new comment

The content of this field is kept private and will not be shown publicly.
Add image
  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd> <table> <tr> <td>
  • Lines and paragraphs break automatically.
  • Images can be added to this post.
  • Image links with 'rel="lightbox"' in the <a> tag will appear in a Lightbox when clicked on.
  • Image links with 'rel="lightshow"' in the <a> tag will appear in a Lightbox slideshow when clicked on.
  • Links to HTML content with 'rel="lightframe"' in the <a> tag will appear in a Lightbox when clicked on.
  • Links to video content with 'rel="lightvideo"' in the <a> tag will appear in a Lightbox when clicked on.
  • Links to inline or modal content with 'rel="lightmodal"' in the <a> tag will appear in a Lightbox when clicked on.

More information about formatting options

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.