How To: Identify which pointer moved in Android

Heads up: When I say pointer in this article, I mean a finger or a stylus – anything in contact with the touchscreen.

Multi-touch in Android is a bit of a strange beast, although you could argue that it’s by necessity. With ACTION_DOWN (primary pointer in contact) or ACTION_POINTER_DOWN (non-primary pointer in contact), or even the corresponding ACTION_UP or ACTION_POINTER_UP events it’s fine. But when it comes to ACTION_MOVE events – you can’t just ask which finger moved, all you’re told is that A finger moved, and you have to figure out which one for yourself as far as I can tell.

This is what I’ve come up with to figure out which pointer has actually moved – it falls behind by one ACTION_MOVE event because there doesn’t seem to be a way to ask for the current X location of a pointer by its index location (that is, there’s no getX(pointerIndex), only a getHistoricalX(pointerIndex, historyPosition)) – so this code misses the very first move event when a pointer moves – BUT – moving your finger generates a large number of move events, so I’m thinking this might not be that much of an issue.

Update: I’d forgotten you can getX(pointerIndex) and as well as getHistoricalX(pointerIndex, historyLocation) – so I’ve modified the code below to do that, which means I don’t think we miss any move events anymore because we’re now comparing current location to historical(0) rather than historical(0) to historical(1).

Anyway, here’s my code which addresses this finicky problem:

@Override
public boolean onTouchEvent(MotionEvent event)
{
    // Get pointer index from the event object...
    int pointerIndex = event.getActionIndex();
 
    // ...which we can use to find the pointer ID!
    int pointerId = event.getPointerId(pointerIndex);
 
    // Get masked action (i.e. action which is not specific to a pointer)
    int maskedAction = event.getActionMasked();
 
    // Depending on what the action was, act appropriately...
    switch (maskedAction)
    {
        case MotionEvent.ACTION_DOWN:
            System.out.println("Pointer with Id: " + pointerId + " at index " + pointerIndex + " down i.e. Primary pointer down!");
            break;
 
        case MotionEvent.ACTION_POINTER_DOWN:
            System.out.println("Pointer with Id: " + pointerId + " at index " + pointerIndex + " down i.e. NON-Primary pointer down!");
            break;
 
        // The MOVE event must be done separately. This is because the ACTION_MOVE event always gets
        // zero as the pointerId for some strange reason. As such, you always have to loop over the
        // pointer indexes and compare positions to the pointer's previous (i.e. historical) position.
        case MotionEvent.ACTION_MOVE:
            int pointerCount = event.getPointerCount();
            for(int i = 0; i < pointerCount; ++i)
            {
                // To find out WHICH pointer moved we must compare pointer historical locations
                if (event.getHistorySize() > 0)
                {
                    // X or Y location for that pointer index moved?
                    // Corner-case: Pointer index changed (pointer up or down promoted or demoted pointer index while moving?) 
                    // Fix: Track by pointer Id via sparse array as outlined in second potential solution below.
                    if ( (int)event.getX(i) != (int)event.getHistoricalX(i,0) || (int)event.getY(i) != (int)event.getHistoricalY(i, 0) )
                    {
                        pointerId = event.getPointerId(i);
                        System.out.println("Pointer with Id: " + pointerId + " at index " + i + " moved!");
                    }
                }
            }
            break;
 
        case MotionEvent.ACTION_UP:
            System.out.println("Pointer with Id: " + pointerId + " at index " + pointerIndex + " up i.e. Primary pointer up!");
            break;
 
        case MotionEvent.ACTION_POINTER_UP:
            System.out.println("Pointer with Id: " + pointerId + " at index " + pointerIndex + " up i.e. NON-Primary pointer up!");
            break;
    }
 
    // Consume the event so that it is not processed any further
    return true;
 
} // End of onTouchEvent method

As I’ve been playing around with this it seems to match up with what I’m doing very well without issue, for example:

