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 static final int MAX_TOUCHPOINTS = 10;
private static final String START_TEXT = "Touch Anywhere To Test";

private Paint textPaint = new Paint();
private Paint touchPaints[] = new Paint[MAX_TOUCHPOINTS];
private int colors[] = new int[MAX_TOUCHPOINTS];

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);
colors[0] = Color.BLUE;
colors[1] = Color.RED;
colors[2] = Color.GREEN;
colors[3] = Color.YELLOW;
colors[4] = Color.CYAN;
colors[5] = Color.MAGENTA;
colors[6] = Color.DKGRAY;
colors[7] = Color.WHITE;
colors[8] = Color.LTGRAY;
colors[9] = Color.GRAY;
for (int i = 0; i < MAX_TOUCHPOINTS; i++) {
touchPaints[i] = new Paint();
touchPaints[i].setColor(colors[i]);
}
}

@Override
public boolean onTouchEvent(MotionEvent event) {
int pointerCount = event.getPointerCount();
if (pointerCount > MAX_TOUCHPOINTS) {
pointerCount = MAX_TOUCHPOINTS;
}
Canvas c = getHolder().lockCanvas();
if (c != null) {
c.drawColor(Color.BLACK);
if (event.getAction() == MotionEvent.ACTION_UP) {
// clear everything
} else {
// draw crosshairs first then circles as a second pass
for (int i = 0; i < pointerCount; i++) {
int id = event.getPointerId(i);
int x = (int) event.getX(i);
int y = (int) event.getY(i);
drawCrosshairsAndText(x, y, touchPaints[id], i, id, c);
}
for (int i = 0; i < pointerCount; i++) {
int id = event.getPointerId(i);
int x = (int) event.getX(i);
int y = (int) event.getY(i);
drawCircle(x, y, touchPaints[id], c);
}
}
getHolder().unlockCanvasAndPost(c);
}
return true;
}

private void drawCrosshairsAndText(int x, int y, Paint paint, int ptr, int id, Canvas c) {
c.drawLine(0, y, width, y, paint);
c.drawLine(x, 0, x, height, paint);
int textY = (int)((15 + 20 * ptr) * scale);
c.drawText("x" + ptr + "=" + x, 10 * scale, textY, textPaint);
c.drawText("y" + ptr + "=" + y, 70 * scale, textY, textPaint);
c.drawText("id" + ptr + "=" + id, width - 55 * scale, textY, textPaint);
}

private void drawCircle(int x, int y, Paint paint, Canvas c) {
c.drawCircle(x, y, 40 * scale, paint);
}

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);
Canvas c = getHolder().lockCanvas();
if (c != null) {
// clear screen
c.drawColor(Color.BLACK);
float tWidth = textPaint.measureText(START_TEXT);
c.drawText(START_TEXT, width / 2 - tWidth / 2, height / 2, textPaint);
getHolder().unlockCanvasAndPost(c);
}
}

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.

Updated on 8/25/2010 for v1.2 which supports up to 10 touch points.

18 Comments

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

It took me 10 minutes.

Good! It works great! 5 mins are spent on making code indent correct.

on HTC Desire Z ,I run this

on HTC Desire Z ,I run this app normally, no problem ~

How we can do with this with images

hello thanks for this tutorial.
I want to draw images in my sample app , and then want to move individual image. So how can i get Id of individual and how to retain images once if they draw.

thanking you lot

great

great

I don't believe there is a

I don't believe there is a defined maximum, though I think 10 is the current hardware limit on devices and is the limit BatteryTech uses.

thanks! just what i was

thanks! just what i was looking for.I get only up to 3 pointers by that (Android 2.2), is that the maximum?

Yeah that's fine with me.

Yeah that's fine with me.

Use in Android-x86 Distribution

Would there be any objection to including this in an Android distribution (android-x86 to be specific) so users could give us feeback as to whether or not multi-touch is working on their device?

samsung GT-I5500

can it use in samsung GT-I5500?

Those are known hardware

Those are known hardware limitations of those devices.

There 's a problem in it. I

There 's a problem in it. I run this app normally, I touched the screen with my left hand,and the other hand touched also actually regular.but when one's x or one's y moved nearby the other, the other's x or y is changed by moving hand seriously,It make our game or app not work normal.for example one hand decide the direction ,the other fire, the fire's hand will change the direction of sprite,probablely I died for this terrible problem. my phone is desire,and i also teing it in lenged moto millstone and N1.

HTC Desire incorrect multitouch behaviour

Hi,
thanks for code, I have same issue with my HTC desire, I haven't used your app, I have noticed that when I was doing AIR for Android application and noticed this awkwardness, then I have installed your app and experienced the same. I have googled on this and video showing htc desire vs Samsung Galaxy S was enough to blame hardware. I have wrote to HTC support and had response that if factory reset will not resolve it I can send it back so they can check it and fix it:)

I am currently using HTC

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

Have you any report about

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

I don't believe so. It's

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

Blame?

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

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.

Download

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