Deal with the unforeseen

(This post originally appeared on my blog)

It’s quite common to invite users to rate an app on Google Play at some point, on one hand it’s good to know that your users are happy and on the other it’s a good way to attract new users. It’s definitely not the only variable in the equation, but I can definitely say that user satisfaction is inversely proportional to the amount of crashes. But bugs are unfortunately something we have to expect as developers, even after testing our apps thoroughly. One thing we probably don’t want to do, is to ask a user to rate our app just after a crash since we can be reasonably sure that he’s not going to be too happy about it. How can we make sure that this doesn’t happen?

When an uncaught exception is thrown, the uncaughtException method of the defaultUncaughtExceptionHandler for that Thread is called. Good news is, you can supply your own UncaughtExceptionHandler using the setDefaultUncaughtExceptionHandler setter (this is whatACRA and I suppose other libraries like Crittercism or BugSense do under the hood). Here is a little snippet of what we do in the Qype app:

public class CustomUncaughtExceptionHandlerProxy implements Thread.UncaughtExceptionHandler {

    private static CustomUncaughtExceptionHandlerProxy proxy;

    private final boolean enabled;

    private Thread.UncaughtExceptionHandler customUncaughtExceptionHandler;

    private CustomUncaughtExceptionHandlerProxy(CustomApplication application) {
        enabled = !application.inDevMode();
        if (enabled) {
            // Save a reference to the current default handler.
            customUncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
            // Set our custom handler as the default one, so that it's first notified when something ugly happens.
            Thread.setDefaultUncaughtExceptionHandler(this);
        }
    }

    public static void initialize(CustomApplication application) {
        if (proxy == null) {
            proxy = new CustomUncaughtExceptionHandlerProxy(application);
        }
    }

    public static CustomUncaughtExceptionHandlerProxy getInstance() {
        return proxy;
    }

    @Override
    public void uncaughtException(Thread thread, Throwable ex) {
        if (enabled) {
            if (!preferences.rateAppDialogAlreadyShown()) {
                // reset count only if the 'rate app' dialog hasn't been already shown.
                preferences.resetShowRateAppDialogCountdown();
            }
            // Let the default uncaught exception handler do its job.
            customUncaughtExceptionHandler.uncaughtException(thread, ex);
        } else {
            Thread.getDefaultUncaughtExceptionHandler().uncaughtException(thread, ex);
        }
    }
}

This is a typical implementation of the Proxy design pattern, it’s pretty straightforward but here’s a little explanation of how it works. Let’s say you want to show a “Rate App” dialog after n times the app is opened by the user, you would use a counter and increment it each time the launcher Activity is created and it will show the dialog when the value of the counter is exactly n. But if the app crashes and the dialog is yet to be shown, you may want to reset it so that it won’t be displayed “too soon”.

No Comments »

Written by stefano on January 24th 2013. Category: Everything else

Location on Android…the Qype way!

Some months ago we released the first stable version of ignition. In this post I explaned how the ignition-location library works and how it’s possible to include it in an already existing application. This time I’ll explain how we integrated it in the Qype Android application.

A very typical use case when using our app is to look for a place to eat, drink or go party nearby. In order to display the best places around, the app needs to know the user’s location, and it needs to know it as quickly as possible – nobody likes to stare at his/her phone for 5 minutes waiting for something to happen right? Ignition-location is pretty good at that but there are a couple of things the Qype app itself must take care of. In particular, when developing our new location manager, we had two requirements in mind:

  1. a good trade off between quickness and accuracy is achieved.
  2. battery is not drained.

There are two key components in our location logic: one is QypeLocationManager, the other QypeNewLocationHandler. The former contains all the location logic, in particular it takes care of deciding if a location is good enough – I’ll go back to this in a bit – and if the app should keep requesting location updates, the latter handles UI-related behaviour. For the sake of completeness, there is a third component which is a small interface called QypeLocationAwareActivity implemented by all our location-aware activities.