Pointer with Id: 0 at index 0 down i.e. Primary pointer down!      <--- 1st finger down
Pointer with Id: 0 at index 0 moved!                               <--- 1st finger moved
Pointer with Id: 0 at index 0 moved!
...
Pointer with Id: 1 at index 1 down i.e. NON-Primary pointer down!  <--- 2nd finger down
Pointer with Id: 1 at index 1 moved!                               <--- 2nd finger moved
Pointer with Id: 1 at index 1 moved!
...
Pointer with Id: 0 at index 0 up i.e. NON-Primary pointer up!      <--- 1st finger up, promoting 2nd finger to primary!
Pointer with Id: 1 at index 0 moved!                               <--- 2nd finger (now primary) moved
Pointer with Id: 1 at index 0 moved!
...
Pointer with Id: 0 at index 0 down i.e. NON-Primary pointer down!  <--- 1st finger back down, DEMOTING 2nd finger from primary!
Pointer with Id: 0 at index 0 moved!                               <--- 1st finger moved
...
Pointer with Id: 1 at index 1 up i.e. NON-Primary pointer up!      <--- 2nd finger up
Pointer with Id: 0 at index 0 up i.e. Primary pointer up!          <--- 1st finger up

Alternative Solution

Keep a sparse array of whatever you're keeping track of - for example, let's say we've got the world's simplest Circle class:

public class Circle
{
	public float x;
	public float y;
}

Then in your class handling the onTouchEvent() method you could have something like this (in this particular example we have a view which responds to multi-touch events):

// imports here...
 
public class MultiTouchCircleView extends View
{
    private static final int SIZE = 150;
 
    // Note: A SparseArray is kind-of like a hash-map, it maps an
    // integer to an object in a key/value manner.
    private SparseArray<Circle> circleArray;
 
    private Paint paint;
 
    // Array of 10 colours
    private int[] colours = {Color.BLUE, Color.GREEN, Color.RED,
            Color.YELLOW, Color.CYAN, Color.GRAY, Color.MAGENTA, Color.DKGRAY,
            Color.LTGRAY, Color.YELLOW};
 
    private Paint textPaint;
 
    public MultiTouchCircleView(Context context, AttributeSet attrs)
    {
        super(context, attrs);
        initView();
    }
 
    private void initView()
    {
        circleArray = new SparseArray<Circle>();
        paint = new Paint(Paint.ANTI_ALIAS_FLAG);
 
        // set painter color to a color you like
        paint.setColor(Color.BLUE);
        paint.setStyle(Paint.Style.FILL_AND_STROKE);
        textPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        textPaint.setTextSize(50);
        textPaint.setColor(Color.BLUE);
    }
 
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        // Get pointer index from the event object
        int pointerIndex = event.getActionIndex();
 
        // Get pointer ID
        int pointerId = event.getPointerId(pointerIndex);
 
        // Get masked (not specific to a pointer) action
        int maskedAction = event.getActionMasked();
 
        switch (maskedAction) {
 
            case MotionEvent.ACTION_DOWN:
            case MotionEvent.ACTION_POINTER_DOWN: {
                // A finger has touched the screen - so let's create a new
                // Circle at the touch location and add it to the list!
                Circle circle = new Circle();
                circle.x = event.getX(pointerIndex);
                circle.y = event.getY(pointerIndex);
                circleArray.put(pointerId, circle);
                break;
            }
 
            case MotionEvent.ACTION_MOVE: {
                // How many pointers are in contact with the screen?
                int pointerCount = event.getPointerCount();
 
                // Loop over them all...
                for (int i = 0; i < pointerCount; ++i) {
                    // i is the pointer index but we'll update our pointerIndex variable for clarity
                    pointerIndex = i;
 
                    // Get the pointerId at that pointerIndex (pointerId never changes while pointer is in contact)
                    pointerId = event.getPointerId(pointerIndex);
 
                    // Get access to the circle with that pointer id...
                    Circle circle = circleArray.get(pointerId);
 
                    // ...and update the circle's location.
                    circle.x = event.getX(pointerIndex);
                    circle.y = event.getY(pointerIndex);
                }
                break;
            }
 
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_POINTER_UP: {
                // Finger has left contact - remove the circle from the array
                circleArray.remove(pointerId);
                break;
            }
 
        } // End of switch block
 
        // Trigger redraw to display any changes
        invalidate();
 
        // Return true to consume the MotionEvent
        return true;
    }
 
    @Override
    protected void onDraw(Canvas canvas)
    {
        super.onDraw(canvas);
 
        // Draw circles for each pointer in contact with the screen
        int numCircles = circleArray.size();
        for (int i = 0; i < numCircles; ++i)
        {
            // Get the circle at index location i
            Circle circle = circleArray.valueAt(i);
 
            // ...to choose a colour between 0 and 9.
            if (circle != null)
            {
                int colourNum = i % 9;
                paint.setColor(colours[colourNum]);
                paint.setColor(colours[i]);
            }
 
            // Draw a circle in our chosen colour
            canvas.drawCircle(circle.x, circle.y, SIZE, paint);
        }
        canvas.drawText("Total pointers: " + numCircles, 10, 100, textPaint);
 
    } // End of onDraw method
 
} // End of MultiTouchCircleView class

