Experiences porting iPhone apps to Android

December 2nd, 2009 by David Brackeen

This year I had the oppurtunity to port two iPhone apps to Android. One was a game, another a communication app. I wanted to share some of the gritty details while they’ve been bouncing around my head.

With respect to my clients (well actually it’s because of those crazy NDAs), for the purposes of this blog, I’ll call the two apps I ported “the Game App” and “the Chat App”. Also, I mention some code below, but unfortunately I can’t share that either.

In the (near) future, there may be none of this “porting” nonsense – maybe we’ll all be building apps using cross-platform APIs like PhoneGap or Flash CS5. But in the case of these two apps, they were already written in Objective-C, and they needed to be converted to run on Android. So I got to work.

In some cases, Objective-C can be directly translated to Java, while in other cases new Java code has to be written from scratch. Things like string manipulation, XML handling and SQLite interfaces aren’t that different on the two platforms. However things like UI code can be incredibly different.

UI layout

Neither of the two iPhone apps I ported used Interface Builder.

The Game App used UIViews (mostly UIImageViews) and CoreAnimation. This is an acceptable choice for a simple 2D game. So, for the port, I thought I’d also use Android’s UI scene graph and animation framework. I was able to do it, but it required a lot of supporting classes for layout and animation, and in the end, it proved to be more difficult than it was worth. It would have been faster to write a custom scene graph. Here are some of the issues:

  1. Creating a custom Layout. Android requires Layouts for placing Views, and there was no existing Layout that could place a View based on a position and an anchor (the iPhone version made heavy use of anchorPoints for placement).
  2. Creating custom Animation classes. By default, the animation framework couldn’t do the type of key-frame animation that CoreAnimation can do (it took a while just to figure that out). I ended up having to create my own Animation class (and subclass AnimationSet) to get it right. This took a long time to figure out because it was a trial-and-error process. Unfortunately, for this app, Android has much longer build times than iPhone, which greatly impacts any trial-and-error process (on my machine, 20 seconds from hitting the “build” button to getting it to run on the emulator, vs. 2-4 seconds for the iPhone version).

The Chat App was more straightforward, but I needed to use RelativeLayouts on Android, and it was a bit difficult to create a RelativeLayout that matched the absolute positioning on the iPhone. The biggest problem was one of the relative layouts was considered recursive: I was laying out a view group from the bottom up, where the final dimension of the view group was defined by its contents. This was solved by determining the dimension of the view group using trial-and-error at runtime.

The other minor problem with the Chat App was that the iPhone version did some long-running networking in the UI thread. This isn’t the best practice, but it isn’t a problem on iPhone if you don’t have any usable UI controls on the screen at the time (there was just a spinning activity indicator). However, this is a big no-no on Android, which interrupts the user with a dialog asking the user to “Force Close” the app or “Wait”. (You could say iPhone has a physical “Force Close” button – the Home button. On Android the Home button doesn’t force close the running app). Luckily, using Android’s AsyncTask was an easy fix.

Another problem with the Chat App was with contact picking. Unfortunately, the old Contacts API was deprecated in Android 2.0. It still works, but on Android 2.0, the returned values are different from the Android 1.5 version, which required some extra code to fix. Also, at least one device (Motorola CLIQ) has a custom built contact picker which gives the user an error dialog every time it runs (to be fair, this is supposed to be fixed in an upcoming OTA release).

The little things

One thing to keep in mind is Android’s lack of a JIT and it’s slow garbage collection. Eventually this will be fixed, but that fact, and the fact that most Android devices’ hardware is slower than the iPhone, makes the experience of these two apps not as smooth on Android.

One strange problem is that many (or all?) Android devices use 16-bit color. Even on the Droid, which is advertised to have a 24-bit color depth, shows obvious color banding when I displayed a gradient that looked fine on the iPhone. (Apple doesn’t say what the color depth of the iPhone is, but based on how that gradient appeared, I’m guessing it’s 24-bit).

This is a bit off-topic for a moment, but one of the weirdest things about both platforms is launching the email client. Compare iPhone:

