The how and why of Google+ History

What is Google+ history?

At I/O 2012, Google quietly introduced a new feature and an accompanying API for Google+, history, signaling a big push in the near future to what Google believes could be the next big shift for the company, one that could unify Google products and open up huge opportunities for developers looking to transparently bring social hooks to their creations. Although the feature is currently in a preview state, it’s a great time to experiment with how you can use it and to prepare your site for when the feature opens up to a broader audience. The general idea behind history is that it takes the concept pioneered in instant upload – passively uploading photos while you take them – and extends the functionality to a much larger scope of “things”.  These “things” are recorded privately within your Google+ dashboard and then can be conveniently shared with your circles as you see fit. An all-up record of your activities is consolidated into a single dashboard that by default is private to you but that you can elect to share with the people who it is relevant to.

The approach Google takes ensures that:

  • You have compete control over how (and what) you share. For example, all those embarrassing Justin Bieber music plays I have listened to in my social music player don’t appear in my social feeds unless I want them to.
  • You reduce the amount of work you have to put into sharing. This is possible because you don’t have to do explicitly do anything to move your activities into your Google+ history log. Tracking all of the places that you save your social activities and making use of this data is tedious when it’s spread across the web, history brings it all together.
  • You no longer have to remember everything you have done. I can’t even count how many times I forgot a song I listened to, a site that I liked, or a restaurant I visited but didn’t remember to check in or save the site for sharing later. Having a passively created log of activities, powered by Google services and 3rd party services, saves you all the hard work.
  • Most of all, our way of tracking activities ensures that you aren’t oversharing and spamming your friends with activity requests, a glut of the irrelevant information, and private data that should stay private. This is good both for Google+ because there is less noise in people’s stream and for users because you don’t have to worry about what you’re keeping track of.

This feature is super handy and we’ve been having loads of fun with the concept and using history with Google products.  The more you use history, the more you get spoiled by its convenience and desire to have it in all the places you work and play. Activities make actively having to manage your social history (check-ins, music plays, browsing history, video watches, and so on) across multiple site and using multiple tools seem dated.  All activity is privately stored in your Google+ history dashboard and when / if you want to share something it’s just a click away.

The following image shows how history looks after you have some activities created:

Example of Google+ History UI

What kinds of activities are supported?

Looking at the table of contents on the Google+ hitstory API docs will give you a current list of supported Google+ History activities. The current list consists of:

  • AddActivity
  • BuyActivity
  • CheckInActivity
  • CommentActivity
  • CreateActivity
  • ListenActivity
  • ReserveActivity
  • ReviewActivity
  • ViewActivity – video
  • ViewActivity – movie
  • ViewActivity – TV episode
Check back with the documentation to discover more activities as the list grows. That said, you can begin to see how these activities could apply to sites around the web.  For example, let’s say that you purchase something on an e-commerce site.  If that site has hooked in Google+ history, purchases appear in your history as BuyActivity records. If you purchased a book and want to share thoughts on the book, that item will be recorded in your history and you can conveniently share the link from your buy experience with the appropriate audience from your social circles. In this example, e-commerce sites are given a golden opportunity to increase sales because the feature’s convenience can drive additional sales to the site and users are given a dead simple way of giving their social circles an easy way to discover content curated from the people who share their interests.

How do I integrate activities from my site into Google+ history?

It’s easier than you might think.  Depending on your site’s infrastructure, integrating Google+ history could be less than a week’s development time. In fact, we have run code labs where developers were writing activities to history in under an hour.  The steps are as follows:

  •  Set up your application
  • Authenticate the user
  • Write moments to Google+ history

Google has done most of the heavy lifting and offers plenty of tools to get you up and running quickly. A great tutorial is available in code lab form, built around our starter project, the Google+ history client side flow, that walks you through the process, but I will share some key code that outlines the process.

Set up your application

You must first join the Google+ History preview group on the Google+ developers site. As with other APIs, you should make note of your application’s client ID.

