Select a device with min API level <x>

April 5, 2013, 4:00 am

Had a frustrating hour or so today trying to get Eclipse to run goCatch on an old HTC Desire running Android 2.2. Eventually found the solution so this is a note to self to avoid this in the future.

The problem was that old device was not showing up in the Android Device Chooser, who (notice how I anthropomorphize pretty much everything?) smugly asserted Select a device with min API level 11. This sent me into a little bit of tailspin because I had jumped feet first into moving over to Android Maps V2, ActionBarSherlock and Support Library goodness with the understanding that I could support devices all the way back to 2.2. I frantically went through all the dependencies I had recently introduced, looking for a minSdkVersion greater than 8 (Android 2.2). To my relief I didn't find any. I double checked my own manifest and made sure my minimum API was set correctly. It was:

<uses-sdk android:minSdkVersion="8" android:targetSdkVersion="17"/>

What the hell?! Why is the device chooser insisting I could only run on devices with API level of 11 or more? I dug through about a thousand (might have been less than a thousand) config screens in Eclipse looking for a obscure project property that was causing the issue, with no luck.

Eventually I decided I would reduce the targetSdkVersion down to 8 and see if I could build and run on the old device. I updated the manifest and did a clean build for good measure. Eclipse built my project and ran it on the device. Android Device Chooser was now happily insisting that I should Select a device with min API level 8.

Not really wanting to target API 8, I udpated the Android Manifest back to its original state and voila everything works. Hooray!

Permalink - Comments - Tags: Development,Android,Google

You might have forgotten to update your Android Maps V2 API key

April 5, 2013, 3:31 am

If you notice your logcat rapidly filling up with Failed to load map. Could not contact Google servers, you might have forgotten to update your Android Maps V2 key in AndroidManifest.xml.

<meta-data android:name="com.google.android.maps.v2.API_KEY" android:value="your_maps_key"/>

The maps object gets quite angry, but remains sadly inarticulate when this happens. Just sayin.

E/Google Maps Android API( 2277): Failed to load map. Could not contact Google servers. E/Google Maps Android API( 2277): Failed to load map. Could not contact Google servers. E/Google Maps Android API( 2277): Failed to load map. Could not contact Google servers. E/Google Maps Android API( 2277): Failed to load map. Could not contact Google servers. E/Google Maps Android API( 2277): Failed to load map. Could not contact Google servers. E/Google Maps Android API( 2277): Failed to load map. Could not contact Google servers. E/Google Maps Android API( 2277): Failed to load map. Could not contact Google servers. E/Google Maps Android API( 2277): Failed to load map. Could not contact Google servers. E/Google Maps Android API( 2277): Failed to load map. Could not contact Google servers. E/Google Maps Android API( 2277): Failed to load map. Could not contact Google servers. E/Google Maps Android API( 2277): Failed to load map. Could not contact Google servers. E/Google Maps Android API( 2277): Failed to load map. Could not contact Google servers. E/Google Maps Android API( 2277): Failed to load map. Could not contact Google servers.

Permalink - Comments - Tags: Development,Android,Google

What's right with Google Maps Android V2

March 16, 2013, 11:52 am

TL:DR The new maps are awesome. The API has been cleaned up (with all my previous complaints addressed) and the map itself is stunning. Beautiful. Fast vector maps viewed from arbitary camera positions.

Given my vocal compaints about the shortfalls of the old Android maps library, I would be remiss if I didn't comment on Google's rewrite.

Because maps are encapsulated in the MapFragment class, you can implement them by extending the Android standard Activity class, rather than extending the MapActivity used in version 1.

With this the main issue I had with the old library, the dependancy on the MapActivity, has been fixed. There is now no requirement to extend MapActivity and, my second issue with the old maps, no limitation on the number of MapViews that can be embedded in a single layout. Just to test this I put together a quick demo using a bog standard activity inflating a layout with two fragments:

<fragment android:id="@+id/map_fragment1" android:layout_width="match_parent" android:layout_height="200dip" class="com.google.android.gms.maps.MapFragment"/> <fragment android:layout_below="@+id/map_fragment1" android:id="@+id/map_fragment2" android:layout_width="match_parent" android:layout_height="200dip" class="com.google.android.gms.maps.MapFragment"/>

This took about ten minutes to set up and worked perfectly. A bit of a departure from my previous attempts to get two of the old MapViews on screen at the same time (also check out the fancy rotation on those maps):

So two of my three issues with maps are fixed. The third was actually pretty minor and in hindsight not such a big deal. There is still the requirement for a maps key which is associated with the signing certificate for your app. Sometimes looking back at a rant you wonder what you were thinking when you wrote it and this was one of those times:


In addition to these architectural improvements, the new maps API provides some pretty neat functional addtions. Maps are rendered using vector data, so smooth, arbitary camera changes are now possible. In addition to rotation and pinch zoom, if you stick a map in your Android app your users will now be able to slide two fingers onscreen to change the camera pitch (seeing markers laid out on a 3D map of sydney for the first time was pretty cool).

As you would expect from a maps API, markers (with info windows) are now fully supported (more on this next), but you can also add polylines and polygons pretty easily.