NSString* subject = NSLocalizedString(@"Subject", nil);
NSString* body = NSLocalizedString(@"Body", nil);
NSString* encodedSubject = [subject stringByAddingPercentEscapesUsingEncoding: NSASCIIStringEncoding];
NSString* encodedBody = [body stringByAddingPercentEscapesUsingEncoding: NSASCIIStringEncoding];
NSString* url = [NSString stringWithFormat: @"mailto:?subject=%@&body=%@", encodedSubject, encodedBody];
[[UIApplication sharedApplication] openURL: [NSURL URLWithString: url]];

To Android:

String subject = getString(R.string.subject);
String body = getString(R.string.body);
Intent mail = new Intent(Intent.ACTION_SEND);
mail.setType("text/plain");
mail.putExtra(Intent.EXTRA_TEXT, body);
mail.putExtra(Intent.EXTRA_SUBJECT, subject);
Intent chooser = Intent.createChooser(mail, getString(R.string.email_description));
startActivity(chooser);

Both are awkward, wouldn’t you say? Why not something like this?

String to = "";
String subject = getString(R.string.subject);
String body = getString(R.string.body);
context.email(to, subject, body);

Distribution

Obviously the Android Market wins in terms of time-to-release. Fifteen minutes and you’re done – no two-week review, no fear of rejection. But there was a couple oddities to deal with.

The Game App had both Full and Lite versions of the game. On the iPhone, both versions used the same codebase, and a few preprocessor conditionals determined which code to execute for each version. Java has no preprocessor conditionals, and also, the Android Market requires that each app have a unique package name. This posed a problem since it would be wise for the two versions to share the same codebase on Android, too. (Creating two projects meant maintaining two codebases!) The problem was exasperated by the fact that Android references image resources by package name, too.

What I did is write a script that programatically copied the project and changed the package name along the way. Everywhere in the manifest, Eclipse project files, and every source file was changed. A single source file named Build.java had a boolean static field named LITE which was programmatically changed as well. The script worked surprisingly well.

Also, the Full version was accidentally marked as “free”. We pulled it, but we couldn’t convert it to a paid app, and we couldn’t re-upload because the package name was already in use. Support to Google didn’t help. We ended up using the package-name script again to create a new Full version that we could upload and make a paid app.

Closing thoughts

If you’re looking to port an iPhone app to Android, it might be a good idea to do some code cleanup first. It’s important to keep the model code clearly separated from the view code. Porting model code, which involves more direct translation rather than re-write, is a lot easier if it’s not mixed in with the views.

Also, watch out when searching for Android example code. I ran into a few examples that use old, pre-1.0 code that doesn’t even compile anymore.

All in all, it’s has been a strange ride. I kind of feel like porting these two apps gave me some new skills that will be obsolete soon, but we’ll see. At least, if I have to do it again, I’ve got a few tricks up my sleeve to make it a bit easier.

JavaFX, startup time, and security dialogs

December 5th, 2008 by David Brackeen

Sun released JavaFX yesterday, which has a lot of great technology in it, but unfortunately the user experience is tragically poor. From my own experience, the first time I loaded a JavaFX applet I was presented with four dialogs: one security dialog for the JavaFX runtime, another security dialog for the JavaFX samples, a JavaScript warning dialog, and a JavaFX license agreement acceptance dialog. The applet took over two minutes to load.

A security dialog appears every time the SimpleVideoPlayer example is shown (if it manages to start at all). Imagine if YouTube required the user to navigate a security dialog every time a video was played.

It’s a shame, and it represents a lack of vision in the direction of JavaFX.

The overall vision of JavaFX should have included three items: A great animation framework, fast startup time, and frictionless loading – AKA no security dialogs.

Sun created a pretty good animation framework, with a new scene graph and the declarative JavaFX Script. Unfortunately not all effects are truly animatable. The effects example crawls on my machine. Part of the problem is the algorithm choice: the blur example uses a nice-looking, but CPU intensive Guassian blur instead of a fast, but lower quality, box blur. Plus, their effects framework requires native code – one of the reasons for the security dialogs. There is no good reason why a software-based effects cannot be developed optimized for HotSpot.

Startup time is improved with the new, Java 6u10 quick starter feature. However, the quick starter is moot because the JavaFX runtime libraries are so large. People are reporting – including myself – that many of the applets take several minutes to load – even the second time you load them. Based on posts from a Sun employee at the javagaming.org message board, this is a mistake that will be fixed in the next version. However, the problem should have never existed in the first place. Fast startup time should have been priority number one, and no code modifications should have been allowed that broke this feature.