This technique doesn't miss any ACTION_MOVE events because it looks them up by pointerId in the SparseArray - which is potentially a nicer solution. Go with whatever works for you.

How To: Mount a Google Nexus 7 Tablet in Linux

In Windows you can just plug it in and it’ll mount and recognise just fine, but in Linux you need to work a little bit harder. Not that much harder, though – it’s a 4-step….

1 – Get the right tools

sudo apt-get install mtp-tools mtpfs

2 – Set up a udev rule to do the right thing on connection

gksu gedit /etc/udev/rules.d/51-android.rules

Add this text to the rule to specify the Vendor ID, Product ID and user who has access to the device (change YOUR-USERNAME-HERE to your username, obv):

SUBSYSTEM=="usb", ATTR{idVendor}=="04e8", ATTR{idProduct}=="6860", MODE="0666", OWNER="YOUR-USERNAME-HERE"

3 – Restart udev and set up a mount point

sudo service udev restart
sudo mkdir /media/Nexus7
sudo chmod a+rwx /media/Nexus7

4 – Hook it up

Plug in your Nexus 7 and select MTP device as the connection type.

Then enter:

sudo mtpfs -o allow_other /media/Nexus7

At this point you should be able to read/write to your N7 via the /media/Nexus7 folder through any means you choose.

When you need to, just run either of the following for a clean dismount:

sudo umount /media/Nexus7

or

sudo umount mtpfs

Source: http://www.nexus7tablethelp.com/2012/07/connect-nexus-7-to-linux-via-mtp-using.html, I just paraphrased it a little to simplify.

Retrogaming on Android

A picture of the Android logo with a heart made from retro consolesI got a new phone the other day (a Samsung Galaxy S3), so I was looking for fun things to play on it when I found a great retrocollect article about emulation on Android which lists the best emulators for all of the different emulated systems.

As it happens, from July 14th 2012 to July 28th 2012 (or thereabouts) there’s a stack of free *oid emulators available – usually you have to pay for the full versions (which have save-game support), but for a while they’re free – so why not? =D

So if you’re interested, you can get all the apps in the list below for the low, low price of absolutely nothing before approx. July 28th 2012:

  • Ataroid (Atari 2600 emulator): here,
  • Gameboid (Gameboy Advance emulator): here,
  • GBCoid (Gameboy / Gameboy Colour emulator): here,
  • Gearoid (Sega Gamegear emulator): here,
  • Gensoid (Sega Genesis / Megadrive emulator): here,
  • NESoid (Nintendo Entertainment System emulator): here, and
  • SNESoid (Super Nintendo Entertainment System emulator): here.

Winning!

Also, if you’re interested in arcade emulation with MAME there’s the free MAME4droid applications:

  • MAME4droid – based on MAME 0.37b5: here, and
  • MAME4droid Reloaded – based on MAME 0.139: here

The difference in the above two version of MAME are in regard to what version of MAME they’re based on (as I’m sure you’ve worked out) – but what this actually means might be a little less clear.

In essence, the MAME 0.375b is based on a version of MAME circa July 2000 – and this version was chosen because it came before the big MAME re-write. This means that the 0.37b5 version of MAME was the pinnacle of MAME development when the developers chose speed over emulation accuracy. MAME4droid supports around 2000 ROMs, which will have to be the right format for the 0.37b5 version of MAME, and there’s no (and never will be any) save/load game support.

The MAME4droid Reloaded version on the other hand is based on 0.139 MAME code (circa 2010), which means that it’s a lot more accurate, but it’s either going to be slower or you’re going to need a beast of a phone to run many games at full tilt – we’re talking about having a dual-core phone as the minimum. MAME4droid Reloaded supports around 8000 ROMs, which this time you can save/load as your wish, and (again) these ROMs will have to be in the right format for the 0.139 version of MAME.

As you might expect, it’s horses for courses – need save/load support and got a high-end phone? Pick reloaded. Lower end phone or game-o-choice not running fast enough? Try out the original.

