Tuesday, 21 May 2013

HTML5 programming with Dart

Dart is web programming language from Google designed to address some of Javascript's perceived flaws. While Javascript has its supporters, there are many who find developing with it irritating and difficult, especially for large-scale or complex projects.

Dart attempts to address these shortcomings by providing many features that are associated with the more fully-featured object-oriented languages, such as Java. These features include classes, interfaces, generics and optional typing.

With this in mind, I decided to take a look at Dart with a view to creating a simple HTML5 canvas game, in order to try out some of the features. This means having a html canvas element as shown, with the game mechanics and UI written in Dart and then compiled into Javascript.
    <canvas id="gameCanvas" width="400" height="464">     </canvas>
Setup is straightforward, and is made easier still by the existence of the Dart Editor, which is essentially a stripped-down version of Eclipse.

First Impressions


Well, it initially looks very similar to Javascript. Much of the syntax, including for declaring variables and functions, is the same. But, for someone with knowledge with Java, the additional features such as classes suddenly make it much easier to plan and develop your project. The notation is familiar:
class MovingObject {   ImageElement img;   int x;   int y;   int dx;   int dy;   MovingObject(this.img, this.x, this.y); }
Another nice feature is that object types can be declared, and this information can be used the editor to provide "content assist" or compile-time warnings, etc. The types are optional so if you need to do rapid initial development using Javascript-like "var"s  in your code, you still can.

The Dart Editor provides Content Assist features

The intention is that Dart can be exported to Javascript so that compatibility with the current crop of web browsers will be maintained. However, this is where I found the most problems. Even when my app would run perfectly as a native Dart app, on several occasions the exported Dart app would not function correctly. As might be expected, I got the best results when using Google Chrome. But if the intention is to compile to Javascript that works on any browser, then my experience suggests there is a way to go yet.

My first project was to create a simple canvas-based two-player strategy game called Gomoku, which you can try out. Note due to the Javascript compilation problems mentioned above, it is currently only working in Google Chrome.

In summary, after a first look, I think Dart has a lot of potential. It's unlikely that the native Dart VM will make it to all the browsers, so it's not going to be a replacement for Javascript. But if the compilation problems can be sorted out, I can imagine it being a popular choice of source language for developing both HTML5 canvas and web-form based applications.

More Dart-related stuff to come shortly...

Sunday, 20 May 2012

Custom Spinner component with images

This post was inspried by a request I received via HackerBuddy, asking for help on creating a customized ListView. The problem was how to show a different image on each row of the ListView. I had implemented something similar to this previously, except in my case it was a Spinner component (A Spinner is the Android equivalent of a drop-down menu selector). As the cases for ListView and Spinner are virtually the same, let's take a look at the Spinner.

Let's say we have an array of countries which we want to put into a spinner. A simple spinner containing text strings only can be created using the default ArrayAdapter provided by the Android SDK:

String[] countries = {...}; Spinner countrySpinner = new Spinner(context); ArrayAdapter adapter = new ArrayAdapter(context, android.R.layout.simple_spinner_item, countries); countrySpinner.setAdapter(adapter);

Now lets say we want have a flag icon representing each country, and we want to show the respective flag next to each country in the list. Because we now need two peices of information per country (the name and the flag), we first replace the "countries" string array with an array of Country objects. Next, we need a more complicated ArrayAdapter to deal with these Country objects, so let's extend ArrayAdapter to create a new CountrySpinnerArrayAdapter.

Country[] countries = {...}; Spinner countrySpinner = new Spinner(context); ArrayAdapter adapter = new CountrySpinnerArrayAdapter(context, android.R.layout.simple_spinner_item, countries); countrySpinner.setAdapter(adapter); class CountrySpinnerArrayAdapter extends ArrayAdapter { private List countries; public CountrySpinnerArrayAdapter(Context context, int resourceId, List countries) { super(context, resourceId, countries); this.countries = countries; } public View getView(int position, View convertView, ViewGroup parent) { TextView textView = (TextView) super.getView(position, convertView, parent); Country country = countries.get(position); textView.setText(country.getName()); textView.setCompoundDrawablesWithIntrinsicBounds(country.getFlagImage(), null, null, null); return textView; } public View getDropDownView(int position, View convertView, ViewGroup parent) { return getView(position, convertView, parent); } }

