Select a device with min API level <x>
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
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
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
iOS 6.0 in Sydney? No suburb for you!
Just discovered an interesting bug in the iOS CLGeocoder object.
At goCatch we use the reverse geocoder to get a good estimate of the passenger's pickup location, so having the passenger appear in the correct suburb is pretty important. When you get a response from the CLGeocoder you get the following fields:
@property (nonatomic, readonly) NSString *name; // eg. Apple Inc.
@property (nonatomic, readonly) NSString *thoroughfare; // street address, eg. 1 Infinite Loop
@property (nonatomic, readonly) NSString *subThoroughfare; // eg. 1
@property (nonatomic, readonly) NSString *locality; // city, eg. Cupertino
@property (nonatomic, readonly) NSString *subLocality; // neighborhood, common name, eg. Mission District
@property (nonatomic, readonly) NSString *administrativeArea; // state, eg. CA
@property (nonatomic, readonly) NSString *subAdministrativeArea; // county, eg. Santa Clara
@property (nonatomic, readonly) NSString *postalCode; // zip code, eg. 95014
@property (nonatomic, readonly) NSString *ISOcountryCode; // eg. US
@property (nonatomic, readonly) NSString *country; // eg. United States
@property (nonatomic, readonly) NSString *inlandWater; // eg. Lake Tahoe
@property (nonatomic, readonly) NSString *ocean; // eg. Pacific Ocean
The two that I am particularly interested in this case is locality and subLocality. Because we want to show the most relevant neighborhood to the taxi driver, we prefer subLocality over locality. This has worked fine right up until iOS 6. Unfortunately with the iOS 6 release, the API has started returning the two values reversed. So for Ultimo (suburb or neighborhood) in Sydney (city), we get back:
[locality Ultimo]
[subLocality Sydney]
[administrativeArea New South Wales]
I checked the same results for the mission district in San Francisco] and it is working fine. I guess Apple would have noticed if that area was similarly broken:
[locality San Francisco]
[subLocality Mission]
[administrativeArea California]
With the iOS 6 release we stated getting a lot of taxi drivers complaining about jobs coming up in "Sydney" as opposed to a specific suburb. Ouch!!
We have added an iOS 6 specific hack which swaps the values until Apple gets around to fixing the issue.
Permalink - Comments - Tags: Development,iPhone,iPad
48 hours of coding FTW! Literally
Last week Vodafone Australia held a 48 hour hackathon where developers team up with a charity to build an app to fix a pain point in their organisation. The winning charity receives a 30,000 prize decided by a panel of judges including Guy Kawasaki.
The dev team at goCatch decided to leverage our experience tracking taxis in realtime to help St John Ambulance with managing volunteer first aid workers in the field at events like New Years Eve in Sydney.
We built an app that we think will help save lives at these events and the judges agreed.
We won! Here are some photos from the 48 hours of lunacy.
Permalink - Comments - Tags: Development,iPhone,iPad
[First Page] [Prev] Showing page 8 of 40 pages [Next] [Last Page]