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

Filterable ArrayAdapter Followup

November 9, 2010, 6:01 pm

A couple of points to follow up my previous ListView post:

Top left hand corner bug with the workaround

There is actually a hack that will force the ListView to re-request the section names, but unfortunately it causes the section name overlay view to re-render at the top left corner of the screen:

@Override protected void publishResults(CharSequence prefix, FilterResults results) { // ... Turning fast scrolling off before updating our // index and then turning it on again will cause // getSections to be called, but it messes up the fast scrolling // section name overlay lv.setFastScrollEnabled(false); updateListIndex (); lv.setFastScrollEnabled(true); // ... }

How AlphabetIndexer does it

When I was writing this post I discovered the AlphabetIndexer class, a helper class that implements the sectionIndex interface:

If the items in the adapter are sorted by simple alphabet-based sorting, then this class provides a way to do fast indexing of large lists using binary search. It caches the indices that have been determined through the binary search and also invalidates the cache if changes occur in the cursor.

Having a look at the source for this guy, we can see that it uses a similar fixed set of section names that are intialized based on the alphabet passed into the constructor. So if you are filtering a large list with a diverse range of section names, you will have the same problems that exist with my SectionIndexer hack.

Permalink - Comments - Tags: Development,Android,Google

A Filterable ArrayAdapter with Fast Scrolling in Android

November 6, 2010, 12:30 pm

I spent today at the Google Sydney offices for an Android Developer Lab. It is my experience with events like this that you pick up handful of useful bits of information from the presentations, but the real value is the people you talk to during the day and the stuff that you build during the coding labs. My time in the lab was spent chasing down an issue with ListViews, Filterable ArrayAdapters and setFastScrollEnabled.

The issue raises its head when you implement a filterable and section indexed ArrayAdapter for your ListView and then call setFastScrollEnabled to enable the thumb button fast scrolly thing on the right hand side of the list:

Enables fast scrolling by letting the user quickly scroll through lists by dragging the fast scroll thumb. The adapter attached to the list may want to implement SectionIndexer if it wishes to display alphabet preview and jump between sections of the list.

Filterable ArrayAdapter

The first part, building a filterable android.widget.ArrayAdapter, is pretty straightforward. You implement the android.widget.Filter abstract class and store it as a member of your ArrayAdapter subclass. ArrayAdapter has a getFilter method which returns your custom Filter implementation to the ListView.

The Filter interface has a couple of methods that let you create a subset of your ListView data based on the search term that user types on the keyboard. Called in a worker thread performFiltering does the actual work adjusting the ListView content based on the CharSequence parameter. The publishResults method publishes the results of the filtering operation to the UI thread. The publishResults method will look something like this:

@Override protected void publishResults(CharSequence prefix, FilterResults results) { //noinspection unchecked mItems = (ArrayList<GlossaryEntry>) results.values; updateListIndex (); // Let the adapter h about the updated list if (results.count > 0) { notifyDataSetChanged(); } else { notifyDataSetInvalidated(); } }

If we have some data after the filtering operation, then tell the ListView that its data set has changed via the notifyDataSetChanged method, otherwise just tell it to invalidate its contents (so it can render a blank view).

This method is significant for what I am going to talk about next, because it is the mechanism with which I can tell the ListView that it's stuff has changed and it needs to re-draw. I'll come to that in a minute.

SectionIndexer implementation on ArrayAdapter

SectionIndexer is the interface on the ArrayAdapter that allows the ListView to find out how the items should be broken up by section and what text to display as the fast scroll UI is dragged on the right hand side of the view:

Interface that should be implemented on Adapters to enable fast scrolling in an AbsListView between sections of the list. A section is a group of list items to jump to that have something in common.

So this interface has three methods:

  • getPositionForSection(int section) - Provides the starting index in the list for a given section.
  • getSectionForPosition(int position) - This is a reverse mapping to fetch the section index for a given position in the list.
  • Object[] getSections() - This provides the list view with an array of section objects.

So when I initilise my ListView, you need to build an index to map from items to sections, sections to items and the list of section names. In my first implimentation of this stuff I did this organically based on the actual items in the list. I assumed that when I called notifyDataSetChanged the ListView would call getSections on the SectionIndexer so it can get the section names that correspond to the new content. Alas, this is not the case.

public ListIndex (int size, Delegate d) { // Build an index map m_sectionNames = new ArrayList<Object>(); m_sectionForPositionArray = new ArrayList<Integer>(); m_positionForSectionArray = new ArrayList<Integer>(); String prevSection = ""; m_Size = size; for(int i = 0; i < size; i++) { //String sectionName = names[i].substring(0, 1); String sectionName = d.sectionNameForIndex(i); if(!prevSection.equals(sectionName)) { m_sectionNames.add(sectionName); m_positionForSectionArray.add(i); prevSection = sectionName; } m_sectionForPositionArray.add(m_sectionNames.size() - 1); } }

So this implementation will build an m_sectionNames array that will be different for each subset of the ListView content. Because getSections isn't called when you publish a filtering operation to the listView, you will get index out of range errors. The solution I came up with during the lab was to build a section names array based on a fixed alphabet, rather than the variable contents of the ListView:

public ListIndex (int size, Delegate d) { // Build an index map m_sectionNames = new ArrayList<Object>(26); m_sectionForPositionArray = new ArrayList<Integer>(); m_positionForSectionArray = new ArrayList<Integer>(26) ; for (int j = 0; j < 26; j++) { // populate all the position for sections with -1 m_positionForSectionArray.add (-1); // add the section names for the alphabet m_sectionNames.add (String.format ("%c", j + 65)); } // now run through the index int currentIndex = 0; for(int i = 0; i < size; i++) { String sectionName = d.sectionNameForIndex(i); char c = sectionName.charAt(0); int sectionIndex = currentIndex; if ((int)c >= 65 && (int)c < (66 + 26)) { // if this term has an alpha first character, store the section Index sectionIndex = (int)c - 65; currentIndex = sectionIndex; } // set the position for section array with the first value we find if (m_positionForSectionArray.get(sectionIndex) == -1) m_positionForSectionArray.set(sectionIndex, i); // add the section for position item with the last valid sectionIndex we got m_sectionForPositionArray.add(sectionIndex); } // now we have to run through our position for section and make // sure we don't have any gaps left currentIndex = -1; for (int i = 0; i < m_positionForSectionArray.size (); i++) { if (currentIndex == -1 && m_positionForSectionArray.get(i) != -1) { // have our first index ... go back through the array // and set the values currentIndex = m_positionForSectionArray.get(i); for (int j = 0; j < i; j++) m_positionForSectionArray.set(j, currentIndex); continue; } if (m_positionForSectionArray.get(i) != -1) { currentIndex = m_positionForSectionArray.get(i); continue; } if (currentIndex != -1 && m_positionForSectionArray.get(i) == -1) { // fill any gap values with the last index m_positionForSectionArray.set(i, currentIndex); continue; } } }

So this implementation is less than ideal for a couple of reasons. It will miss-categorize items that don't start with an alpha character and it will include sections with no entries (the index is forced to the next available item). It would be much nicer if the ListView just re-grabbed the section names when you call notifyDataSetChanged.

Permalink - Comments - Tags: Development,Android,Google

Patrick O'Brian Mapping Project in Google Maps V3

July 2, 2010, 5:17 pm

I spent the workshop at todays DevFest porting the ancient version two javascript in the Patrick O'Brian Mapping Project over to version three:

The old implementation was accessing raw XML file via the old GXMLHttp object. This is an AJAX wrapper object that was in v2 because, at the time, there wasn't an alternative. In the modern world of javascript libraries, there are lots of good implementations of an AJAX request object, so v3 didn't need to include GXMLHttp anymore. The new implementation uses a Mootoos request object and grabs JSON from the database rather than raw XML files. This is something I have meant to fix for a long time.

The old Google Maps v2 GPoint object has an x and y property (longitude then latitude), so when I moved over to v3's LatLng object, all my coordinates were backwards, leading to amusing results:

Once I had the map working in v3, I was able to take advantage of the styled maps features in v3 and give it a slightly more old world feel:

Not quite ready to make the port live, the links between info windows aren't hooked up yet, but it is almost there.

Permalink - Comments - Tags: Google,Patrick O'Brian,Development

Google App Engine and Chrome, but mostly people, at #devfestau

July 1, 2010, 1:31 am

I have spent the last two days at Google DevFest 2010 in Sydney. The focus on Tuesday was Google App Engine and today we looked at Google Chrome, HTML5 and tools for the next generation of web applications. The talks were very interesting and I feel like I learnt something on both days, but the real value was in the people I met and the conversations I had.

Tuesday, 29th June - App Engine, Python and Scaling It

I have some web projects (including this blog), built on the LAMP stack and cobbled together in ways that would make a Scalability Engineer cringe. These sites work, but they won't scale and thanks to my fairly narrow demographic (Patrick O'brian fans and military history buffs), they haven't needed to thus far. However, over the last few months, this has started to change. My suite of iPhone applications have started to generate a respectable amount of traffic and I have had to start entertaining the idea of a more scalable backend architecture.

So this backend requirement, an embaressingly fervent love for Python, an interest in meeting Don Dodge in person and the gigabyte of bandwidth for free (yep, I am cheap) made the App Engine session a sensible choice.

Wednesday, 30th June - Chrome, HTML5 and the death knell of IE6

I have dabbled with Chrome extentions before and wanted to check out what was on the horizon with Chrome and HTML5.

I found the presentations on the new stuff coming in HTML5 compelling and frustrating at the same time. Basically this stuff is awesome and people are going to build some fantastic stuff with it, but until it is supported in a majority of browsers, I don't see how you can afford to spend much time on it. This is the nature of the business I suppose, but I am impatient, I want to use the new shiney stuff now!

I had a bit of fun playing around with javascript and SVG support in HTML5 during the coding labs at the end of the day. If you are using a modern browser you will see my logo blinking at you, if you aren't, then not so much. This is basically some Javascript accessing the DOM in an SVG file and messing with the properties:

People

Some of the people I met ...

Pamela Fox

As usual, Pamela did a kick ass job. She's a great developer, very smart and genuinely passionate about building awesome software.

Don Dodge

I had seen Don Dodge interviewed on This Week in Startups and had followed his move from Microsoft to Google, so I was pretty keen to meet him in person. I thought his talk about how the web has evolved and is continuing to evolve was pretty insightful and, given his move from the heavyweight client world of Microsoft to Google and the cloud, I think this guy gets it.

I overcame my natural tendency to hide behind furniture and forced myself to step up and say hello. I found the man pretty approachable and had an amiable, if somewhat brief, conversation about how developers can benefit from the various cloud platforms and in particular how I can leverage it for mobile applications.

Alan Noble

I met Alan at the Mashup Australia hack-a-thon a few months ago, so I jumped at the chance to sit down with him and some other developers over lunch. Alan is the head engineer for Google Australia and had some interesting things to tell us about the day to day operation of Google from a developer perspective. I was particularly interested to hear about Australian Googler's involvement with the Cyber attacks on Google servers early this year. Alan described how Google security engineers are working around the clock (around the globe) on tracking and preventing cyber attacks. I wonder how many other companies were vicitims of the attacks that Google detected, but were completely unaware of it.

Patrick Chanezon

A very friendly French Googler in San Fransisco. This guy did an awesome presentation on App Engine for business and later showed how he fed his delicious bookmarks into the Google Prediction API to build personally meaningful tags for arbitary web sites. Very cool.

Halton Stewart

I met this guy towards the end of the day yesterday when he was having a chat with one of the Google guys about scaling the web apps that he had built. As far as I am concerned this guy is the poster boy for independant developers building scalable web sites on App Engine.

You might have seen advertisments on the web for a certain viral empire building game that has gained some notoriety for it's particular style of advertising. This notoriety probably contributed to a certain amount of self deprecation when Halton was describing what he had built. Basically he took a feature missing from the game and built a set of sites to satisfy that need. It turns out that 100s of thousands of other players wanted this feature too. His sites have done incredibly well and are generating real ad revenue every month. A couple of points:

  • This is what being an awesome developer is all about. Halton had an itch and scratched it, built something awesome, that has real value for 100s of thousands of people.
  • He started out using app engine, having never used Python or the platform before, and taught himself by 'doing'. Also, full of awesome!
  • He encountered some nasty problems along the way (like the Python image encoder not supporting text) and hacked until it worked. Again, awesome.
  • He made a choice to use App Engine rather than persisting with the familiar, but not so scalable LAMP stack, that he was used to. Outside the comfort zone, awesome!

So in summary, and with a little bit of re-iteration, this guy is crushing it and has absolutely nothing to be embarressed about. I was very impressed.

Geoff Mcqueen

Geoff gave a great presentation on Affinity Live and their experiences integrating Google Apps. Had an intesting chat after the talk about how they came to be a Perl shop and the resurgence of that language.

We're a Perl shop, which means nobody loves us.

Eric Bidelman

It is easy to take a simplistic view of the various software platforms that Google is working on right now. You might argue that building Chrome OS and Android are at cross purposes and that there is some confusion about which direction Google is moving in. Apps or web apps? Native code or portable code?

I had a long chat with Eric about the future of development, at Google and elsewhere, and got a very different impression. It is obvious that software is changing and that the focus is quickly moving away from the old desktop paradigms, towards the cloud and mobile. But this doesn't mean that desktop applications are becoming irrelavent, it just means they are going to be different. They are going to be more integrated with the cloud and they will run inside the browser, so we need modern browsers (and browser based operating systems) to host those applications. We will also need mobile platforms like iOS and Android to run native apps. Apps that are designed for, and take full advantage of, the hardware they are running on.

Gary Miller

I chatted with Gary about the future of spreadsheets, data visualization, GWT and Nick Lothian's awesome hacks.

Awesome

I talked to lots more people over the two days, but it is 2am, I am saying awesome way to much, have rambled on far more than I intended and really should go to bed. Goodnight! I am looking forward to seeing what is in store on Friday.

Permalink - Comments - Tags: Development,Google