Using Chrome Mobile Apps to make Oplop an Android/iOS app

My consistent password generator project -- Oplop -- started life as a Python script, became a website, got packaged up as a Chrome extension, which then morphed into a Chrome app. There are also implementations by others for Emacs, webOS, Perl, SL4A/Android, and J2ME. In other words this project has been ported to a bunch of different platforms.

But two platforms it has not been officially ported to is Android and iOS. While Oplop works fine as a web app, the issue is the lack of easy copy/paste spoils the user experience. It has led to a constant back-and-forth in Oplop's web app design to try and balance functionality for both desktop and mobile, with the desktop typically winning as that's where I use Oplop the most.

But I knew that the clipboard was accessible for Android and iOS apps so I knew that if I did something as a native app it could work and allow me to work around the security restriction that the browser placed on me. But since Oplop is something I do in my spare time and already runs on two platforms, adding two more with special handling seemed very tedious.

Enter Apache Cordova, the open source version of PhoneGap. I figured that if I could find a plug-in for Cordova that gave me clipboard access it should (hopefully) be nothing more than another re-packaging and a minuscule amount of custom code to make a proper mobile app for Android and iOS which required very little custom maintenance as I had already refactored the base code for the Chrome app with an eye of making it pluggable for eventual mobile app use. But still, even Cordova seemed like enough work to act as a barrier to not want to work over.

Using Mobile Chrome Apps

But then Google announced Mobile Chrome Apps, a project designed to take a Chrome app and auto-create a Cordova project for Android and iOS. In other words it was exactly what I was after!

Over a couple of evenings I managed to use the project in [a branch of Oplop](https://github.com/brettcannon/oplop/tree/chromepackagedapp_v2
) to end up with a Cordova project which operates just like the Chrome app on Android and iOS (e.g. the clipboard works!). While the overall process was pretty good, there are a couple of rough edges as the project is only at version 0.0.5. But as the team is very quick to respond to bugs and this was going to solve a specific problem I had so I decided to give it a try.

Getting started

The instructions for Mobile Chrome Apps are enough to get you started and have something that is your Chrome app as a native app. I did figure out a couple of things which don't explicitly belong in the official docs or have yet to make it there so they are posted here to help anyone out there. The instructions are not long or complicated so feel free to read through them before proceeding with this blog post.

Don't check in anything that cca creates (except for one file)

When you run cca create to generate the Cordova application, it creates a directory for you filled with the needed files to create Android and iOS apps. Because it's auto-generated you really shouldn't need to check any of the files in. But there is one file -- manifest.mobile.json -- that you will want to keep around somewhere. The file contains things that are inappropriate for your manifest.json file but are needed for mobile apps. For instance, iOS and Android have different version specification requirements than Chrome apps, so they need to be set separately. Since you will be tweaking those values on occasion it probably makes sense to check that file in somewhere and keep it up-to-date (more on how to programmatically do that later). Otherwise just assume everything in the directory cca create made for you will get deleted on occasion so be able to generate the project from scratch.

Automating all of the steps of getting your project together is handy

I use the Ninja build tool to copy the common files from the website code for the Chrome app and then zip up the necessary files, so I just continued with that trend for the mobile app. I wrote rules to run cca create, install the clipboard plug-in I use, copy over my manifest.mobile.json into place as well as some custom code and splash screens for iOS (more on that later).

The build file is simple and allows me to completely nuke the project and start from scratch at any moment without worrying about getting things set up properly (except for needing to remember to unset CC in my shell as I run the latest version of Clang for my Python work and the iOS toolchain doesn't like that).

Auto-update your manifest.mobile.json

I wrote a Python script to auto-update my manifest.mobile.json to stay in sync with my manifest.json file from my Chrome app. Since Chrome, Android, and iOS all have different version number requirements I chose to settle on some consistent versioning scheme. I decided to go with Android's as it's the common denominator by simply being an integer. And since I view Oplop as a web app first and foremost I don't really care about semantic versioning and thus switching to just an integer didn't bother me (previously I used a date-based version denoting when the Chrome app was updated on the Chrome Web Store). By switching to what Android requires meant the Chrome app version can just be turned into an integer literal for Android. Unfortunately iOS requires a dotted version number, so I just tack on .1.1 to the version. I figured making it .0.0 would mislead people into thinking that the app might be unstable, so faking that it's already had one maintenance release wouldn't hurt anything. Then again, because of the date-based versioning I was doing the first integer-based version is 2014 so it already seems like I iterate constantly.

The code to update manifest.mobile.json is simple and should be future-compatible as it only updates two fields and otherwise copies over pre-existing values. Then it was just a matter of adding a build rule to make sure things stay in sync.

Be ready to make lots of icons

If you already have 16x16 and 128x128 icons for your Chrome app you still have 10 more sizes of your icon to create to cover Android, iOS 6, and iOS 7. Lucky for me I had already created icons for iOS 6 for Web Clip support, so it was only 6 in the end, but still annoying to have to go through the process of creating the PNGs, optimizing them, and getting them listed in the right place(s). And it's especially annoying to me as my icon is an SVG so it's vector-based and can be automatically scaled perfectly if any of these platforms supported vector-based icons (which they don't).

iOS doesn't like SVGs read from disk

Speaking of SVG, iOS will not render an SVG from a file. Mobile Safari decides that even though a file ends in .svg that it has a MIME type of text/xml and so isn't something to go in an img tag. Luckily my SVG isn't big so I just inlined it in the HTML, but that was a surprising annoyance to work around.

iOS splash screens suck

iOS has this concept of launch images which are just splash screens that are shown during startup of your app. If you read the design guidelines they basically say to present your app in a start state so it looks like your app is practically ready but not in a state that makes the user try to interact with it yet. This means no widgets that people think might be usable (either by leaving them out or showing them deactivated), no text that might change after startup is finished, etc. OK, fine, I can understand those guidelines.

But the splash screen sizes are the same as the full screen of the iOS device. While innocuously this sounds like no big deal and would suggest you can just take a screenshot and be done, it turns out to not be that simple thanks to the status bar.

There's no way from the iOS simulator to take a screenshot without the status bar. So if you do then you will end up wanting to cut that bar out or at least blacking it out, otherwise your current time and telecom company is going to flash before everyone. And the zooming in of the splash screen makes the idea of just blacking out the bar not that pleasing. Plus the top of my app is not some gradient I felt like just continuing on to the top of the image to make the logo seem like in an odd position.

So I just said "screw the HIG" and made splash screens that are black with Oplop's icon in the middle. The app already doesn't operate like a native iOS app anyway so one more faux pas that I have committed doesn't bother me.

Why can't stores agree on promo image sizes?!?

To continue with the theme of complaining about images, the Google Play Store wants promotional images and such at a different resolution than the Chrome Web Store. It also entails a new set of screenshots in the rather slow Android emulator. I can understand the need for new screenshots, but having to redo the promotional image is going to be a right pain (which is why I have not just gone ahead and released on the Play Store).

Happy with the process overall

Granted I had to figure some things out, but the team is very responsive to bugs so I got answers to questions and issues quickly. And Oplop also uses a permission from Chrome that isn't supported yet so I had to worry about pulling in a separate Cordove plug-in (which is easy to do), so it might not be typical. If your Chrome app's permissions are supported by Mobile Chrome Apps then it should really be just a cca create command plus verifying your app works as expected to go from nothing to Android & iOS apps. So if you have a Chrome app but want to get in on the native app fun to work around browser restrictions without writing a platform-specific version I think Mobile Chrome Apps is a good solution.