Finally, it appears that most, or all, of the JavaFX applet examples require the user to naviagate a security dialog. On my Mac, I had to accept a security dialog just for JavaFX to compute prime numbers. (The demo was supposed to show how much faster JavaFX is compared to JavaScript, but ironically, if you count the loading time, the JavaScript version computes faster.) There is probably a very good technical reason for showing the security dialogs, but to a non-technical general audience, this doesn’t make any sense. Why should I have to navigate a dialog to see a blur effect or watch a video? Plus, because some applets perform additional loading after the dialog is accepted, the dialog appears in the middle of the entire loading sequence. For large web apps, I typically start loading the app in one browser tab while reading in another tab. If a dialog appears in the middle of the loading sequence, that means when I return to the app’s browser tab, I have to accept the dialog and wait again. Finally, security dialogs are an instant turnoff to many users: I don’t have hard statistics, but anecdotal evidence suggests some users never accept the scary, confusing dialogs.

There is a lot of good technology in JavaFX, and it’s a shame that the user experience is what it is. However, I don’t think all is lost. I believe the underlying technology truly doesn’t matter as long as there is a positive user experience. Idealistically, developers should be able to use the tools they want, not the tools they are forced to use. And I believe a positive user experience can be created in Java – that’s why I developed PulpCore. Sun has incredibly smart engineers, and they can fix the problem if they are intrinsically motivated to do so. Here what they should so:

  1. Make sure everything can run without security dialogs. Both the video decoding and the effects framework should work entirely in software. HotSpot is entirely capable as is, plus GPU rendering won’t work on all machines anyway.
  2. Dramatically decrease the startup time. As mentioned before, it appears the Sun engineers are already working to fix this.
  3. Every effect should be animatable at 30 frames per second. Downgrade some of the effects if you have to (i.e. use box blur instead of Gaussian blur)

There’s a few other minor complaints I have of JavaFX (it appears Nodes can’t have their own BlendMode, dirty rectangle calculation is slow, and if I’m not mistaken it appears Nodes can’t display at fractional locations.) But these are small issues. The big picture is having a positive user experience, and although JavaFX doesn’t deliver that today, if it is moved in the right direction, it can.

P.S. This blog was hacked several months ago, and I didn’t even notice. The content of one post completely disappeared. Yes, I should update more often.

In Helvetica (where available)

June 29th, 2007 by David Brackeen

Just a quick note to mention the new web design here at interactivepulp.com. A lot of stuff was added in the last three months (like pulpgames.net, this blog, and PulpCore) and the site needed a refresh to reflect it.

On a completely different topic, I’m seeing quite a bit of searches like “Milpa tips” and “Milpa hints” showing up in the server logs. So there is a thirst out there – any Milpa experts out there wanna share their skillz? After all, you all are better at the game than I am. :) Write a blog about it and I’ll link to it!

Capture the Newbies game design

June 29th, 2007 by David Brackeen

Note: this article was written around December 2005 and later added to the blog during a website redesign. The game is playable online at www.nicktoonsnetwork.com.

Fire your Net Shooter at unsuspecting pests in this 3D action/adventure web game!

Capture the Newbies screenshot

Capture the Newbies is an action/adventure web game designed for the Nicktoons Network.

  • A kid-friendly 3D first-person shooter featuring Nicktoons characters.
  • Filled with narrative content, plot twists, and surprising bosses.
  • Tons of all-original graphics spanning two worlds.
  • User-friendly design.
  • No special browser plugins required.

Nicktoons characters and content are copyright Nicktoons Network.

Goal

The goal of the Newbies project was to create a 3D web game that is fun, kid-friendly, and featuring Nicktoons characters.

Game Design

Capture the Newbies is a first person shooter (FPS) that is immediately familiar to anyone who has played similar FPS games, like Doom or Half-Life. However, Newbies is designed for a younger audience that might not have experience with FPS games.

Newbies contains many elements of a traditional FPS. The players fires a weapon, gets powerups, and occasionally partakes in puzzle-solving. The game is driven by an underlying story, and the player must confront a few tough bosses.