So what happens when a new location is received? As you already know from the previous post, IgnitedLocationManager invokes the onIgnitedLocationChanged() callback in the current IgnitedActivity. Since we want our logic to be shared across all out location-aware activities, each of them has a reference to an instance of the QypeLocationManager and it invokes a callback on this object each time a new location is returned:

@Override
public boolean onIgnitedLocationChanged(Location location) {
return locationManager.onNewLocation(this, location, lastSearchedLocation);
}

This callback has got two locations as parameters: the current location and the location used for the last search. I’ll expain in the next few lines why both are needed. The onNewLocation() callback is the hearth of our location logic, It checks if it’s the first time the current Activity is making a request – in this case the Activity is probably being created for the first time – and, if that’s the case, it checks if the current location is not null and if it’s “good enough” or not. If it is, it invokes the onLocationAvailable() callback (defined in the QypeLocationAwareActivity interface) on the Activity, that will perform whatever task it needs to perform, i.e. searching the best nearby places. Here is a code snippet:

public boolean onNewLocation(final QypeLocationAwareActivity context, Location newLocation,
        Location lastSearchedLocation) {

    ...

    // Check if the new location is too old, if so wait for a most recent location.
    if (lastSearchedLocation == null) {
        // Delete any pending API request with an old location and send a new request right
        // away using the most up-to-date location.
        if (locationHandler
                .hasMessages(QypeNewLocationHandler.MSG_SEND_REQUEST_USING_UNRELIABLE_LOCATION)) {
            locationHandler
                    .removeMessages(QypeNewLocationHandler.MSG_SEND_REQUEST_USING_UNRELIABLE_LOCATION);
        }
        Activity activity = (Activity) context;
        // Wait some time if the location is too old, but if a new fix doesn't arrive send the
        // request using the location we've got.
        if (isOldLocation(newLocation)) {
            DialogSupport.safeShowDialog(activity, R.id.ign_loc_dialog_wait_for_fix);
            locationHandler.sendEmptyMessageDelayed(
                    QypeNewLocationHandler.MSG_SEND_REQUEST_USING_UNRELIABLE_LOCATION,
                    INTERVAL_SEND_REQUEST_USING_UNRELIABLE_LOCATION);
        } else {
            Dialog waitForLocationDialog = context.getWaitForLocationDialog();
            if (waitForLocationDialog != null && waitForLocationDialog.isShowing()) {
                activity.dismissDialog(R.id.ign_loc_dialog_wait_for_fix);
            }
            context.onLocationAvailable();
        }
    }

So when is a location good enough? In this specific case, that simply means fresh enough. You might ask: so it doesn’t matter if the location is too coarse? Well…yes and no. Keep in mind the requirements in first point above, the app should return results as quickly as possible. Moreover in a densely populated area, i.e. a city, a location returned by a coarse provider, i.e. the the network provider, is often a good approximation of the current location. I’ll explain in a bit when accuracy is taken into account.

Going back to the onLocationAvailable() callback, what happens if the location is not good enough? A couple of possible approaches are: waiting for a better location or using this location anyway. Our approach is somewhere in the middle: the app waits for a certain amount of time, 5 seconds, for a new location. If no new location is received, the app sends an API request using the old location. This is a tricky situation: on one hand we don’t want our users to wait too much before they can get some results, but we’d like to make sure that they get the best results we can give them. In this particular case we decided to take an optimistic approach and the app sends a request anyway, it can be that a user didn’t move too much from the last known location saved by the location manager. But the app does a little bit more under the hood. Let’s jump for a second at the end of this callback:

    ...
    return requestMoreLocationUpdates(newLocation);
}

private boolean requestMoreLocationUpdates(Location newLocation) {
    double accuracy = newLocation.getAccuracy();
    int desiredAccuracy = getDesiredAccuracy();

    // Check if the new location is fresh and accurate enough.
    return isOldLocation(newLocation) || accuracy > desiredAccuracy;
}

