Change default date range in Google Analytics with a Chrome Extension

[Update – May 2015] Updated description so that it matches the latest version of the extension on GitHub

[Update – September 2014] I moved the extension project to GitHub, and updated this post accordingly

This is a continuation from my previous post on the same subject.

I promised a Chrome Extension that opens Google Analytics page and sets today’s date as the default date range. You can grab it from GitHub.

To add it to your Chrome:

  1. download and unzip the extension to some folder
  2. open your Chrome Extensions page (type chrome://extensions in the address bar or press the Settings button (top right) then Tools/Extensions)
  3. drag and drop the extracted ganalytics-lastDay.crx file to the Extensions page, a Drop to install message should appear
  4. confirm the dialog

To configure the extension, simply open it and follow the instructions (which are the same as in my previous post). If you need to change the Analytics code at any moment, you just go back to the chrome://extensions page, find the extension and click on (the ridiculously small) Options button.

In case you want to play with date ranges, follow the instructions on the GitHub page.

The files you want to play with are background.js and conf.js, which both contain the getURL() function (duplicated, because using shared JS files in Chrome Extensions turned out to be a bit tricky). That function takes the portion of the URL manually pasted by the user and builds the full Analytics URL with it. As you can see, there’s 2 variables involved: today and yesterday. You can change these dates using Date‘s functions, like this:

var date = new Date(), today = '', oneMonthAgo = '';

today += date.getFullYear();
today += pad2(date.getMonth() + 1);
today += pad2(date.getDate());

date.setMonth(date.getMonth() - 1);
oneMonthAgo += date.getFullYear();
oneMonthAgo += pad2(date.getMonth() + 1);
oneMonthAgo += pad2(date.getDate());

return 'https://www.google.com/analytics/web/?#home/' + code +'/%3F_u.date00%3D' + oneMonthAgo + '%26_u.date01%3D' + today +'/=';

Of course, you may also want to change the default landing page: just go to that page in Google Analytics and change the ?#home part in the URL with whatever you want, like for example

return 'https://www.google.com/analytics/web/?#report/app-visitors-overview/' + code +'/%3F_u.date00%3D' + oneMonthAgo + '%26_u.date01%3D' + today +'/=';

Something that can also be useful for bookmarklets: if you monitor more than one website/app with Analytics, you may want to have a bookmark for each of them (or you may want to have the extension open Analytics for a specific webpage). Each webpage/app has its own code, so you can either paste the code for the webpage you want the extension to open, or maybe hardcode the correct combination of link and code on different bookmarklets.

Change default date range in Google Analytics

I don’t know the reason why Google chose to set the default date range to the last 30 days excluding the current date (maybe I’m the only one interested in today’s stats), but it’s definitely annoying not having the option to change that default.

There used to be a bookmarklet to overcome the issue, but I’ve not been able to find an update for that after Google’s changed how URLs are managed in Analytics.

So, here’s the update ūüôā

The “easiest” way I found to make a bookmarklet works like this:

  1. Log in to your Google Analytics account
  2. Look at the URL, it should be something like https://www.google.com/analytics/web/?hl=en&#home/a12345678w12345678p12345678/
  3. Copy the last portion of the URL, in the example it’s a12345678w12345678p12345678
  4. Open a text editor, copy & paste this code on a new file you can call analytics.html
    <html>
    <head></head>
    <body><a target="_blank" href="javascript:(function(){function d(a){a=String(a);a.length<2&&(a='0'+a);return a}var c=new Date,b='';b+=c.getFullYear();b+=d(c.getMonth()+1);b+=d(c.getDate());location.href='https://www.google.com/analytics/web/?#report/visitors-overview/a12345678w12345678p12345678/%3F_u.date00%3D'+b+'%26_u.date01%3D'+b+'/=';})();">Google Analytics</a></body>
    </html>
    
  5. Replace a12345678w12345678p12345678 in the file with the code you copied at step 3
  6. Save the file and open it with your browser (tested with Chrome, Firefox and Safari on Mac OSX)
  7. Drag the link to your bookmarks bar

Don’t delete/move/rename the HTML file if you’re using Chrome or Firefox, for some reason they need it even after you’ve added the bookmarklet.

This bookmarklet sets the date range to today only, you can play with the javascript to change that (now only b is used, you’d need to create a new Date and set it at the end of the URL). Also, it takes you to the visitors overview page, you can change that by looking at the other pages’ URLs.

Steps 2, 3 and 5 are not technically needed, in that if you leave the bogus URL I put in the code, Analytics is going to tell you that something’s wrong with your credentials, but will set the date anyway (and update that part of the URL). I guess that’s your session ID, so I’m not sure if it’s better to use an existing one or just leave the dummy and let Analytics generate a new one every time. It’s probably hackish to use an old session ID, but it gets rid of the warning dialog and it works!

Ok, this was to create a bookmarklet, but what I actually did was create a Chrome Extension that does the same thing, but has an icon and most of all is listed on the new tab page. If anybody is interested in that, let me know in the comments and I’ll add a new post to explain how it’s done :).

