Wednesday, July 7, 2010

GWT 2.0 and 2.1 at I/O 2010

The release of GWT 2.1 M2 reminded me that I hadn't yet watched the GWT presentations from Google I/O 2010, so I queued them up and have been watching them while doing some incredibly dull (but unfortunately necessary) data mapping.

GWT Testing Best Practices was of immediate interest to me, as this is something that I've put a fair amount of mental effort into as well. This is a good introduction to testing in GWT and makes several of the points that I would have as well.

In a nutshell, GWTTestCase is comprehensive, but terribly slow, so emphasize junit tests. In order to emphasize unit tests, you must be able to isolate your code from the GWT framework, in particular widget classes that use JSNI and rely on the DOM. When attempting to isolate code for testability, dependency injection is important.

In addition to that, the presentation shows some useful examples of writing testable views, talks about testing asynchronous logic, and suggests a different mode of integrating with handlers than Ray Ryan did a year ago (setClickHandler() vs. getButton()).

Architecting GWT Applications for Production is another Ray Ryan presentation which is almost a sequel to his presentation from I/O 2009. It's mostly a rough-and-ready tour of some of the changes coming up in GWT 2.1 with respect to an application framework, as well as the benefits you should expect to get from that. There are some parts of the presentation that apply to changes in GWT 2.0 as well, so even if you're not ready to get into GWT 2.1, you may learn something useful from this.

GWT's UI Overhaul talks about a bunch of the UI changes in GWT 2.0 and 2.1, from high-level layout to cell rendering. Useful to get in touch with the changes already present in 2.0 and coming in 2.1.

Measure in Milliseconds: Meet Speed Tracer is another presentation that ties in heavily to a presentation from I/O 2009. This time around, it's mostly a quick intro to why performance matters (primarily to usability), what Speed Tracer is and how it works, and then delves into specific examples.

Happily, it's these examples that are the most interesting to me, so I think the emphasis of the presentation is just right. 'The Mysterious Case of Too Much Layout' is a good example of the dangers of premature optimization, and why it's important to use a profiling tool that works with the client-side of web application development as well.

It's interesting to see they've extended this from being a primarily client-side profiler to supporting integration with AppEngine and SpringSource TC for integration on the server-side.

Faster Apps Faster talks about how the GWT compiler makes your applications faster, and how to speed up the compilation process. I've read a fair bit about the GWT compiler, but this kind of detail is interesting for people who aren't aware of how much GWT optimizes Javascript, and why that might be faster than hand-written code.

The suggestions for speeding up the GWT compilation process are good: reducing permutations, draft compilation (GWT 2.0), RAID/SSD, 64-bit JVM, etc. Apparently GWT can do a distributed build as well, although it looks like setting it up will be a little work.

Architecting for Performance with GWT has two presenters, Joel Webber and Adam Schuck. Joel's on the GWT team, and has the builder-of-framework perspective, while Adam works on Wave, and approaches it from a user-of-framework perspective.

There are some interesting examples of enhancements the Wave Team has done, like skipping the selection script during initial startup and loading data in parallel with the Javascript, as well as generalized suggestions such as batching requests to avoid sending a series of serialized data requests, or structuring your RPC interface to deliver all the data you need in a single call.

There's also more talk about measuring performance on a continuous manner, which dovetails nicely with speed tracer's new headless capabilities.

GWT + HTML5 Can Do What?! didn't really teach me anything that I expect to use anytime soon, but it was nice to see the GWT Quake port in action, and hear a little more about it. I am interested to see what comes of GWT + HTML5, and the capabilities that adds.

GWT Linkers is fairly low-level for some of you. In essence, it's targeting building something other than a classical web application using GWT, like Chrome Extensions, HTML5 Web Workers, etc. If you want to build something like this, you'll want to learn about GWT linkers, how they work, what they do, and how to write your own.

Friday, June 25, 2010

Canadian iPad Data Plans Compared

I was curious about how the iPad plans that were announced by Bell, Rogers and Telus compared, where the break points were, and so forth. So I threw together a Google Spreadsheet to do some quick math on the plans. Assuming I read the plan rules correctly, I think the lessons are as follows:

  • Bell and Rogers have eerily similar plans, like one copied the other, or both took a suggestion from Apple.
  • The $15 plan offered by Bell and Rogers saves $5 over the $20 plan from Telus until you use more than 250MB.
  • Telus' one-price plus overage model is cheaper than Bell and Rogers' $35 plan from 250MB-800MB.
  • From 800MB to 5GB, Bell and Rogers $35 plan seems to be the way to go, because you'll be paying more than $35 by the time you pay Telus for your overage.
  • After 5GB, it seems like Telus' plan, which maxes out at $50, would be cheaper than paying Rogers or Bell for another $35/5GB, assuming Telus doesn't cut you off.