private boolean isOldLocation(Location location) {
    long locationTime = location.getTime();
    long timeDiff = getDesiredTimeDifference();
    long desiredTime = System.currentTimeMillis() - timeDiff;
    return locationTime < desiredTime;
}

Let’s assume that the location isn’t either precise or fresh enough, in this case the location manager will keep asking for location updates until it gets a location that is accurate enough. I’d like to point out something here: in our specific use case, the app doesn’t need to continuously ask for location updates. If the app finds a location that is fresh and accurate enough, the app will simply stop requesting location updates. The tricky part here is to find a good threshold: if it requires the location to be too accurate this logic will be useless, otherwise the risk is to use really coarse locations and have bad results returned (if, like in our case, the API you’re using searches for results in an area centered around user’s location). If you read the previous post you know that the logic that requests a new location is always re-triggered onResume(), so by “stopping” I don’t actually mean that location updates won’t be requested at any point in the future, but only that as long as the user stays withiin the context of that Activity the location logic will be turned off, saving battery power. This fullfills our second requirement.

If the last location isn’t either precise or fresh enough, at some point in the future a new location will be returned by one of the available providers. The onNewLocation() callback will be invoked again but in this case the last searched location won’t be null.

...

    } else {
        boolean diffDistanceTooBig = diffDistanceTooBig(newLocation, lastSearchedLocation);
        if (diffDistanceTooBig) {
            boolean hasBetterLocationMessages = locationHandler
                    .hasMessages(MSG_BETTER_LOCATION);
            if (!hasBetterLocationMessages) {
                // It's a better location, invite the user to refresh his location
                locationHandler.obtainMessage(MSG_BETTER_LOCATION).sendToTarget();
            } else {
                locationHandler.removeMessages(MSG_BETTER_LOCATION);
                Message msg = locationHandler.obtainMessage(MSG_BETTER_LOCATION);
                locationHandler.sendMessageDelayed(msg,
                        QypeNewLocationHandler.DELAY_SHOW_BETTER_LOCATION_RELOAD_TOOLTIP);
            }
        }
    }

    ...

The location manager will calculate the distance between the new and the old distance and, if this distance is greater than a defined threshold (in our case this is simply the desired location accuracy), it will send a new message to the location handler. The rationale here is that if the new location doesn’t differ too much from the old one, it means that the old location wasn’t that bad and the app doesn’t need to do anything special (note: it doesn’t matter how coarse it was!). But if the new location is too far away from the last one, the app will show a reload view that asks the user if he/she wants to reload the results.

The last piece of the puzzle is the QypeNewLocationHandler class, which is just a simple implementation of a Handler that takes advantage of delayed messages to show/hide the reload view.

@Override
public void handleMessage(Message msg) {
    ReloadTooltip reloadTooltip = context.getReloadTooltip();
    switch (msg.what) {
    case MSG_BETTER_LOCATION:
        if (showReloadTooltip(reloadTooltip)) {
            reloadTooltip.setText(R.string.msg_reload_results_on_new_location);
            reloadTooltip.show(true);
            sendEmptyMessageDelayed(MSG_HIDE_RELOAD_TOOLTIP, TIMEOUT_HIDE_RELOAD_TOOLTIP);
            reloadTooltip.setTag(System.currentTimeMillis());
            hasBetterLocation = false;
        } else if (context.isLoadingData()) {
            hasBetterLocation = true;
        }
        break;
    case MSG_HIDE_RELOAD_TOOLTIP:
        reloadTooltip.hide(true);
        break;
    case MSG_SEND_REQUEST_USING_UNRELIABLE_LOCATION:
        Dialog waitForLocationDialog = context.getWaitForLocationDialog();
        if (waitForLocationDialog != null && waitForLocationDialog.isShowing()) {
            ((Activity) context).dismissDialog(R.id.ign_loc_dialog_wait_for_fix);
        }
        context.onLocationAvailable();
        break;
    default:
        super.handleMessage(msg);
    }
}