[Edit – June 27]: so here’s the promised extension

Stack notifications on Android (plus: get users to see your notifications on JellyBean – phone/phablet UI)

This is something I put together for WhatsHare, a small open source app that I published; I couldn’t find a tutorial that had all of this together in one place.

My app (actually, just one particular Activity) has no UI: it receives an Intent, does its thing and finish()es. The only way I found to notify the user that it’s done something is to add a Notification, as dialogs would get too in the way.

I want the notification to be shown every single time, as it must tell the user “ok, I did something” or “an error occurred”, because she has no other way of knowing if the app even worked!

On the other hand, I don’t want to pollute the notification area with lots of “Success!” notifications, as they’re ultimately pointless once the user has seen them.

The options were to either:

  1. automatically cancel() the notification once the message has been displayed, or
  2. show the message every time, but collapse all notifications into a single one, with an increasing counter

I preferred the latter approach, as I think it feels a little more predictable for the user (also, the other solution requires dedicated services/timers, so it’s probably more complicated).

Here’s the final code , comments follow:

@SuppressWarnings("deprecation")
private void showNotification(int sharedWhat) {
    String title = getString(R.string.app_name);
    // this will be routed to onNewIntent(), SendToGCMActivity is this class
    Intent onNotificationDiscarded = new Intent(this,
            SendToGCMActivity.class);
    PendingIntent notificationIntent = PendingIntent.getActivity(this, 0,
            onNotificationDiscarded, 0);
    Notification notification = null;
    String content = getString(R.string.share_success,
            getString(sharedWhat), outboundDevice.type);
    
    // notificationCounter is a private static AtomicInteger
    int notificationNumber = notificationCounter.incrementAndGet();

    NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
            .setSmallIcon(R.drawable.notification_icon, 0)
            .setContentTitle(title)
            .setContentText(content)
            .setTicker(content)
            .setContentIntent(notificationIntent)
            .setDeleteIntent(PendingIntent.getActivity(this, 0, onNotificationDiscarded, 0))
            // update the counter
            .setNumber(notificationNumber);

    if (Build.VERSION.SDK_INT > 15) {
        notification = buildForJellyBean(builder);
    } else {
        notification = builder.getNotification();
    }

    // notifications disappear after the user taps on them
    notification.flags |= Notification.FLAG_AUTO_CANCEL;
    NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

    // cancel previous notification to clean up garbage in the status bar
    nm.cancel(notificationNumber - 1);
    // add new notification
    nm.notify(notificationNumber, notification);
}

@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
private Notification buildForJellyBean(NotificationCompat.Builder builder) {
    // for some reason Notification.PRIORITY_DEFAULT doesn't show the counter
    builder.setPriority(Notification.PRIORITY_HIGH);
    return builder.build();
}

protected void onNewIntent(final Intent intent) {
    if (intent.hasExtra(Intent.EXTRA_TEXT)) {
        // do your thing
        // ...code goes here
        showNotification(R.string.link);
    } else {
        // user clicked on the notification
        notificationCounter.set(0);
    }
    // this is needed cause even if we set android:theme="@android:style/Theme.NoDisplay"
    // an invisible overlay is laid out on top of the caller activity, we don't want that
    finish();
}

It works like this:

  • you define a static counter for all your notifications, so that the NotificationManager treats them as separate
  • AUTO_CANCEL is set so when the user taps on the notification it disappears
  • setDeleteIntent() is called to have an Intent routed to the Activity’s onNewIntent() method whenever the notification is discarded, so we can reset the counter. If users tap on the notification, onNewIntent() is also called because of notificationIntent being set as content intent
  • since this activity has an intent-filter for action.SEND of type text/plain, we know that an Intent with no EXTRA_TEXT field can only be passed explicitly — that is, by setting the class target when creating the Intent –. We could add some other extra if we wanted to make extra sure that the Intent comes from that specific class
  • whenever a notification needs to be stacked, we cancel() the previous one (using its id, which is always the current counter minus one) and create a new one with the updated count