Adding markers to a map in v1 of the API was shockingly problematic. To illustrate what I mean, to get info window enabled taxi markers moving around on a map in the last version of goCatch I had to build all of this:

  • MarkerItemizedOverlay.java - Extending BalloonItemizedOverlay<OverlayItem> to manage the drawables that get used for markers, forwarding select and clear events from the baseclass on to delegates (in this case the map activity).
  • BalloonItemizedOverlay.java - Extending ItemizedOverlay<OverlayItem> to show info windows when OverlayItems are tapped and forwarding events down to the MarkerItemizedOverlay. Making sure that tapped items result in map view changes to center that item. Some jiggery-pokery (that's actually a thing) to ensure that only one info window is shown at a time.
  • BalloonOverlayView.java - A nice custom view with a nine part background drawable and some info about the selected marker.
  • MapViewWrapper.java - Ugh! The old mapview didn't have a nice OnCameraChangeListener so you had to put code in the draw method of a class extending the MapView and constantly check if the zoom or pan had changed.
  • PassengerMapActivity.java - Extending MapActivity and managing all the previously described bits and pieces so they reflect the position of the passenger and the current set of taxis on the map.

In these descriptions I am minimizing the amount of hacking involved to get all this to hang together in a reasonably usable way. It really was quite a lot of horrible code (especially MarkerItemizedOverlay and BalloonItemizedOverlay).

Last week I spent a couple of days ripping out all of that stuff and replacing it with code to use the new API and damn it felt good. This is what I have now:

  • BalloonInfoWindow.java - Basically BalloonOverlayView. We still need a nice custom view to show when markers get tapped.
  • PassengerMapActivity.java - Extends SherlockFragmentActivity (Yep, I can now extend whatever the hell I like and still show a map on screen). Grabs the MapFragment from the FragmentManager, turns on some awesome properties with a UISettings, listens on a variety of interfaces ( OnMarkerClickListener, OnCameraChangeListener, InfoWindowAdapter, OnMyLocationChangeListener, OnMenuItemClickListener) so it can react to map changes and provide a BalloonInfoWindow when someone taps on a marker.

Going through this code again to write this post, it is pretty striking how much an improvement the new API is.

The only hurdle I faced when switching to v2 of Android Maps was the limitation that you couldn't change the icon on a marker after it is created. At goCatch we like our taxi icons to glow when you tap on them, so I needed to jump through some hoops to get this to happen (deleting tapped markers and re-creating them with a new icon). After complaining loudly on the social networks (that's how I roll) I got a response that a feature request had been created to fix this (thanks Chris Broadfoot).

Here are the results of the couple of days I spent refactoring goCatch to use the new maps (also including a brand new Action Bar Sherlock):

My congratulations to the Android Maps team. You have taken a real pain point for Android devs and turned it into an absolute pleasure. I am looking forward to hearing from Android users of goCatch when we ship this new version.

Permalink - Comments - Tags: Development,Android

Photoshop hex RGB to a UIColor

February 2, 2012, 1:16 am

Adam (the awesome goCatch designer) and I got sick of converting from hex RGB colors to decimal UIColor initialisation params, so I wrote this script.

import sys if (len(sys.argv) != 2): print ("convert.py <hex color>") sys.exit () color = sys.argv[1] if (len (color) != 8): print ("convert.py <8 char hex color>") sys.exit () # get the two character chunks red = color[:2] green = color[2:4] blue = color[4:6] alpha = color[6:8] decRed = int (red,16) / 255.0 decGreen = int (green,16) / 255.0 decBlue = int (blue,16) / 255.0 decAlpha = int (alpha,16) / 255.0 #iOS code print ("[UIColor colorWithRed:%.4f green:%.4f blue:%.4f alpha:%.4f];" % (decRed, decGreen, decBlue, decAlpha)) #Android code print ("private static final int COLOR = 0x"+alpha+red+green+blue);

Yep it also does an Android static final int. We love Android too ;)

Permalink - Comments - Tags: Development,Android,iPhone

Introduction to MapView on Android - Codelab

August 25, 2011, 2:54 am

I am giving a talk about Android Google Maps development at the Android Australia User Group - Sydney meetup tonight. I hope you can make it.

I have put together a very simple code lab going over the basics for adding maps to your Android apps.

  • Before - A simple list based app that loads a 'Detail' activity.
  • After - Even list entries will load a native MapView based activity, odd list entries will use the Javascript Google Maps API in a WebView. Native views also geocode the name provided to set the map center.

This stuff is very much an introduction and much of it is covered in the Google Map View tutorial, but hopefully some people will find it useful.

Update

In my rush to get something together for the code lab I left some rather embarrassing, unsafe threading code in my "After" code lab.

I was calling set location on my MapView from my worker thread. Oops. Thanks to Darren Mason for pointing out this rather glaring error.

Make sure that you access the Android UI toolkit only on the UI thread.

I have updated the sample to use an AsynchTask to safely update my MapView when the blocking call finishes in the worker thread.

private class GeocodeTask extends AsyncTask { protected GeoPoint doInBackground(String... name) { return blockingGeocodeCall (name[0]); } protected void onPostExecute(GeoPoint result) { setLocation(result); } }

Permalink - Comments - Tags: Development,Android,Google