Authenticate

Before you can write to a user’s history, you must authenticate the user against Google+. You include the script for the sign-in button, render the sign-in button, and then setup a callback that will hide the sign-in button and reveal any UI specific to showing the user has signed in. The following code does this:

<!-- Place this tag where you want the sign-in button to render -->
<g:plus action="connect" clientid="{CLIENT_ID}" callback="{CALLBACK_FUNCTION}"></g:plus>

<!-- Place this asynchronous JavaScript just before your </body> tag -->
<script type="text/javascript">
 (function() {
   var po = document.createElement('script'); po.type = 'text/javascript'; po.async = true;
   po.src = 'https://apis.google.com/js/plusone.js';
   var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(po, s);
 })();
</script>

Create the moment

After you have authenticated the user, it’s time to create the data structure representing the social action that you’re about to write. The following code outlines this step:

    var moment  = {
      "type":type,
      "target":{
        "url": theURL
      }
    };

Write the moment to the user’s history

Finally, you’re ready to write the activity to the user’s history stream.  The following JavaScript code will perform this action:

    // Set the access token on the JavaScript API Client
    var authResult = getAuthResult();

    debugLog("auth result: ", authResult);

    try{
      gapi.auth.setToken(authResult);
    }catch(e){
      alert("arg!" + e.toString());
    }

    var args = {
      'path': '/plus/v1moments/people/me/moments/vault?debug=true',
      'method': 'POST',
      'body': JSON.stringify(payload),
      'callback': function(response) { 
        debugLog("Response: ", response);
      }
    };
    gapi.client.request(args);
  }

History in action

Now that you’ve learned about the high level steps for writing, let’s take a look at a few examples and some convenience wrappers that I have modified, mocking up how this would work in practice.