None of the plans spell out the edge cases:
  • Telus does have fine print saying "Subject to a monthly data limit of 5GB", but they're not very clear what happens if you hit that limit.
  • Neither Rogers nor Bell explains what happens when you reach your cutoff. If you run out of data on the $15 plan, can you upgrade to the $35 plan for $20, keeping the original timeframe? Can you buy a new 30-day window of $15?
Based on my iPhone data usage, if I were to acquire and use an iPad regularly, I think I could probably live within 250MB, although if I went over occasionally, it might be simpler to have Telus' plan. I suspect I wouldn't go over 800MB.

Wednesday, June 16, 2010

The Future of iPhone Multi-Tasking

Marco Arment on iPhone Multitasking:

One of my most common feature requests is for Instapaper to periodically download articles in the background. A lot of people forget to launch the app to let it download content before going underground or boarding a plane.
I’ve already received multiple emails from people who are excited for iOS 4’s multitasking because they can’t wait for this to finally stop being an issue, because they think Instapaper will be able to download articles periodically in the background.
It’s painful to respond, crushing their hopes, to tell them that the iOS multitasking system doesn’t allow me to do that.

The solution he proposes:

  • The application gives the system an NSURLRequest and an ideal refresh interval, such as every 30 minutes, every few hours, or every day.
  • iOS executes that request, whenever it deems that it should, and saves the response to a local file.
  • Next time the application launches, iOS hands it an NSData of the most recent response.
It's an improvement, although it doesn't go far enough for me. There's a couple of enhancements I'd love to see.

Idle Processing
If the user isn't actively using the phone when the data is retrieved, it would be great if the application were given a chance to process that information, and possibly notify the user (even if this requires user opt-in).

For instance, if you've got a stock application and it periodically retrieves the prices of the equities within your portfolio, it would be nice if it could be set to notify you when prices cross certain thresholds, or change dramatically, rather than having to launch the application before it's given a chance to process the data.

Server Push
The polling model will do, but since the iPhone already supports push notifications, it seems like supporting push notifications whose target is an application rather than the user would also be handy. Instead of having your stock application download regular price updates, the server-side element could watch stock prices, looking for price changes of enough difference to matter, then push those prices to your application.

No Guarantees
As with the rest of Marco's solution, I think it's reasonable to give iOS some decision-making authority with respect to when and how these things are processed. I don't expect the phone to run out making regular requests to the server or sending lots of push notifications and firing up applications when the battery's already low.

The Future of iPhone Multitasking
I suspect Apple has already considered approaches like what Marco has proposed and the enhancements I'm suggesting here. I also suspect that they've decided explicitly not to take these approaches for the time being.

I can live with that. They're expanding on multitasking for iOS4, and applications will take some time to catch up. Once developers, consumers and Apple have had the chance to take iOS 4 multitasking in hand, there will be a chance to expand upon that further, and I think the proposal offered by Marco and the enhancements I'm suggesting here are good examples of the directions they can take while still taking a cautious approach to multi-tasking in iOS.

Monday, April 19, 2010

Telus iPhone Visual Voicemail

I was pleased to see that Telus announced and rolled out Visual Voicemail, as was implied in the Carrier Update file I received and analyzed.


I guess that means I may well look into the next carrier update file I receive, since the first one gave me a clue to an upcoming rollout.

Friday, April 2, 2010

OS X 10.6.3 and GWT 1.x

Safari 4.0.4, when it came out, had an issue with respect to GWT development, causing the GWT hosted mode browser to crash on load. As a result, I didn't take the Safari 4.0.4 update. When Safari 4.0.5 came out, reports indicated that it wasn't any better, despite the fact that some of the interim builds of WebKit had been used successfully, so I didn't take that update either.


