Archive for January, 2009

Updating UI items from multiple threads

So I recently found out that only one thread is ever allowed to update things like ImageViews and whatnot. If you try to make another thread update them, everything breaks, and all of a sudden, that ImageView never ever updates again. This was causing trouble in RemoteDroid because I wanted the tap to click to update the on-screen button state, but the tap events had to come from a Timer object, which put everything in a different thread.

Anyway, if you need to make a graphical change from another thread, the solution is to use the Handler class. The you use it is by creating a handler in your UI thread, then create some Runnable objects that call all your graphics methods. Here’s an example:

public class Thing extends Activity {
        private Handler = new Handler();
        // runnables for updating state
        private runnable buttonOn = new Runnable() {
                private void run() {
                        drawImageOn();
                }
        };
        private runnable buttonOff = new Runnable() {
                private void run() {
                        drawImageOff();
                }
        };
        // our ImageView
        private ImageView iv;
        // a Timer object, which creates another thread for doing something else
        private Timer timer = new Timer();

        public void onCreate(Bundle savedInstanceState) {
                // set ref to ImageView
                this.iv = (ImageView)this.findViewById(R.id.whatever);
                //
                this.handler.post(this.buttonOn);
                // Let's start up another thread for whatever reason
                this.timer.scheduleAtFixedRate(new TimerTask() {
                        public void run() {
                                // If we tried to call drawImageOff() here directly, something would break.
                                handler.post(buttonOff);
                        }
                }, 0, 500);
        }

        // drawing methods

        public void drawImageOn() {
                // draw in our ImageView here
                ...
        }

        public void drawImageOff() {
                // draw in our ImageView here
                ...
        }
}

So basically, you’re setting up a few Runnables for changing graphical state, and then making every thread use the Handler for changing between those states.

Comments off

Windows 7 support?

Recently spotted on Twitter: a RemoteDroid user’s report that I believe was about the server app working fine on Windows 7, which is still in beta. The tweet was in German, so I’m guessing to some degree on the meaning. But if anyone out there has RemoteDroid up and running on Windows 7 — and can post a comment or drop me a line in English to confirm — that would be great, thanks.

Comments off

Version 1.2 available now

Hi, Josh’s web site co-collaborator here to let you know about the latest version of RemoteDroid, released today. A number of new features were added, many in response to user feedback:

  • Tap to click
    The onscreen touchpad is now clickable: tap once for a single click, twice for a double click, and tap and hold to select text or click-and-drag.
  • Trackball scroll wheel
    Clicking on the scroll wheel toggles it from a mouse controller to a scroll wheel. Click again to toggle it back.
  • Customizable mouse sensitivity
    New user preferences menu lets you set the sensitivity of your mouse and tap speed.
  • Saved IP address
    You no longer have to enter your IP address every time you launch the application. RemoteDroid saves the IP address of your last network used — just tap on “Connect” to begin.

The Preferences menu, mentioned above, also lets you choose which new features you want to globally turn on or off, such as tap to click or using your trackball as a scroll wheel.

The new version of RemoteDroid has been tested with computers running Mac OS (Tiger, Leopard), Windows (XP, Vista) and Linux. However, there will inevitably be certain operating system and computer configuration combos that will, for one reason or another, work less than perfectly. If you’ve read the Support page and have ruled out all of the possible reasons for the app not working listed there, you can report an issue using the contact form.

As always, the phone app is available in the Android Marketplace. And make sure you also have the latest version (1.2) of the server app, available here.

Comments off

Loading images from jar files in windows

In the first version of the server app, I was telling Windows users to start the program by clicking on a .bat file because for some reason, the thing wasn’t starting up when clicking on the .jar file directly.

I’d seen other apps that did start from the jar file just fine, and after a lot of hunting around, I figured out why. When you search google for load image from jar file, most of the hits have you getting a URL object like this:

URL url = this.getClass().getResource("the_image.jpg");

then using that to load the actual image.

This works just fine on OSX, and Linux, but does absolutely nothing on Windows for some reason. Instead, on Windows, you have to get a JarFile object for the jar you’re loading from, then get a ZipEntry (JarEntry, whatever) from that, then get an InputStream from the ZipEntry, turn that into a BufferedInputStream, then pass that off to your default Toolkit to turn into an image.

Of course this means you have to figure out whether the app’s being run in a jar file in the first place or not. This is how I’m doing it:

URL fileURL = this.getClass().getProtectionDomain().getCodeSource().getLocation();
String sBase = fileURL.toString();
if ("jar".equals(sBase.substring(sBase.length()-3, sBase.length()))) {
	MyClass.jar = new JarFile(new File(fileURL.toURI()));
}

after that, you can load the image ike this:

Image imReturn = null;
try {
	if (jar == null) {
		// This is used if you're not loading from a jar
		imReturn = this.toolkit.createImage(this.getClass().getClassLoader().getResource(sImage));
	} else {
		//
		BufferedInputStream bis = new BufferedInputStream(jar.getInputStream(jar.getEntry(sImage)));
		ByteArrayOutputStream buffer=new ByteArrayOutputStream(4096);
		int b;
		while((b=bis.read())!=-1) {
			buffer.write(b);
		}
		byte[] imageBuffer=buffer.toByteArray();
		imReturn = this.toolkit.createImage(imageBuffer);
		bis.close();
		buffer.close();
	}
} catch (IOException ex) {

}
return imReturn;

Comments off

Related Links

Resource Links