Nothing special here, apart from a tweak we added while we were testing the app that prevents the reload view from being shown too often and becoming annoying for the user.

If you want to know more or keep in touch, have a look at my blog of follow me on twitter or Google+.

No Comments »

Written by stefano on October 16th 2012. Category: Everything else

Getting to grips with the Android 4.0.3 social APIs

This post first appeared on Androitism.

We recently started getting our hands onto some of the shiny new Ice Cream Sandwich APIs, particularly Beam and the social stream API that was introduced with Android 4.0.3. Integration turned out to be a little harder than we expected, mostly due to two things: code verbosity (you’ll write a lot of boilerplate) and undocumented pitfalls. With this post I’d like to shed some light on a few things that bit us when developing with the new APIs.

Integrating with the social stream

First of all, you should know that to get going with the social stream, you will have to go through two major steps:

  1. Publish your app as an account provider, and log in your users via the AccountManager API, so that contacts from your service can be synced with the Android address book (now called the People app in ICS)
  2. Once your contacts are synced with the People app, import their status updates into the “Recent Updates” feed

I won’t talk about 1. There are two excellent blog posts on how to deal with connecting accounts and syncing contacts respectively, plus the SampleSyncAdapter app that is shipped with the SDK ApiDemos. Once you’re there, adding support for publishing a user’s social feed is generally quite simple. From here on I assume that you have read all the documentation that is available, since I want to focus on the difficult parts. Let’s have a look at the final product first.

People app Qype social stream

Let’s quickly recap when the “Recent Updates” pane appears. It becomes available whenever there are status updates from the contact you’re looking at that are no older than a few days (I believe it’s five days, but I haven’t exactly checked that). There is also a threshold for how many items will ever show up at the same time, and as pointed out in the docs, this threshold is platform or even device specific.

There are two ways to import these status updates from your service: eagerly, as part of the contacts sync (i.e. in your contacts SyncAdapter), or lazily, via an Intent that is fired whenever a user looks at another user’s profile. Again, the general mechanics behind this are outlined on the Android dev blog.

Generally, you don’t want to fetch status updates (plus images) for a hundred or more contacts as part of the sync, since it’s very unlikely that a user would look at all of them to see their status updates. I say “plus images”, because you will have to download them synchronously and either insert them in binary form into the StreamItemPhotos table, or write them to disk using an AssetFileDescriptor. Since ICS devices often have high resolution displays, you want to download high res images, so that’s a lot of data you’re pushing over the wire, keep that in mind.

Hence, you most likely want to go down the callback route, perhaps with optionally pre-populating important contacts with their status updates during contacts sync (what important means depends on your service, but you could for instance check if the user has starred a contact, and prefetch status updates accordingly).

Regardless for which sync strategy you settle (lazy or on-demand or hybrid), here are a few things that bit me while syncing social stream items. Continue Reading »

No Comments »

Written by Matthias on March 3rd 2012. Category: Everything else

How we work: flipping features

When we started internationalizing Qype we ran into the problem of how to sync development of new features with translators. Obviously you can’t deploy a new feature without all of it being translated. And even without the need for translations, when you deploy frequently you need to find a way to make sure only the stuff that’s done actually gets deployed to production. One solution is to use branches, but even with Git this is not what I would call a fun and painless way of doing releases. And since the Lean Startup showed us that moving to smaller batch sizes by deploying more often is the way to go, making the release as painless as possible is important.

It’s a bit like flipping burgers. Photo from tibchris

The simple solution that works surprisingly well for us (and apparently also for Flickr) is to just use if/else statements for the new and old code. It started with

if Rails.env == 'development'
  # new stuff
else
  # old stuff
end

but that obviously doesn’t work well because you can’t remember which block belongs to which feature. We now use this:

if show_feature?(:city_feed)
  # new stuff
else
  # old stuff
end

and we have a configuration file which defines the features and the states they are in (small example of how that looks like). Since we link the feature name with the translation key this also allows us to have a nice web page that tells us the state of each feature and how much translation work is already done.