App.js – a wrapper I wrote, based on the client-side flow starter project to greatly simplify writing moments for my examples:

  function onSignInCallback(authResult) {
    if (authResult.error == null) {
      debugLog('Auth Success', authResult);
      toggleInterface(true);

      // Store the auth result for later use
      storeAuthResult(authResult);
    } else {
      debugLog('There was a problem!', authResult);
    }
  }

  /**
   * Toggle the interface between the signed in and signed out state
   */
  function toggleInterface(animate) {
    if (animate) {
      // Hide the sign in button
      $("#panel-sign-in").toggle("blind", "slow", function() {
        // Display features that require the user to be signed in
        $("#panel-communicate-moments, #panel-sign-out").toggle("blind", "slow");
      });
    } else {
      // do the same quickly
      $("#panel-sign-in, #panel-communicate-moments, #panel-sign-out").toggle();
    }
  }

  /**
   * Store the authentication result using a cookie that expires in 7 days
   * @param authResult A JavaScript object that represents the current
   * authentication state.
   */
  function storeAuthResult(authResult) {
    if (authResult != null) {
      $.cookie('starter-auth', JSON.stringify(authResult),
          { expires: 7, path: '/' });
    } else {
      // If null comes in, delete the cookie (sign the user out)
      $.cookie('starter-auth', null, { expires: 7, path: '/' });
    }
  }
  /**
   * Get the currently stored authentication cookie.
   * @returns A JavaScript object that represents the current authentication
   * state or null if the user is not signed in.
   */
  function getAuthResult() {
    return $.parseJSON($.cookie('starter-auth'));
  }

  $(document).ready(function() {
    // If the user is already logged in, hide the button
    if (getAuthResult() != null) {
      debugLog("You are already signed in :)");
      toggleInterface();
    }

    // attempt to write the moment for this

  });
  //////////////////////
  // JavaScript for the writing moments

  /**
   * Write a moment to Google+
   *
   * @param payload an object representation of the moment
   */
  function writeMoment(payload) {
    // Set the access token on the JavaScript API Client
    var authResult = getAuthResult();

    debugLog("auth result: ", authResult);

    try{
      gapi.auth.setToken(authResult);
    }catch(e){
      alert("arg!" + e.toString());
    }

    var args = {
      'path': '/plus/v1moments/people/me/moments/vault?debug=true',
      'method': 'POST',
      'body': JSON.stringify(payload),
      'callback': function(response) { 
        debugLog("Response: ", response);
      }
    };
    gapi.client.request(args);
  }

  function debugLog(message, object) {
    if (object) {
      $("#debug-panel").prepend(
          message + ": " + JSON.stringify(object, null, "  ") + "\n");
    } else {
      $("#debug-panel").prepend(message + "\n");
  function debugLog(message, object) {
    if (object) {
      $("#debug-panel").prepend(
          message + ": " + JSON.stringify(object, null, "  ") + "\n");
    } else {
      $("#debug-panel").prepend(message + "\n");
    }
  }

  function writeThisMoment(theURL, type){

    debugLog("starting moment write");
    var moment  = {
      "type":type,
      "target":{
        "url": theURL
      }
    };

    debugLog("moment is" , moment);

    writeMoment(moment);
  }

Note that this is all specific to my site and usage that will appear in the next snippet. Also, this code is based entirely on the client side starter project, which is a much better and more authoritative project than this code.

The following example takes one of my schema.org examples and writes it to history as a moment:

<html itemscope itemtype="http://schema.org/Person">
  <head>
    <title>Gus is a person</title>

    <!-- first snippet goes in head -->
    <script src="//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
    <script src="//ajax.googleapis.com/ajax/libs/jqueryui/1.8.18/jquery-ui.min.js"></script>

          href="//ajax.googleapis.com/ajax/libs/jqueryui/1.8.18/themes/start/jquery-ui.css"/>
    <script src="static/json2.js"></script>
    <script src="static/jquery.cookie.js"></script>

    <!-- Required to render the Google+ sign in button -->
    <script src="http://apis.google.com/js/plusone.js"></script>

    <!-- The JavaScript API Client library: Required to write moments -->
    <script src="http://apis.google.com/js/client.js"></script>

    <!-- A simple history wrapper -->
    <script src="js/app.js" ></script>A

    <!-- end first snippet -->
  </head>

  <body>
    <g:plus action="connect"
      clientid="{some client id}"
      scope="https://www.googleapis.com/auth/plus.moments.write"
      callback="onSignInCallback"></g:plus>
    <button onclick="storeAuthResult(null);">Clear Auth</button>
    <hr/>
    Name:<div itemprop="name">Gus Class</div>
    Description: <section itemprop="description">
      Gus Class is an awesome human.
    </section>
    <p>
    <img itemprop="image" src="http://gusclass.com/square_profile.png" />
    </p>
    <button onClick='writeThisMoment("http://wheresgus.com/history/gus.html", "http://schemas.google.com/AddActivity");'>Write This Moment</button>

    <!--debug panel for testing only -->
    <pre id="debug-panel"></pre>
  </body>
  <script> </script>
</html>

Conclusions and closing thoughts

Essential to using moments is creating an engaging experience when users write to history.  Going back to the start of this post, remember what the benefits are for using moments. Controlling shared content, reducing feed spam, simplifying the user’s ability to share, and helping users remember what matters to them and what they did.

After taking a closer look at the previous example, I’m wondering how useful it would be for the user to know / have this information that I captured. As such, I have begun thinking about what I think are best practices for using moments:

  • Is this the right / best way to represent the moment? There are usually multiple activities that are appropriate to a moment, make sure to use the right one for what you are trying to share.
  • IF you had this moment in your history, would it be useful to you? Would you share this moment from your history, have you ever needed or wanted to share something like this?
  • Is this the right information to represent the moment? Is the right content being passed to history?
  • More to come 🙂

I’ll revisit this topic soon with a closer look at the various types of activity.  I’m very excited about Google+ history and I hope you are too.