Aside from minor differences, like a cartoon-style environment or a simplified health system, the main difference between Newbies and a traditional FPS is the weapon system. Instead of shooting a gun to kill bad guys, players in Newbies use a “Net Shooter” to hurl nets at unsuspecting pests – a more kid-friendly gameplay mechanic. Pests caught in a net can be “captured” by the player.

Traditional shooter games feature two types of weapon ammo: unlimited or disposable. Unlimited ammo is fired from a weapon that will never run out of projectiles to fire. Disposable ammo – bullets, rockets, bombs, etc. – are destroyed forever once fired, leaving the player to find new ammo packs in the environment. The Newbies ammo system is a different ammo type: reclaimable. Each unit of ammo is unique and reusable: after the player fires a net, the player can later reclaim the net by running into it. This creates different design challenges and ultimately allows Newbies to have a unique gameplay experience.

Interaction Design

Capture the Newbies screenshot

The controls are designed to be as simple and minimal as possible. There are only two main functions: moving (arrow keys) and firing (space bar or mouse click). No other keys are necessary: doors open automatically and other objects activate when the player bumps into them (e.g., pressing a switch or talking to an in-game character). Also, weapon upgrades are permanent, so there is no key needed to switch to different weapons.

Friendly characters gradually tell the player how to interact with the game. As the game progresses, input from friendly characters is limited in order to let the player explore and discover how portions of the game work on their own.

When the game first starts, the heads-up-display is initially blank. Visual feedback in the form of status icons do not appear until they become relevant to the game.

The game involves several pages of text in order to progress the storyline. The game avoids intimidating young readers by presenting game text in large, friendly letters one paragraph at a time. Also, a “Tell me again” button is available when important text is presented to the player, allowing the player to rewind to view text again if they missed something important. Since Newbies is a web game, the mute button is always available on screen. Web games are designed for casual players, and casual players often want to quickly and immediately mute game sound because they are in a social setting (work, school, etc) or because they prefer listening to music or streaming audio without any additional sound.

Technology

The Newbies engine is Java 1.1 compatible, so virtually all of today’s desktop machines can play the game without installing a browser plug-in. Newbies also runs great on low-end machines.

The engine also contains technology not found in Java 1.1, like themed UI buttons, alpha blending, bilinear filtered scaling, animation, tweens, high-precision timers, and more.

Engine behind Milpa released as open source

June 14th, 2007 by David Brackeen

Milpa running in debug mode

The engine used to create Milpa – known as PulpCore – is today available as free open source. PulpCore is a 2D rendering and animation framework for the Java plug-in.

I didn’t really plan on creating a 2D framework – it just sort of happened gradually over a few years. It recently came to the point where I wanted to let others take a look at it and use it for their own games.

Read more about the goals and features at the Google Code PulpCore page, and see examples and API documentation at the PulpCore page here at Interactive Pulp.

Ridiculously high scores? No problem!

May 13th, 2007 by David Brackeen

Two things became apparent after I added high scores to Milpa: first, people are doing exceptionally well at the game. Second, the game is easy to hack. Check out those scores:

All Time

Several people were getting higher than Year 30, doubling my personal high score. I didn’t expect anyone to get that far. So, hey, you guys are awesome.

Second, the game scores are totally hackable. Year 88? No way. I took the precaution of encrypting scores before they are sent to the server, but that doesn’t stop something I knew nothing about: easy-to-use tools that can modify game memory during runtime. These tools allow you to find where a score is stored in memory and edit its value. One of these tools is called Cheat Engine.

Cheat Engine screenshot

There was a recent Digg article about hacking games where many of these tools were mentioned in the comments. People generally seemed enthusiastic about sharing different ways to cheat.

I downloaded Cheat Engine and within 5 minutes hacked Milpa. Watching the score jump from 1,000 to 100,000 felt like I had just won the lottery. It gives a sense of accomplishment, and a chance to show off to others. In a sense, it’s like playing a whole other game with a new set of rules and your own goals. So I can see why people do it.

I thought about leaving the hacked scores in and just watching to see what happens, but ultimately, that is not fair to the people who legitimately make high scores. So I did a couple things.

