What's wrong with Google Maps development on Android - Part One

April 12, 2011, 12:23 pm

Update 5th December 2012 - Android Maps v2

This rant is coming from an unabashed Google fanboy. I have always loved Google as a company and the maps API in particular. I write this in the hope that it will help Android become a better place for developers like me.

Also, all the hard work for this post was done by Nick Maher. He blazed the Android development trail for me and most of the insights in this post are his. Some of the hoops that he has jumped through makes Tripview for Android even more impressive.

This fairly inocuous sentence in the Hello MapView tutorial hides a nasty underlying problem with the Google Maps library:

This is a special sub-class of Activity, provided by the Maps library, which provides important map capabilities.

Basically it means if you want to display a google map inside an activity in Android, you need to derive from the MapActivity class. So this isn't so bad right? Well maybe not.

What if you have an activity, GenericToolboxActivity, that does a bunch of different things, one of which involves displaying a map? Forcing GenericToolboxActivity to derive from MapActivity seems kinda dumb.

You can get around that problem by sticking your MapActivity in a LocalActivityManager. The LocalActivityManager is basically a sandbox for your activity. You stick your MapActivity subclass, let's call her JealousSpouseMapActivity, in your LocalActivityManager and gently prod her until she gives up that MapView object that you so desire:

// set up the LAM m_lam = new LocalActivityManager(this, true); // stick the map activity in the LAM Intent intent = new Intent(this, JealousSpouseMapActivity.class); m_lam.dispatchCreate(null); m_lam.dispatchResume(); m_lam.startActivity("map", intent); m_mapActivity = (JealousSpouseMapActivity)m_lam.getActivity("map"); // the view please? return m_mapActivity.getWindow().getDecorView(); // Once I am finished with the view, clean it up m_lam.dispatchPause(true); m_lam.dispatchDestroy(true);

You are essentially lulling your JealousSpouseMapActivity into a false sense of security:

Dear JealousSpouseMapActivity, I would never re-parent any of your stuff. That would be wrong. Put your feet up and relax, i'll just need a pointer to your MapView before I pop off and get you breakfast in bed.

Once you have the MapView you can stick it in your view heirachy for your generic activity and JealousSpouseMapActivity is none the wiser. So why can't this stuff just be done in a MapView class instead? According to the MapActivity documentation its responsibilities include:

  • Activity lifecycle managment - So presumably most activities need to manage their lifecycle, so I wonder how much map specific stuff is done here.
  • Setup and teardown of services behind a MapView - Hrm. If we can have a WebView object that does requests in the background, fairly complex rendering and all sorts of other magical things, I wonder why we can't have a MapView that does the same. What is so "special" about a map that makes it impossible to implement as a view subclass.

In fact thinking about WebView brings up another argument against implementing the map this way. What if our helpful and friendly WebView had some kind of mental break and turned into AngryExGirlfriendWebActivity? What if we had to derive activies that show web content from AngryExGirlfriendWebActivity? If we want to show web and map content at the same time, basically we are screwed, we can't derive from them both. I guess we could try and stick both AngryExGirlfriendWebActivity and JealousSpouseMapActivity into a LocalActivityManager and then gently ask them to give up their respective view goodness, but I can't imagine that is going to end well.

Ok, Perhaps I have taken this analogy a little too far, but you get the idea.

This rant continues:

Permalink - Comments - Tags: Development,Android,Google

iPhone apps and cappuccinos

March 19, 2011, 11:04 am

Ok. I need to preface this rant with a couple of things:

I love coffee, it is totally worth the three bucks and that I have been doing 'on the side' development projects for 15 years and I never made any money untill the App store came along.

The other day Glasshouse Apps (awesome apps you should check out) had a small rant about users expectations for mobile app pricing:

Just read yet another app review from a well known blog which concluded that the $1.99 app didn't have enough zing for the hefty price.

This is not a new idea, but it is a contagious one, amongst app developers anyway.

Is an iPhone app that took months of technical work worth more than a cup of delicious coffee?