Unfortunately, I did take OS X 10.6.3, which apparently came down with Safari 4.0.4 and broke my GWT development environment. I tried upgrading to Safari 4.0.5, but had no success. Eventually, I settled on the workaround that others were using with the WebKit nightly build (comment #22 on that issue) which seems to have solved my problems.

I did also briefly look into upgrading my current project to GWT 2.0.3; seems like it works pretty well, but we're coming up on a release point and it's an awkward time to do a framework upgrade. I'm hoping that by broadcasting this, some of you will avoid this same path.

Wednesday, March 17, 2010

Telus iPhone Carrier Update

This morning, iTunes warned me that there was a carrier update available for my iPhone. The 'more info' button opened a link that explained the process, but didn't offer any details about what the update contained, other than updates with regard to my settings for my carrier, Telus.


In the spirit of interested inquiry, I thought I'd follow in the footsteps of others, and take a peek inside the update file. I started by copying the carrier file to a temporary folder and extracting the contents:

$ mkdir ~/telus
$ cp ~/Library/iTunes/iPhone\ Carrier\ Support/Telus_ca.ipcc ~/telus/telus.zip$ cd ~/telus$ unzip telus.zip
Archive: telus.zip
creating: Payload/
creating: Payload/Telus_ca.bundle/
inflating: Payload/Telus_ca.bundle/carrier.plist
inflating: Payload/Telus_ca.bundle/Default_CARRIER_TELUS.png
inflating: Payload/Telus_ca.bundle/FSO_CARRIER_TELUS.png
inflating: Payload/Telus_ca.bundle/Info.plist
inflating: Payload/Telus_ca.bundle/version.plist

After that, I took a quick peek at the carrier.plist, but it was in binary format, so I had to convert the .plist files to XML:


$ plutil -convert xml1 *.plist

That let me take a look at the .plist files. The info.plist and version.plist files were very dull, looks like metadata about the bundle. The carrier.plist file was slightly more interesting:


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>BookmarkURLs</key>
<array>
<dict>
<key>BookmarkName</key>
<string>TELUS Web</string>
<key>BookmarkURL</key>
<string>http://m.telusmobility.com/homepage</string>
</dict>
</array>
<key>CarrierBookmarks</key>
<array>
<dict>
<key>Title</key>
<string>Web TELUS</string>
<key>URL</key>
<string>http://m.telusmobility.com/homepage</string>
</dict>
</array>
<key>CarrierName</key>
<string>TELUS</string>
<key>IntlDataRoamingSwitch</key>
<true/>
<key>MMS</key>
<dict>
<key>GroupModeEnabled</key>
<false/>
<key>MMSC</key>
<string>http://aliasredirect.net/proxy/mmsc</string>
<key>MaxRecipients</key>
<integer>20</integer>
<key>Proxy</key>
<string>74.49.0.18:80</string>
</dict>
<key>MaxBluetoothModemConnections</key>
<integer>1</integer>
<key>MyAccountURL</key>
<string>http://m.telusmobility.com/homepage</string>
<key>MyAccountURLTitle</key>
<string>TELUS Web</string>
<key>Services</key>
<array>
<dict>
<key>ServiceCode</key>
<string>#411</string>
<key>ServiceName</key>
<string>Directory Assistance</string>
</dict>
<dict>
<key>ServiceCode</key>
<string>#411</string>
<key>ServiceName</key>
<string>Assistance-annuaire</string>
</dict>
<dict>
<key>ServiceCode</key>
<string>#8294</string>
<key>ServiceName</key>
<string>TAXI</string>
</dict>
<dict>
<key>ServiceCode</key>
<string>#2886</string>
<key>ServiceName</key>
<string>Roadside Assistance</string>
</dict>
<dict>
<key>ServiceCode</key>
<string>#2886</string>
<key>ServiceName</key>
<string>Assistance Routière</string>
</dict>
</array>
<key>ShowCallForwarded</key>
<false/>
<key>ShowCallForwarding</key>
<false/>
<key>ShowDialAssist</key>
<false/>
<key>ShowTTY</key>
<false/>
<key>StatusBarImages</key>
<array>
<dict>
<key>AllowPrefixMatching</key>
<false/>
<key>CarrierName</key>
<string>TELUS</string>
<key>DefaultImage</key>
<string>Default_CARRIER_TELUS.png</string>
<key>FullScreenOpaqueImage</key>
<string>FSO_CARRIER_TELUS.png</string>
</dict>
<dict>
<key>AllowPrefixMatching</key>
<false/>
<key>CarrierName</key>
<string>TELUS UMTS</string>
<key>DefaultImage</key>
<string>Default_CARRIER_TELUS.png</string>
<key>FullScreenOpaqueImage</key>
<string>FSO_CARRIER_TELUS.png</string>
</dict>
</array>
<key>StockSymboli</key>
<array>
<dict>
<key>symbol</key>
<string>T.TO</string>
</dict>
<dict>
<key>symbol</key>
<string>TU</string>
</dict>
</array>
<key>SupportedSIMs</key>
<array>
<string>302220</string>
</array>
<key>SupportsNITZ</key>
<true/>
<key>SupportsUserBusyCauseCode</key>
<true/>
<key>VVMIgnoresIntlDataRoaming</key>
<false/>
<key>VisualVoicemailServiceName</key>
<string>IMAP</string>
<key>apns</key>
<array>
<dict>
<key>apn</key>
<string>sp.telus.com</string>
<key>password</key>
<string></string>
<key>signature</key>
<data>
...
</data>
<key>type-mask</key>
<integer>7</integer>
<key>username</key>
<string></string>
</dict>
<dict>
<key>apn</key>
<string>isp.telus.com</string>
<key>password</key>
<string></string>
<key>signature</key>
<data>
...
</data>
<key>type-mask</key>
<integer>48</integer>
<key>username</key>
<string></string>
</dict>
<dict>
<key>apn</key>
<string>sp.telus.com</string>
<key>password</key>
<string></string>
<key>signature</key>
<data>
...
</data>
<key>type-mask</key>
<integer>6</integer>
<key>username</key>
<string></string>
</dict>
</array>
<key>com.apple.voicemail.imap</key>
<dict>
<key>BeaconAddress</key>
<string>474663</string>
<key>ClientManagesTrash</key>
<false/>
<key>GreetingNotification</key>
<true/>
<key>MaxGreetingDuration</key>
<integer>60</integer>
<key>MaxPINLength</key>
<integer>10</integer>
<key>MinPINLength</key>
<integer>4</integer>
<key>UsesMWI</key>
<true/>
<key>UsesSSL</key>
<false/>
</dict>
<key>voicemail_context</key>
<integer>0</integer>
</dict>
</plist>

I won't claim to understand the meaning of all those settings, but some of them seem to point to Telus supporting visual voicemail, which would be interesting. And finally, the .PNG files, once decoded from iPhone PNG format, were themselves pretty boring. Very small transparent images of the word "TELUS" in all caps; these are referenced in the .plist above as Status Bar Images.

Monday, January 25, 2010

Composite Event Handler Registrations in GWT

In my previous entry, I wrote up a class for displaying Input Prompts in GWT. As I started to fold that code into my project, I realized that I didn't expose the handler registrations, which would make it impossible to remove the event handlers if and when the text fields for which input prompts were displayed were created and removed during the lifecycle of the application.

Because the Input Prompt registers handlers for both Blur and Focus, there are two registrations. It's not easy to return two values from a single method, and frankly, I don't think a class using InputPrompt should have to know or care what events it's employing in great detail. As a result, I've created a composite event handler registration to return:


package com.codiform.gwt.event;

import java.util.ArrayList;
import java.util.List;

import com.google.gwt.event.shared.HandlerRegistration;

public class CompositeHandlerRegistration implements HandlerRegistration {

private List registrations;

public CompositeHandlerRegistration() {
registrations = new ArrayList();
}

void add( HandlerRegistration registration ) {
if( registration instanceof CompositeHandlerRegistration ) {
CompositeHandlerRegistration composite = (CompositeHandlerRegistration) registration;
registrations.addAll( composite.getRegistrations() );
composite.clear();
} else {
registrations.add( registration );
}
}

private List getRegistrations() {
return registrations;
}

public void removeHandler() {
if ( registrations.size() > 0 ) {
for ( HandlerRegistration item : registrations ) {
item.removeHandler();
}
clear();
} else {
throw new IllegalStateException( "Composite handler registration is currently empty, and cannot remove handlers." );
}
}

private void clear() {
registrations.clear();
}

}


If a composite handler registration is passed to another composite handler registration, I flatten them; this might be unnecessary. In the spirit of YAGNI, I won't be at all unhappy if you decide you don't need that capability. I also decided I preferred to clear my local references to any inner handler registrations as soon as they've been removed, rather than hanging on to them indefinitely.

All in all, this is pretty simple GWT code, but seemed worth following up on the previous entry to talk about the need for handler registrations.

Friday, January 22, 2010

Input Prompt Pattern in GWT

For a GWT project I'm working on, I've reached a point where I wanted to apply input prompts to a text box; this doesn't seem to be something that's built in to the basic GWT framework, or an easily-located extension. For that matter, I couldn't find anyone who'd done it and blogged about it, although it might be that they've used different terminology.

I thought it was worth a quick experiment to see how easy they would be to apply, and this is what I came up with:


package com.codiform.gwt.widget;

import com.google.gwt.event.dom.client.BlurEvent;
import com.google.gwt.event.dom.client.BlurHandler;
import com.google.gwt.event.dom.client.FocusEvent;
import com.google.gwt.event.dom.client.FocusHandler;

public class InputPrompt implements BlurHandler, FocusHandler {

private String promptText;

public InputPrompt( String text, TextBox... inputs ) {
this.promptText = text;
for( TextBox item : inputs ) {
apply( item );
}
}

public void apply( TextBox input ) {
input.addBlurHandler( this );
input.addFocusHandler( this );
applyPrompt( input );
}

public void onBlur( BlurEvent event ) {
TextBox blurred = (TextBox) event.getSource();
applyPrompt( blurred );
}

private void applyPrompt( TextBox input ) {
if( input.getText().isEmpty() ) {
input.setText( promptText );
input.addStyleName( "inputPrompt" );
}
}

public void onFocus( FocusEvent event ) {
TextBox focused = (TextBox) event.getSource();
if( promptText.equals( focused.getText() ) ) {
focused.setText( "" );
focused.removeStyleName( "inputPrompt" );
}
}
}


The final version has some project-specific tweaks (interface for the textbox to make this code testable with mocks, a style name in the project namespace), but for the most part the above code seems to do the trick well and I'll be applying it shortly.