What's happening here is the adapter returns the required View for each item in the list. This View can be modified to meet our requirements, which in the case means setting an image to the left hand side of the text, using the textView.setCompoundDrawablesWithIntrinsicBounds(...) method.

To see this implemented in the wild, check out my Daylight World Map app.

Wednesday, 18 April 2012

Deploying on multiple markets with in-app purchases

Last year, Google introduced in-app purchases for apps for the Android Market. This provided developers with an alternative model for generating income from apps. Rather than having a two versions of an app (limited free and paid-for premium), developers could now release a single app, with premium content being unlocked via in-app purchases.

More recently, Amazon have also announced a similar in-app purchase system, with promising initial results.

The downside with this in-app purchase model comes when developers have products available on several different app markets. Google's in-app purchase system only works through their own Android Market. So, what to do if you want to leverage in-app purchases, but also want to make your app available on a number of markets?

Well, there are no doubt various possible solutions, but this is what I have come up with. The strategy is essentially:

1. Create a "basic" (free) version of the app - this is the version that is available on markets which don't support in-app purchases.
2. For each market with in-app purchases that you want to support, there will be a separate version of the app. This is less work than it sounds if you simply take the basic version and extend it with the extra features. Then use a common interface to manage the different in-app billing systems.

Here are two suggested interfaces to use for handling in-app billing: public interface BillingHelper { /* initialize the billing system */ public abstract void init(Activity activity, BillingListener listener); /* call when activity.onStart() */ public abstract void start(); /* call when activity.onResume() */ public abstract void resume(); /* call when activity.onDestroy() */ public abstract void destroy(); /* determine whether billing is available */ public abstract boolean isBillingAvailable(); /* determine if a product ID is currently owned */ public abstract boolean isOwned(String itemId); /* request the purchase of a product */ public abstract void purchase(String itemId, String payload); }
public interface BillingListener { public void doUpdate(); }
Then, these interfaces are implemented for each market which supports in-app billing. The key to making life as simple as possible here, is that the different versions of the app can use entirely the same code except for switching between which BillingHelper implementation they choose, and the native billing code for that market:

A typical implementation would work like this... The basic version of the app can query the BillingHelper interface to determine whether billing is available (via the isBillingAvailable method), and only make enable the option the purchase upgrades if this is the case. When the user is ready to purchase an upgrade, the purchase method is called, and the payment and billing are handled by the appropriate BillingHelper implementation and the billing SDK.

The BillingListener interface is used to detect when a purchase event has occured, so the app can take the required action, such as downloading/unlocking the paid-for levels.

With this approach, although it remains necessary to maintain different versions of the app for each market you want to target, in practise nearly all the code is maintained in the basic version of the app. The additional versions for the specific markets are constructed via build scripts, in my case using ant.

Well, that's the strategy I plan to use to build different versions of my apps for a variety of app stores. If you have an alternative approach or a suggestion for improvement, do let me know!

Update 1:
On closer inspection of the Amazon developer payments system, it seems they are totally not set up to deal efficiently with non-US developers! Apparently it is required to obtain a US taxpayer identification, then fill in some US tax forms, and at the end of all that, they make payments by sending US$ cheques out in the mail. Seriously, US$ cheques???

This policy makes Amazon currently unusable for selling products in my view. They should definately take a leaf out of Google's book when it comes to payments. As an international company I would have expected better from Amazon. Let's hope they sort this problem out soon!

Update 2:
Amazon have recently much improved their services for non-US developers. The Amazon app store is now available in multiple counties, and developers in a lot more countries now have the opportunity to get paid in their home currency via bank transfer. Better late than never!