That’s the magic behind stacked notifications: Android does not show your notifications if you’re just updating them, so you need to cancel the old one and add the new one. If you don’t need to show the message every time, just update the previous notification using its id (that must not change, so no counter at all).

About JellyBean: if you don’t raise the notification’s priority to at least PRIORITY_HIGH it won’t show the counter on phones/phablets for some reason. Also, to make sure that messages are displayed every time on phone/phablets you must set them as tickers; tablets instead show the full message set by setContentText() anyway.

A final note: I’m using NotificationCompat.Builder instead of plain Notification.Builder to support older devices. You can find that class in the support library (android.support.v4.app package). If you’re targeting honeycomb and higher, just use Notification.Builder.

HttpPost requests executed multiple times (Apache HttpClient)

This is something I noticed on Android, but from what I read it also involves the desktop Java version.

I was sending POST requests to an API server, and I was getting some random 400 Bad Request responses from time to time. I wish Apache provided an easy way to log the plain text version of Http requests, but I couldn’t find a better way to see what the app was sending than sending the same request to my PC when failing.

So to log requests I start netcat (sudo nc -l 80 on a mac) or a very minimal server in python (it’s more or less the same as the example on Twisted’s front page) and route them there whenever an error occurs.

try {
   response = client.execute(post,
                  new BasicResponseHandler());
} catch (IOException e) {
   if (DEBUG_FAILED_REQUESTS) {
      post.setURI(URI.create(DEBUG_FAILED_REQUESTS_SERVER));
      try {
         client.execute(post, new BasicResponseHandler());
      } catch (IOException e1) {
         e1.printStackTrace();
      }
   }
}

I don’t know if it’s my router, but sometimes connections from the Android device to my PC get blocked: to make them work I just open a browser on the Android, go to some website and then try again with my internal IP (192.168.0.whatever). It always works, no idea why.

Using this code I discovered that my post requests were executed 4 times each, nearly at the same time. I discovered that it’s the default behavior, and you must provide your own RetryHandler if you want the HttpClient to work otherwise.

In my case, my calls are sent to Google’s shortener service, and for some reason sometimes it just rejects requests. If you wait a little bit between attempts you increase your chance of getting valid responses. So this is what I did:

HttpPost post = new HttpPost(SHORTENER_URL);
String shortURL = null;
int tries = 0;
try {
    post.setEntity(new StringEntity(String.format(
            "{\"longUrl\": \"%s\"}",
            getURL(encodedID, encodedAssignedID))));
    post.setHeader("Content-Type", "application/json");
    DefaultHttpClient client = new DefaultHttpClient();
    // disable default behavior of retrying 4 times in a burst
    client.setHttpRequestRetryHandler(new DefaultHttpRequestRetryHandler(
            0, false));
    String response = null;
    while (response == null && tries < RETRY_COUNT) {
        try {
            response = client.execute(post,
                    new BasicResponseHandler());
        } catch (IOException e) {
            // maybe just try again...
            tries++;
            Utils.debug("attempt %d failed... waiting", tries);
            try {
                // life is too short for exponential backoff
                Thread.sleep(RETRY_SLEEP_TIME * tries);
            } catch (InterruptedException e1) {
                e1.printStackTrace();
            }
        }
    }
    Utils.debug("response is %s", response);
    if (response != null) {
        JSONObject jsonResponse = new JSONObject(response);
        shortURL = jsonResponse.getString("id");
    } else if (DEBUG_FAILED_REQUESTS) {
        Utils.debug("attempt %d failed, giving up", RETRY_COUNT);
        debugPost(post, client);
    }
} catch (JSONException e) {
    e.printStackTrace();
} catch (UnsupportedEncodingException e) {
    e.printStackTrace();
}

where debugPost() is a method that calls my PC to log the request, and Utils.debug() is just a small utility method I wrote to log messages with logcat using String.format() if format args are passed to it (it also takes care of splitting messages that would be truncated by logcat itself).

You could choose to implement exponential backoff very easily, but since it’s a blocking operation for the user in my case I preferred not to.