What's this?

WhereBlogger is a tool for Android phones, for keeping a location-based journal.

WhereBlogger can automatically collect GPS points for you, or you can add them manually. When an internet connection is available, maps will be downloaded for each logged point, and you can annotate each point with a title and a longer description. The program aims to be kind to your phone's battery and does not require a data connection or switch the GPS on at random times, as both electricity and data can be scarce while traveling.

Everything recorded by WhereBlogger can be published online (to a blog of your choice), or exported to the SD-card and copied to your PC to use as you see fit.

You can download WhereBlogger from the Android market (or just view the listing here).

Tuesday, October 27, 2009

Choosing an Android Maps API key at run-time

This is a little hint for any Android developers out there.

When using the Google MapView component in an Android app, you need a Maps API key which matches your app's signature. This is all fine and dandy, and pretty well explained on Google's web site.

However, a side-effect of how this is done means most developers are working with two different signatures on a daily basis: the signature of the debug key, which is autogenerated by the Android SDK, and their "production" signature which they use for publishing their apps to the market. Working with two signatures means you need two different Maps API keys as well, one for debugging and another for releases.

I didn't want to have to hand-edit constants in my code each time I built a release, and couldn't find any hints online on a more elegant solution, so I came up with the following snippet of code. This code will, at run-time, check which key was used to sign the app and then choose between two Maps API keys, based on whether it is a debug key or not.

static final String DEBUG_TAG = "YourApp";
static final String DEBUG_SIG = "...";
static final String DEBUG_KEY = "DEBUG KEY GOES HERE";
static final String RELEASE_KEY = "RELEASE KEY GOES HERE";
static String staticMapsApiKey = null;
public static String getMapsApiKey(Context context) {
if (staticMapsApiKey == null) {
try {
staticMapsApiKey = RELEASE_KEY;
Signature [] sigs = context.getPackageManager()
.getPackageInfo(context.getPackageName(),
PackageManager.GET_SIGNATURES)
.signatures;
for (int i = 0; i < sigs.length; i++) {
MessageDigest sha = MessageDigest.getInstance("SHA-1");
sha.update(sigs[i].toByteArray());
byte [] digest = sha.digest();
String str = "";
for (int di = 0; di < digest.length; di++) {
str += Byte.toString(digest[di])+":";
}
Log.d(DEBUG_TAG, "Got key sig: " + str);
if (str.equals(DEBUG_SIG)) {
Log.d(DEBUG_TAG, "Oh look, a debug signature!");
staticMapsApiKey = DEBUG_KEY;
break;
}
}
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NameNotFoundException e) {
e.printStackTrace();
}
}
return staticMapsApiKey;
}

This code is admittedly rather ugly - a more experienced Java developer would probably write something a bit more elegant. But it gets the job done, once you have filled in all the constants at the top. The DEBUG_KEY and RELEASE_KEY strings should contain the keys you get from Google. The DEBUG_SIG constant can be filled in by running the code in debug mode, and searching the output of "adb logcat" for the "Got key sig: " message.

Hope this helps someone, and if anyone knows of a more elegant solution, please let me know!

Update: Sure enough, someone came up with a more elegant solution, based on this one. Cool! :-)

6 comments:

  1. Hy Bjarni,
    How can I get RELEASE_KEY from google?
    Thanks.

    ReplyDelete
  2. Not sure how to make clickable links in comments here, but this is the page you want:

    http://code.google.com/android/add-ons/google-apis/mapkey.html

    ReplyDelete
  3. Thanks Bjarni, but I still not understanding this:

    " Note that, when you are ready to publish your application, you must get a Maps API Key that is based on the certificate that you will use to sign the application for release. You must then change the Maps API Key strings referenced by all of your MapView elements, so that they reference the new Key. "

    ReplyDelete
  4. When developing an Android app, you will generally be building APKs signed with a 'debug certificate'.

    When you publish an app to the market, you will sign your APKs with a different certificate. Read the Android docs on publishing for details on how this works, I think this may be what is confusing you.

    Last I checked, the Maps API key is linked to the certificate and my code snippet above eliminates the need to make the change described by Google in the text you quoted - instead you want to invoke my function wherever you would otherwise have referenced the constant key.

    ReplyDelete
  5. Your example helped me to get on the right track, thanks! Had some issues, so simplified it a bit (basically just using the signature object's hash value) and to use different xml layouts instead of adding the view from the code.

    Full example here: http://stackoverflow.com/questions/3029819/android-automatically-choose-debug-release-maps-api-key/3828864#3828864

    ReplyDelete
  6. Thanks for the update, Bachi, glad my post helped!

    I've updated my post with a link to your (more elegant) solution. Nicely done.

    ReplyDelete