Emulators for other systems

The list of systems above is in no way exhaustive, as there are emulators for many other systems like the N64, Atari ST, MSX, PC Engine, Playstation – and even the Bandai Wonderswan! Quite!

For a fuller looking list of emulators available on Android, you could do worse than try this AndroidForums post. Just be aware that many emulators are paid apps, or you can get the “Lite” versions which either don’t allow you to save your games or come with a ton of bundled adware rubbish to pollute your phone.

Finding and converting ROMs

As ROMs for old game systems live in a somewhat grey area of the law as to whether they’re legal to download and use or not, you’re going to have to use Google to track them down on your own. Generally, the older the game, the less anyone’s going to care about you downloading and playing it.

Games for most old systems that work on any standard PC-based emulator should work exactly the same on the Android based emulators with no modification required.

The ROMs for arcade games on MAME though are a little bit different, as the version of MAME that you use will determine which version of the ROM you need. Thankfully, there’s only really two main options between the 0.37b5 versions and the more up-to-date versions, and in general you can use tools like clrmamepro to convert the ROMs between formats.

Input

Dreamcast Fishing Rod Fighting Game - Good luck!Trying to use a touchscreen as a joystick or gamepad is like trying to use a Dreamcast fishing rod to play a fighting game – and it’s a lot harder to pull off a Shoryuken with the fishing rod… So what can we do about it? As it turns out, quite a lot – if you want to use a controller, the chances are that you can get it to work with a suitable amount of research.

The Sony Xperia Play phones come with their own built-in joypad on the slide-down tray, but if you don’t have one of those, then you can still connect bluetooth devices like Nintendo wiimotes, PS3 Six-Axis controllers and XBox 360 controllers* as long as you have a compatible Bluetooth stack on your phone. Older Android phones (certainly my old HTC Desire) didn’t have a suitably functional bluetooth stack by default, but you could get one by installing a CyanogenMod ROM onto the phone. You could probably also use a mini/microUSB-to-USB connector for any standard USB joysticks or gamepads.

* = Wired Xbox 360 controllers aren’t a problem – wireless 360 controllers on the other hand require a USB-dongle, which itself needs drivers which may or may not be available for your device. In fact, from what I’ve seen, unless you have a Motorola Xoom tablet you could well be out of luck for wireless 360 connectivity.

Anyways, for those with a wish to play emulated games using a controller, try:

Output

A small screen is fine to play games on if that’s all you’ve got – but the chances are that you’ve probably got access to a big Plasma/LCD/LED screen with a HDMI input, too. So why not use that?


Skip to the 24 minute mark to see N64oid on the big screen with a wireless controller!

You’ll need an appropriate cable for your phone (usually some form of microUSB to HDMI converter), for the Samsung Galaxy S3 it’s what’s termed a MHL cable – which’ll set you back about $50AUD or thereabouts.

Also, with a phone to HDMI cable, you’ll be able to watch NBA.TV or any streaming video on the big screen from your phone instead of linking up to a laptop or whatever you currently do (unless you’ve got some kind of VNC/Mediaplayer solution up and running – in which case I envy you and would like to know what software you use!).

Honourable Mentions (other best-in-class free emulators)

Missing Sir Clive’s baby? Try the free Sinclair Spectrum emulator Marvin.
Yearning for Chucky Egg? Beebdroid to the rescue =P
Want to perform the woman’s move in IK+? (crouching punch to the nuts!) – try the Commodore 64 emulator Frodo.
Craving some Speedball or Xenon 2? There’s even an Android port of the Ubiquitous Amiga Emulator (UAE) called UAE4droid.

Come to think of it, I’m getting quite the hankering for some It Came From The Desert, or maybe Supercars II

Emulation – you’ve just gotta love it =D

Android dev is easy!

Well, kinda. It’s certainly easy to get up and running with the Android SDK and Eclipse with the ADT Plugin (Android Development Tools), as long as you know a little Java. Even the GUI stuff is all drag-and-drop, which I really quite liked.

To knock together a quick celcius<–>fahrenheit converter (from never having developed on Android to installing the tools and being up and running in less than an hour):

  1. Read this
  2. …which guides you step-by-step on how to build this:

    Android - App in Menu

    Android Temperature Conversion App

This has got me wondering how easy it is to integrate OpenGL ES into an Android app, which would be a pretty spiffy weekend project I reckon…