It's ALIVE!!!!

AdSense Dashboard 3.1 is alive!!!

For those who've been wondering, life's been a little busy lately. Since moving to Mountain View, I've taken over the role of TL lead and Manager on the advertiser's side frontend of our Display business at Google, what we call the 'Content AdWords Frontend' in Google.

That's left me with precious little time; I first knew about 1.2 six months or more before it got released, and knew I'd have to migrate the dashboard to the API - but I ended up moving to Mountain View to take on this role before I had the chance to do so.

Now that my other major project, Search and Display Select is launched, I have a moment to take a breath and fix the dashboard.

Now, keep in mind I still want everyone to move over to the official app - but it's also true that it's 4.0+ only, and I supported folks on Froyo.

Froyo is no more. If you're still on Froyo, go buy a phone, the OS is much nicer now and you'll be happy you did.

So the AdSense Dashboard is Gingerbread or later now. I've gone and changed a few things to make it easier to maintain and take some of the pain away - including to moving to Play Services for authentication. (Auth used to be particularly ugly under the hood.)

New navigation hierarchy

  • Local TimeZone everywhere. Everyone but Google thinks in their local timezone. So timezone reporting isn't optional; the app works in the timezone you gave AdSense.
  • A new widget that supports resizing and lockscreen use.
  • Goodbye, ViewPager. It was broken anyways, and we now have way too many reports to just blindly page through.
  • Hello, Navigation The new design paradigm on Android is an ActionBar button linked to a navigation drawer; now that we have navigation, we've added more reporting.
  • New reports Ad unit and site reporting have been added.
  • More data A full set of metrics on all of the reports we show.
  • Pull To Refresh Because I was wrong, Nick.
  • New icons While playing with the navigation drawer I found we needed some kind of visual indicator. I wanted scaleable icons that worked at all DPIs, but was way too lazy to actually go and make icons of all of those sizes. Enter FontAwesome, a font with a host of icons of just the right style and use case; that, plus a customised TextView that supports specifying a font, and a bit of aggressive caching of typography, and we've got some icons in the app now.
  • Use of typography I switched everything over to the fonts that are used in JellyBean and KitKat, Roboto. This is temporary, until I can find (or get around to buying for app embedding) something like Trafalgar and Requiem
  • API 1.4 introduced a change I've been begging for since the first version of the API; at least some of this happened because they're now seeing these problems for themselves as users of the API.
  • Rewrite of networking I rewrote the networking to make a single batch request at the same time I moved to Play Services for authentication. The refactor cut the amount of code I had in the app by more than half.
  • Use of Play Services SDK Play Services adds a lot of critical support for doing auth properly across a wider range of devices.

And, of course, moving off of the v1.2 API, which is what broke the app for all of October and November.

I did this in two stages - a 3.0 release in November, and a 3.1 release just a few days ago to make use of some of the earlier cleanup.

Along the way, I cleaned up a bunch of code, imported the 1.4 libraries, followed the daisy chain of required updates, moved to Android Studio, deleted all of that and switched to the maven repository, fixed all the maven conflicts, updated to later versions of support libraries, rewrote a bunch of stuff that the support library changes broke, etc. This has been, undoubtedly, a massive yak shaving exercise; but it's better off now.

We'll see where things go next; as always, send me your feature requests, complaints, and general chatter to the support address.

AdSense Dashboard Update

TL;DR: App broken, fix coming this weekend. AdSense API we were using has been replaced by a newer version. App will be updated this weekend with fixes and new features. In the meantime, use the Google Adsense app in the Android Market.

So since I write that last message, I found myself with an unusual job offer; I left the AdSense team in London, which I've been on for years, and moved to Mountain View to run the Display portion of the AdWords frontend and help advertisers make sense of our display ads business.

So my life has been a bit upside-down lately, and that's meant I've had very little time to launch the version of the app that wouldn't have broken when the rest of the AdSense API team did their launch. Most of my life is still in boxes, there's IKEA furniture stacked in little flat-pack cardboard boxes in our new house in Noe Valley, and the dog sees me for about three hours a day.

None of those things are excuses for having failed to deliver you an update, but I hope they go some way into explaining why it didn't happen in time.

I'm very sorry the AdSense Dashboard isn't working for you today. I'm going to fix this very soon - this weekend - and will iterate a few times over the weekend to clean up a few long-standing bugs and handle a few errors and crashes that the occasional person has reported.

Now, it's important to state clearly that this app isn't going to live forever. I really want everyone to migrate to the official app - I worked hard to get approval for that team to get officially sanctioned and to build the best possible user experience for you and the rest of our AdSense users, and they're working hard to build out a fantastic feature set, and already has features I don't offer, like notifications. If that app has the feature set you're happy with, switch now; if it doesn't, tell them what you most wish it had by providing feedback on the app, which they're watching closely.