I have a handful of apps on the store, the first of them is a glossary of Ballet terms (I have daughters) and it is hard to imagine an app that is less complicated. It has a searchable index of terms, a callout to a view showing some HTML defining the term and a handful of images, it is a universal app (native iPhone and iPad), it has a simple self testing mode and keeps track of your favorite terms. So the minimum price point of 99c is appropriate for an app that took so little to build?

Actually this app took months and months of ongoing effort to build and update. The first 95% of the functionality took relatively little time to write, but the last 5% of polish took far longer. The content itself is the product of careful research and is a project that I imagine I will be working on for years to come. So why do I feel reluctant to charge more?

Mostly it's a fear of pricing myself out of the market, but it's also the understanding that before the iPhone came along, I spent months & months on programming projects that made zero money. Both of these arguments are closely tied to how programmers value our own work.

I have made a few thousand bucks from my Ballet glossary, and that is awesome, but it doesn't come close to the amount that the market values my time as a developer. So can app developers collectively raise our prices and our users expectations? Probably not, but it would be nice.

I am heading over to the app store right now to raise my prices. I am sure I will chicken out in a few weeks and drop them again, but it is worth a shot.

Permalink - Comments - Tags: Development,iPhone,iPad

More Podcasts

February 13, 2011, 11:48 pm

It's been a while since I talked about the podcasts I listen to and I have expanded my set of regulars a little:

  • Techzing - Two developers (Justin Vincent and Jason Roberts) natter about a whole host of interesting topics and interview some great people (I got into the show after the Jason Calacanis interview). Really enjoying this show. It is very easy to identify with these guys. This show has become one of my favorite tech podcasts.
  • This Developers Life - Since the demise of the StackOverflow podcast, this podcast fills the gap in my lineup for purely entertaining chatter about life as a programmer. Mostly great stuff ('Drive' went a little off the rails).
  • This American Life - The single greatest thing about 'This Developers Life' is that it introduced me to this show. Consistently brilliant.
  • The Pipeline - Dan Benjamin interviews a host of interesting creative types. I have gone back and consumed the entire backlog.

Permalink - Comments - Tags: Links

How do you make a demo video with Push Notifications in the iPhone Simulator?

February 10, 2011, 10:14 pm

I have been working on a new demo video for TingoFamily. I use SimFinger and the iPhone simulator to make my videos based on the fantastic article by Loren Brichter. This gets you pretty far when you want to put together a decent looking demo video, but there are a couple of things that it doesn't do.

Push notifications don't work in the simulator, so if you wan't to show off that functionality in your video, you are a bit stuck. I wrote a very quick utility that uses local notifications to simulate this UI. You can grab the source from GitHub then simulate both in app and home screen push notifications:

First thing you need to do is set the Bundle Display Name to the name of your app (this is shown in the local notification header). Then you need to update the image in the images folder and the backgroundImage property in FakeImageBackground.m to match your application. At the moment they are set to TingoFamily and Dad_Fake_NoPanic.PNG respectively.

Push notification from the home screen

  • Build and run the app in the simulator.
  • Type your required push notification message in the text box.
  • Click Schedule fake push. You have five seconds to get to the home screen.
  • Click Show an image view to display the full screen fake app image. FakePush should now look like your app when it starts up.
  • Close the app and go to your home screen on the simulator. If you have read Loren Brichter's post and followed his advice you will have a fake simulator home screen that looks like the real thing (with the appropriate Apple apps).

After five seconds the local notification will fire with your message, click view and it will go back into the Fake app, which will still have your fake home image showing. This looks exactly like the real thing.

Push notifications in the app

If you want to simulate the push notification inside the application it is pretty similar.

  • Run the app.
  • Click Schedule fake push. You have five seconds till the message pops up.
  • Click Show an image view to display the full screen fake app image. If you want the fake push to appear over a particular view in your app, just make sure the FakeImageBackground backgroundImage property is set to a screen shot of that view.
  • Wait five seconds and the local notification will pop up an alert.

So that's it. It is an extremely simple app, but you might find it useful. Once I finish the epic demo video I am working on (including lots of push notification smoke and mirrors), i'll post it here.

Update

Here is some of that video I have been working on (with some fake push notifications):