So far so good. Where it gets really interesting is when you make these states dynamic instead of just dependent on the environment. We are now able to put features into states that depend on the logged-in user, so we can test new features internally on the live site and do private betas and usability tests really easily. This also allows to roll out features to n% of the traffic.

I’m really surprised how well this idea from the “Simplest Thing that Could Possibly Work” department works for us. It helps us to

  • deploy from HEAD pretty much whenever we want to (given all the tests pass)
  • coordinate with translators
  • test new features with live data on production
  • test new features in private betas

The only downside of this approach is that your code gets littered with all these ugly if/else statements. So you have to be strict about cleaning this up regularly.

If you’re interested in flipping features, we just open sourced this today as a small Ruby library. It includes a nice DSL for defining features and the basics of making dynamic states work:

Check it out, the source is on Github:
http://github.com/qype/feature_flipper

Comments Off

Written by Florian Munz on June 3rd 2010. Category: Open Source

How we do deployments

Hi! I’m Aleks, a web developer on Qype. This is my first blog post about the deployment process on Qype and about a small app I built with Artur in one day to help us organize it. In future expect more posts from me about small apps I build for Qype and some other interesting stuff I find about javascript and front-end development.

OK, here we go…

The deployment process on Qype is pretty much automated. Standard Capistrano scripts. Push a button, stare at the terminal for couple of minutes, make sure everything goes smooth. And most of the time it does go smooth, unless we have some migrations that change any large tables. But that’s a different story.

We deploy on Tuesdays and Thursday at about 16:00. Even if the deployment itself is pretty straightforward, it involves some organizational tasks like: make sure that all translations are done(we have a multi-lingual site), make sure that features that are still in development don’t make it into production by accident, make sure that the build is green before deployment, etc. So it’s not such a quick and pleasant task. That’s why we try to rotate the deployment responsibility among the Dev team, so it’s not one person that does it every time.

The first thing we introduced into this process is a panda hat. The person who’s wearing it – is the deployment captain and handles all the deployment related questions that day.

Always good to take a nap before deployment

Always good to take a nap before deployment

That worked pretty well, until we started having discussions who’s turn it is to deploy. Because we never kept track who deployed when, we never knew who has to do it. This is why I decided to write a simple app for that. www.whoisdeploycaptain.com

Who is deploy captain today?

All devs in our team are on Twitter, so the idea is to keep a list of our twitter usernames, notify the person on Tuesday and Thursday and keep track who was responsible when. I used  the twitter-auth gem to handle authentication. Devs have to give access to their twitter to the app. The app will add them as followers to the @qypedeploy bot, and the bot will tweet on Tuesday and Thursday something like “Today’s deploy captain on Qype is @<some_username>”. Also if you go to the site, then you will see this message there as well. The app is deployed on heroku with Rails 2.3.5. I highly recommend heroku if you want to write a small web app and don’t want to waste on time setting up your own server.

I will release the code for whoisdeploycaptain on GitHub next week when I polish the code a bit more. Until then, follow Qype deployments on twitter: @qypedeploy

Comments Off

Written by Aleksandr Lossenko on May 10th 2010. Category: Everything else

Hello World

Welcome! I’m Florian and I’m the Lead Developer at Qype. We started this blog to show some of the cool things we are doing and talk about stuff we learned while scaling and improving Qype.

In 2010 we began experimenting with some skunkworks projects. Playing around with cool ideas we have without necessarily implementing them in Qype directly. We want to use this blog to show them to you.

We also started to ramp up our involvement in Open Source. As a platform that’s completely based on Open Source software we always felt we needed to give back more, which is not easy in the day-to-day life. But I hope we can announce some of it very soon.

So, what better way is there to kick things off than with a random xkcd comic?

Every computer, at the unreachable memory address 0x-1, stores a secret.  I found it,  and it is that all humans ar-- SEGMENTATION FAULT.

None, obviously.

Comments Off

Written by Florian Munz on May 7th 2010. Category: Everything else