I miss my old team. I'm so proud of the app they've built, and the UX team has done an amazing job of making something not just functional but beautiful. Now's a great time to go and try their app - again, if you haven't used it recently - and remind yourself of what having full time engineering on making a great user experience can achieve, rather than some guy in his 20% time.

If you have any comments or questions, feel free to contact me.

Thank You, Beta Testers!

So the secret can now be told - AdSense Dashboard has been a skunkworks project to test Google’s prototype AdSense API.

Together, we generated enough traffic to verify the production-readiness of the API; thanks for enduring the occasional downtime as we got the API ready for the masses and for its public release, and thanks to the AdSense API team for doing such amazing work under difficult time schedules and a rather large amount of pressure from me on the shape and form of the API.

This isn’t the end of AdSense Dashboard - in fact, it’s just the beginning. But for now, the limelight belongs to our API launch.

Read the complete Adsense API Announcement

If you’d like more information on the API, you’ll find the public documentation is now available. Now that we’re public, I’ll be changing the auth method from ClientLogin (which reported “AdSense” as the permissions it wanted when you attached an account) to the OAuth endpoint for the API, at which point it will tell you that it wants read-only access to the API, further limiting the set of permissible actions that can be taken by the code.

Thanks again to everyone who’s installed the product and helped us test our API.

AdSense Dashboard 2.6

I’ve been pumping out dashboard changes for the last few days, based on feedback, bug reports, and general email discussions with users. Keep them coming - if you use the app, I want to hear from you.

Users on Eclair/Froyo got bit by a pretty nasty bug in the HTTP stack built into the phone; the common fix appears to be to not use that API on those platforms, so that’s what I’m doing.

New stuff:

  • Support for disabling auto-refresh.
  • Bugfixes for Eclair/Froyo. If it’s not fixed for you, I want to hear about it.
  • Device-specific compatibility bugfixes around currency and rounding.
  • Google TV support. This is a bit slapdash - I’ll improve it as I improve the tablet layout.
  • New widget (released as part of the 2.5 chain) by a contributor/coworker.

There’s a lot of activity going on this weekend and early next week; I intend to do a lot of ‘remastering’ of the numeric portion of the display to show more comparative data - today versus yesterday, today verus one week ago, this week verus last week, this month versus last month, etc., for a broader set of metrics than we currently include.

That will also be extracted into widget form - choose an arbitrary 1x1 fact.

Lifetime spend, custom channels, and url channels will also show up, in all probability.

Your feedback isn’t just welcome - it’s wanted. Ping me with your gripes, comments, suggestions, and requests.

Weathrman 4.0: By Popular Demand

I’ve got a shiny new Xoom, and now you have a shiny new Weathrman client.

The biggest changes here are all around two user stories I’ve heard more than anything else:

  • I installed the app, but it doesn’t do anything and I can’t find it.
  • I need more control over how the application uses data on my device.

To address the former, we’ve got a launcher, dialogs on preview, and an embedded usage guide to help the user get the live wallpaper selected on their devices.

To address the latter, we’ve now got a pretty good story on data controls:

  • We now obey background data settings. If you turn off background data support, you can still refresh the wallpaper manually, but all automated, non-interactive network activity is stopped.
  • Support for disabling use of 2G/3G and WiMAX networks, for those who want wifi-only.
  • Control over walllpaper refresh rate.

Now that we’re allowed a lot fewer search terms, we’re doing a lot more fan-out of requests to flickr; a single user can now result in about 5,000 flickr requests, fired off in under 30 seconds. The pressure is on to find better search terms and filter terms, and so our next steps will be to take advantage of the prediction API and start using machine learning to make better decisions.

Another weekend, another update...

This weekend, I’m doing something I’ve been working on for a few weeks now, and am working on tidying up and preparing for release.

I’m a recent LOVEFiLM user - fell in love with the service fairly quickly, when Majin and the Forsaken Kingdom showed up at my door.  There are films that I haven’t seen but wanted to, but won’t go through the cost of buying on blu-ray.  There are, more importantly, games I’d like to play, but won’t buy because, quite frankly, they’re just not good enough, or probably not the kind of game I’m likely to finish.

But what I’ve found is that my interest is…  fleeting.  I know that I wanted to have seen a film when someone mentions something that triggers that memory - but unless I can do something about it right now, it’s never going to happen.

What you really want is a mobile client, oriented towards search and discovery, for helping you quickly get to (and add to your queue) films you’d like to see.

Working title was liveFiLM, but that’s a violation of their naming policy.  For now, it’s Phantoscope.


Weathrman 3.1: The Freshmaker

Lots of minor tweaks going into this.

For a long time now, doubletapping has been problematic.  I install a double-tap handler the same way anyone else would, but end up seeing events that belong to the foreground application.  Android has a mechanism for wallpapers to see only two possible gestures out of all of the ones an Android can handle: a tap, and a drop.

I’ve constructed a (very) simple double-tap handler out of the tap events sent via onCommand(), instead of installing a gesture detector, in the hopes of getting rid of these ‘false positives’ that result in the information-about-this-image dialog appearing too often.