Permalink - Comments - Tags: Development,iPhone

UISearchDisplayController on the iPad

February 4, 2011, 12:09 am

As a programmer you can create incredible stuff in surprisingly short periods of time. It doesn't get much more satisfying that adjusting your music volume to the cusp of permanent hearing damage, hunkering down for a couple of hours and cranking out something awesome. That is not what happened today when I tried to get my UISearchDisplayController to work correctly inside a UIPopoverController, on the iPad, in portrait mode.

In the first version of my Glossary apps I implemented the search bar functionality myself. That is I created my UISearchBar, I created a semi-transparent view that went over the top of the table view and I mucked around poking the UITableViewDelegate to get it to show my search results. It was all a bit of a horrible hack (it being my first iPhone app), but it worked (on the iPhone, in portrait mode).

The new implementation of my Glossary framework is a universal app (iPad and iPhone) and designed from the start to support arbitrary orientation. It became apparent very quickly that my old search code was not going to make it into the new version. On the iPhone the orientation stuff was a mess and on the iPad I was having to do a re-write anyway to support the UISplitViewController layout.

So I checked out the UISearchDisplayController and discovered it was awesome. It was easy to hook up to my existing table view delegate and basically it just worked out of the box. Well ... all except for one tiny 44 pixel hiccup.

In portrait mode, when you click the search bar, the UISearchDisplayController code animates the search bar so it is flush with the top border of the UIPopoverController. This looks great except that the black semi-transparent view covering the table view doesn't get re-sized correctly, so it is left with a 44 pixel (the height of the header that the search bar now covers) gap at the bottom of the popover view:

It works perfectly in landscape mode, I suppose because the view is being displayed in the left split view pane instead of in a popover view:

This is clearly a pretty simple bug in the UISearchDisplayController implementation, but it ate up a couple of commutes for me to develop a fairly horrible workaround:

NSLog (@"%d subviews", [self.searchDisplayController.searchContentsController.view.subviews count]); for (int i = 0; i < [self.searchDisplayController.searchContentsController.view.subviews count]; i++) { id view = [self.searchDisplayController.searchContentsController.view.subviews objectAtIndex:i]; NSRange rng = [[view description] rangeOfString:@"UIControl"]; if (rng.length != 0) { UIView * backgroundView = (UIView*)view; CGRect frame = backgroundView.frame; frame.size.height = self.searchDisplayController.searchContentsController.view.frame.size.height; backgroundView.frame = frame; } NSLog (@"%@", view); }

So this code is spinning through the subviews of the UISearchDisplayController until I find one that looks like the semi-transparent black background over the search results. When I find it, I lengthen it to match the height of the searchContentsController. The good news is that this works, it finds the right view and sets it's height correctly. The bad news is that it happens after the view has been added as a subview and so the user sees it briefly in it's buggy shortened state.

This code is in the searchDisplayControllerDidBeginSearch callback on the UISearchDisplayDelegate delegate. I tried moving the code to the searchDisplayControllerWillBeginSearch callback, and can adjust the view size at that point (before the user sees it, but the bug, causing the erroneous height adjustment, exists sometime after that callback, because by the time the view is displayed it has been shortened again.

I was surprised Googling the issue didn't garner any results and it is possible this because I have done something stupid or perhaps off the beaten track. If you can see the flaw in my logic, please let me know.

I should probably log a bug on this ...

Before I wrote this post I imagined that someone might be interested in this tiny, irritating bug, but in retrospect I can't see why anyone would read this. If you did, well ... uh ... sorry about that. ;)

Update 7th February 2011

I was browsing my iPad and noticed that the Marvel app has a UITableView in a popover and it didn't have the issue. I realised they aren't using the setContentSizeForViewInPopover for the RootViewController (it was the height of the device). I am setting the content size for popover to 600 because I didn't want the table view to run all the way to the bottom.

Checked what happens when I remove the call to setContentSizeForViewInPopover from my RootViewController and the issue goes away :).

So to clarify, the UISearchDisplayController only fails to adjust the size of the background view if the UITableViewController has called setContentSizeForViewInPopover.

Permalink - Comments - Tags: iPhone,Development