First, I made the game more difficult after Year 13. People getting to Year 36 is great, but at that point the game becomes a test of endurance rather than skill – playing one game must take longer than an hour, which is not at all what I intended. Because of this change, I deleted all scores higher than Year 13 so that all high scores represent the same difficulty level.

Second, the game now encrypts in-game memory. The game is still hackable, and always will be as long as the game logic exists on the client, but now hacking a score in the same manner won’t be possible. So if you want to hack, you’ve got a whole new set of rules to deal with. :)

Refresh the page to get the latest version of Milpa, and click the i button to make sure you’ve got version 1.2.1.

Global high scores for Milpa

May 4th, 2007 by David Brackeen

Milpa now has global high scores!

Milpa Scores

There are three lists: personal, recent, and all-time. The personal list shows the scores made on your computer. The recent list shows the top scores in the last 24 hours (although that duration will probably change), and the all-time list shows, well, the best scores ever. Enjoy!

Milpa game design

April 16th, 2007 by David Brackeen

The idea of the spin mechanic in Milpa originates from an old computer game called Reverse published in the book BASIC Computer Games in 1978. In Reverse, the player is presented with the digits 1 through 9 in a random order. The object of the game is to created a sorted list by reversing the order of any number of the digits, starting from the left. There is no time limit, and there is no way to lose – the player either achieves the goal or stops playing.

Reverse game

I enjoyed the game a lot when I was younger, and even programmed it into a TI-85 so I could share it when friends at school. However, I noticed there are two issues with the game: for many people, the “reverse” mechanic is difficult to visualize (it needed animation) and sorting numbers just isn’t that fun.

Recently I wanted to do something fun with the game idea, and eventually settled on turning it into a match-3 game with a “spin” animation. After messing around with different parameters, the game settled with four tile types and nine tiles to spin. This felt pretty fun and also prevented the situation of being stuck with no possible matches.

Adding an arbitrary time limit to the game doesn’t appeal to me. When I see timers in games I think, why do I only have so much time? Who decided this? Would Tetris have been fun if each piece had a time limit in which it could be placed instead of falling by gravity? So, to make the game challenging, the player is given a goal of collecting a minimum number of tiles and an enemy that can eat tiles before the player has a chance to collect them. The enemy (a skull in the final game) moves faster and faster as the player progresses, and the goal gets more difficult. So, instead of trying to beat a timer, players race the antagonist. (The object of not creating an arbitrary timer was still in vain though – many players still associate the enemy with time).

At that point, the tiles were simply different colored discs, and the game needed a theme. The tiles should be something the player intrinsically wants to collect, like jewels or candy. At the time I was reading 1491: New Revelations of the Americas Before Columbus by Charles C. Mann and the concept of a milpa – a field of maize and other crops – fascinated me. Consider this quote from Mann:

Milpa crops are nutritionally and environmentally complementary. Maize lacks the amino acids lysine and tryptophan, which the body needs to make proteins and niacin;…. Beans have both lysine and tryptophan…. Squashes, for their part, provide an array of vitamins; avocados, fats. The milpa, in the estimation of H. Garrison Wilkes, a maize researcher at the University of Masachusetts in Boston, “is one of the most successful human inventions ever created.”

The goal of the game became collecting crops for the player’s Mesoamerican empire.

1491 Book

Finally, the game design and the graphic theme went through tweak upon tweak upon tweak to get it just right in terms of difficulty, fun factor, and appearance. Testing early versions of the game on friends and family proved to be invaluable. A powerup was added, and a fifth tile type was added to advanced levels. It is still not perfect – some find the game too hard, others too easy, and there is plenty of room for visual polish – but I think there is a good balance and Milpa became a fun, unique game.

Milpa Game

P.S. For the curious, so far my best score is 32,350 in Year 19 :)

Hello world

April 15th, 2007 by David Brackeen

A blog. Having a blog sounds like a better idea than creating a new page for this or that every time I want to mention this or that. So here we go, a blog.

This blog will most likely discuss things such as game programming, game design, interaction design, usability, and of course, occasional announcements for Pulp Games. Random personal topics (like coffee preference based on country of origin) will most likely be kept to a minimum.

As for you, the reader, I’d like to hear from you lots of creative ideas and constructive criticism that would help make Pulp Games better. The games will be updated based on user feedback, so if you have something to say, this is the place to say it!