I’ve also removed, you might be happy to hear, all analytics tracking.  Its primary use was to send back stack traces and errors back, so that I could track down client problems.  New features in the Android Marketplace for publishers means I should be getting stack traces directly in the publishing console… but I don’t, because they’re not being seen, because I’m sending those stack traces (in a much less efficient form) to Analytics.

So, analytics is gone, which will result in a better experience for all.

I’ll be pushing this onto the market shortly.

Guicing Android: Mobile Antipatterns

Dependency Injection via Guice is great, but it does what you tell it.  Sometimes, we need to do things that are counter to instinctive programming ideals when building agile, testable mobile applications to work around the constrained resources of mobile environments.  Spotting these problems in code reviews isn’t always easy, because most of them cross the boundary layer of a single class.  Their impact is measured on the cost of injecting a new instance, or the cost of constructing the injector itself.

Android has two major (and, generally, obvious) limitations that everyone using Guice should be aware of:  Call stack depth isn’t infinite, and Guice’s implementation is stack-hungry.  Here are some things to avoid if you’re looking to keep your stack footprint low:

  • Long constructor chains
    Foo needs a bar to construct.  Bar needs a Baz.  Baz needs a Bat.  Guice is going to eat a big chunk of call stack to find a constructor, find the parameters to that constructor, locate constructors for the parameters, etc…  Before you know it, you’ve blown your stack and thrown an error.
    Break these up by using field or method injection in place of constructor injection to shrink the stack depth, or using provider methods.  Where they make sense.  The problem with this, of course, is that the resulting fields are non-final, and that makes my eyes bleed to read.
  • Large object graphs
    I’m not suggesting composition is bad.  In fact, composition is almost always better, especially when it comes to anything remotely complex.  But think carefully when you compose, and try to find ways to use the composed instances lazily, by injecting a provider of the instance in place of the instance.
    Think about whether or not the class you have is just plain doing too much orchestration; consider doing a bit of functional decomposition of the problem domain.
  • Costly instance initialization
    The cost of creating an instance can be high, too - and not purely due to constructor chains being long. Consider delaying work outside of the constructor.  Think about whether the instances you’re creating in your constructor should be delayed until the method is called, and use providers of instances to move that cost.
  • Costly class initialization
    Arguably, one of the whole points of using injection is getting away from hand-built singletons and static references to critical objects.  If you’re doing this, you should think long and hard about whether you really want all that construction to live in the classloader, rather than when you needed it.
    If you’re looking to solve the robot legs problem, do it with annotated types.  @Left Leg and @Right Leg are better than private static final Leg LEFT and RIGHT, and push the job of construction down to its use, and out of the classloader window.
    If you’re looking to solve a singleton problem, use a @Singleton.  Avoid it where it makes sense to do so.
    Last, if you’re trying to build complex maps, consider Guava’s MapMaker implementation, which can build maps of complex types by supplying a generator function at the time of lookup, rather than at the time of wrapper class creation.
  • Contexts and Quasi-Singletons
    If you’ve got an object with a strange lifecycle, create that lifecycle - @Request scope is a great example in Servlet-land, and you’ll doubtlessly find others if you look around carefully.  It separates the lifecycle from the code that depends on the data, so that you’re not littering your code with details about what needs to live when and where, or creating messy context objects that might better have been represented by a scope.
  • Singletons and Static Injection
    A well-placed singleton, along with a bit of constructor chaining and lots of tight coupling, can load your whole object graph when your app launches, stopping up your startup.  Think long and hard about what really needs to be a singleton; inject providers instead of instances where possible to reduce the cost of that singleton.
    When using static injection, think about how you’d have written your code to avoid it, and then do that.  When you can’t, use providers of instances in place of instances to decouple where possible.

Guice won’t magically make your application lazily load its required classes and instances.  You can burn memory and stack by building highly dependent object graphs - Guice isn’t afraid to throw a verbose stack trace, as you’ve probably encountered previously; what it does is complex, and while it’s possible to inline all that code, it wouldn’t leave Guice very maintainable.

Constructors should ideally be slim and svelte.  They should ensure they have exactly what they need to be constructed with, and those things which can sensibly be delayed until first use should be.

Ultimately, Guice is doing what you ask it to; if you ask it to build you a tightly coupled object graph, it will do so.  You have the tools to implement lazily-dependent object graphs, should you choose to do so - and in mobile environments, where latency is critical, you should make every effort to do so in critical areas of your codebase.  Record the amount of time it takes to perform critical operations; you can even report that back to centrally through metrics gathering like the Google Analytics client, which will give you a feel for your performance profile on different devices and operating system revisions. 

Make metric-driven decisions.  Don’t prematurely optimize, and when it comes time to do so, make careful decisions about decoupling.  Gather metrics from your deployed applications to help you locate problems and improve quality.

I wish you luck.