Tales of a Developer Advocate

Developer Relations @ Google

  • Crazy snow. Balham

    • 17 Dec 2010
    • 1 Response
    •  views
    • Edit
    • Delete
    • Tags
    • Autopost

    -780365592

    • Tweet
  • Omni Launch: Launch Installed Web apps from the URL bar

    • 15 Dec 2010
    • 0 Responses
    •  views
    • Edit
    • Delete
    • Tags
    • Autopost

    Yes, I know, three posts today about the Management API. Anyway, I have created an Awesome extension that allows you to use your URL Bar (The Omnibox) to find an launch apps by their name.

    It is pretty cool, just type “go TAB or SPACE app name” in your URL bar and that is it. It will search through your installed extensions and let you launch it very quickly.

    The remarkable thing is that this extension took 20 minutes to create, and I will try and show you how in this post.

    Like all extensions, the first thing that we do is define a manifest.

    {
      "name": "Omni Launch",
      "description": "Launches apps, the quick way!",
      "version": "0.0.0.2",
      "background_page": "background.html",
      "permissions" : ["management"],
      "omnibox": { "keyword" : "go" },
      "icons" : {
        "16" : "go.png"
      }
    }

    The important thing here is the “omnibox” attribute which lets you define a keyword that will allow the user to activate your extension.

    Chrome does a lot of magic now, firstly it takes your 16 pixel icon and when ever the user types “go” into the address bar this icon will be shown next to the action (although it will be in grey-scale). If you load this extension now, as is, you will see what I mean.

    Now lets add some functionality to the background.html file. This is the brains of our extension. The way the extension works is a progressive filter. If I type “go t” it will find all apps that contain “t”, if I type “tw” it will find all apps that have a “tw” in their name. It is pretty basic stuff.

    The first thing that we will do is hook-up the a function that handles the keypresses in the omnibar for our keyword. There is a simple API called

    var inputChanged = function(text, suggestionsCallback) {
        if(text == "") {
          return;
        }
    
        var suggestions = [];
        var textlc = text.toLowerCase();
        var re = new RegExp("("+text+")","ig");
    
        for(var a in apps) {
          var app = apps[a];
          var name = app.name.toLowerCase();
          var idx = name.indexOf(textlc);
          if(idx > -1) {
            var content = app.name;
            var description = (app.description == "") ? 
               " " : app.description;
    
            var matchStyle = chrome.omnibox.styleMatch(idx, text.length);
    
            suggestions.push(
              {
                "content": content,
                "description" : content + " - " + description,
                "descriptionStyles" : [
                  matchStyle
                ]
              }
            );
          }
        }
    
        suggestionsCallback(suggestions);
      };

    And hook-up the handler:

    chrome.omnibox.onInputChanged.addListener(inputChanged);

    The code is pretty straight forward, but it does do some special bits. For a start, we only want to be able to launch apps, so we need to maintain a simple list of all apps (done via chrome.management.getAll(getAllCallback);); once that is out of the way, we have the handler hooked up (onInputChanged) that detects when the users is changing their input against our keyword. The code iterates across all known apps and checks to see if their name contains the search term. If it does we add it to an array of suggestions. We also style the suggestion a little using “chrome.omnibox.styleMatch” to highlight the exact text that matched in the suggestion. At the end of the handler we call the method (suggestionsCallback) passed in to our handler with a list of the apps that we are suggesting to the user.

    And that is the basics done for adding suggestions into the Omnibox, it isn’t an amazing amount of use at the moment because if you select one of the suggestions it doesn’t do anything. We want it to launch the app Dammit! :)

    Luckily, launching apps and find the selected item is really really easy.

    So here goes:

    var inputEntered = function(text) {
      var appsList = [];
    
      if(text == "") {
        return;
      }
    
      for(var a in apps) {
        var app = apps[a];
    
        if(app.name.toLowerCase() == text.toLowerCase()) {
          chrome.management.launchApp(app.id);
          return;
        }
      }
    };

    And hook up a handler that is triggered when the user makes a selection.

    chrome.omnibox.onInputEntered.addListener(inputEntered);

    This code is pretty basic, all we do is loop through each of the installed apps looking for the first one that matches the same name as the name of the app selected in the suggestions list and launch it using the “chrome.management.lauchApp” API.

    And we are all done. If you want to see this code in action it is available on Github/PaulKinlan/OmniLaunch. Fork away and tell me what you build.

    • Tweet
  • An API to detect if a Chrome Extension has updated

    • 15 Dec 2010
    • 0 Responses
    •  views
    • Edit
    • Delete
    • Tags
    • Autopost

    As you might have been able to tell from my previous posts, I am a bit of a nut when it comes to the Management API in the Chrome Extension framework.

    I got asked a question recently about detecting if a Chrome Extension has been updated. The good news is that there is an API for that. The bad news is that I told a little fib: there isn’t actually a specific API for detecting if an Extension/App had been updated.

    Lets get back on to the Good News, you can detect if an Extension/App has been updated by listening to the onInstalled event triggered by Chrome. Every time an extension is installed (or updated) this event gets fired.

    The important thing to remember is that it doesn’t tell you directly if the installation was an update, so the thing that you need to do is first get a list of all the extensions installed by the user and then track the version number in onInstalled.

    Here is a neat little sample of it in action:

    /*
        Track all the version numbers (background.html) 
        in your background page
    */
    var extensions = {};
    var getAllCallback = function(exts) {
      for(var i in exts) {
        var ext = exts[i];
        extensions[ext.id] = ext; // store a reference to the app
      }
    };
    
    chrome.management.getAll(getAllCallback);

    Now that we have a list of the apps, we can listen for installatons. In the handler, we are only going notify they user if the extension/app is not new and the version has changed. An extension is not new if we already know about it.

    var onInstall = function(ext) {
      var id = ext.id;
    
      if(extensions[id] && extensions[id].version != ext.version) {
        chrome.browserAction.setBadgeText({"text" :"New" }); // tell the user  
        extensions[id] = ext; // track the extension
      }
    };

    Cool.

    • Tweet
  • Web app launcher as a browser_action

    • 15 Dec 2010
    • 0 Responses
    •  views
    • Edit
    • Delete
    • Tags
    • Autopost

    In my previous post “Creating a New New Tab Page For Chrome” I showed how you can take advantage of the Management API and the Override Pages framework to make your own custom App Launcher.

    This in itself was pretty cool, however it didn’t address a real problem – Users still like to keep their existing custom NTP AND use Apps installed from the Webstore. To address this, I made a really simple “browser_action” for a new extension called Quick Launch.

    It uses pretty much all of the existing code from the NTP tutorial, but some small changes. First the manifest has been updated to include the “browser_action” and remove the page override as follows:

    {
        "name": "Quick Launch",
        "description": "Launches apps, the quick way!",
        "version": "0.0.0.10",
        "background_page": "background.html",
        "permissions" : ["management"],
        "browser_action" : {
            "default_icon" : "go.png",
            "default_title" : "Quick Launch",
            "default_popup": "popup.html"
        }
    }

    It is pretty simple, we just define an icon, a title and a webpage that will be opened when the user clicks on the button. We then create a file called popup.html and put the following JS in.

    document.addEventListener("DOMContentLoaded", function() {
            chrome.management.getAll(getAllCallback);
          });
    
          var getAllCallback = function(list) {
            var apps = document.getElementById("apps");
            var counter = 0;
            for(var i in list) {
              // we don't want to do anything with extensions yet.
              var extInf = list[i];
    
              if(extInf.isApp && extInf.enabled) {
                var app = document.createElement("div");
    
                var img = new Image();
                img.className = "image";
                img.src = find128Image(extInf.icons);
                img.addEventListener("click", (function(ext) {
                  return function() {
                    chrome.management.launchApp(ext.id);
                  };
                })(extInf));
    
                var name = document.createElement("div");
                name.className = "name";
                name.textContent = extInf.name;
    
                app.className = "app";
                app.appendChild(img);
                app.appendChild(name);
                apps.appendChild(app);
    
                if(counter % 2) {
                  var row = document.createElement("br");
                  apps.appendChild(row);
                }
                counter ++;
    
              }
            }
          };
    
          var find128Image = function(icons) {
            for(var icon in icons) {
              if(icons[icon].size == "128") {
                return icons[icon].url;
              }
            }
    
            return "/noicon.png";
          };

    The code is pretty straight forward – when we detect that the DOM has loaded we ask the extension sub-sytem to get a list of all the apps and extension that the user has installed. Once the results are returned, we simply iterate through the list checking to see if it is an app (there is a handy property called “isApp”) and for each app we build a series of DOM objects and add them to the container. That is it, nothing else….. or is there? (well there is a few extra enhancements and styling I have made, but it is all visible in the source)

    As always, the source is available on Github

    I actually have another extension, that I will blog about a little later that I think you will really like – along the same lines as this.

    • Tweet
  • Is it an App or a Link

    • 15 Dec 2010
    • 2 Responses
    •  views
    • Edit
    • Delete
    • Tags
    • Autopost

    Quick answer: it is nearly always an App.

    Long Answer: I have seen a lot of Comment for some apps in the Chrome Webstore that are along the lines of “This is just a link”. In some cases, the users are correct but they are missing an important point. The webstore is a channel for users to discover great Web Apps, many users who visit the Web Store will have never seen your app.

    Take for example my app http://simpletodo-app.appspot.com/ and the App in the Web Store. Both load the same URL, but both are defiantly an app. There is nothing to suggest that it isn’t an app, but if you check out the manifest for the code, you will see that it is really just a reference to the URL with an request for unlimitedStorage.

    The Web Store doesn’t have to have new applications or applications specific to Chrome; in fact I encourage you to put your existing apps in there, that is what it is designed for – to help you, the developer, to reach users more easily. I think that a lot of users when they see their favourite app in the store think that there should be a new experience for them, but you have to remember that most of the users that see this app probably haven’t used it, so it IS an new experience for them.

    Obviously there are some caveats, yes you can create and list “packaged apps” which have Chrome specific functionality, and yes you will notice that quite a few apps have targeted HTML5 enabled browsers (this is a good thing), but that doesn’t have to be the case. I would love it if you put your existing app in there, and because you have a whole host of new users you decide to upgrade the experience to cater for more modern browsers such as Safari, Chrome, Firefox and IE9.

    So if you are a developer of an app where people are saying this and you can’t devote much development resource, I have one simple piece of advice that can help you: Send your users to functionality as quickly as possible. If you have a log-in page, send them to that (but make sure they can quickly sign-up). Under no circumstances send them to a default product page, if you do this, then it really is just a Bookmark Link. The user has already chosen to “install” your app, make it feel like they are jumping into your application from the instant that they click the icon.

    Even better still, if you have some time, get rid of the sign-in page, use OpenID – apps can now have a near seamless account creation and login process. Check out our article about Federated Login for some really nice examples of account creation and sign-in flows. You will see that they get users to the functionality really quickly. If you want to see this in action, check out the Diary.com application – it is a beautiful sign-in process and will show you how simple it is and the positive effect that it has for users.

    As always, comments welcome! :)

    • Tweet
  • Creating a New New Tab Page for Chrome

    • 9 Dec 2010
    • 2 Responses
    •  views
    • Edit
    • Delete
    • Tags
    • Autopost

    For a long time Chrome Extensions have had the ability to create a new tab page in side Chrome. An excellent example of this is SpeedDial.

    With the introduction of the new Chrome Web Store there is a new boy in town. Apps. Apps are installed on to the New Tab Page and if your extension doesn’t handle them, then you need to update it because users will not be able to run the new apps that they have installed or purchased.

    The good news is that for a little while now Chrome has had a Management API. The API gives you specific access to a list of all the Apps and Extensions that are installed in to a users browser.

    So, without further ado, lets make an awesome Chrome Extension New Tab Page with an amazing App Launcher!!

    As always with an extension, you start with a manifest.

    {
        "name": "The New App Launcher",
        "description": "Launches apps, the good way!",
        "version": "0.0.0.1",
        "permissions" : ["management"],
        "chrome_url_overrides" : {
            "newtab": "newtab.html"
        }
     }

    Done, that was simple. Notice that we defined a permission – management, and we also defined an object called “chrome_url_overrides”, specifying a new url for the New Tab Page.

    Next step, create the “newtab.html” file – we will keep it simple for now – just a simple skeleton.

    <html>
      <head>
        <style>
    
        </style>
        <script>
          document.addEventListener("DOMContentLoaded", function() {
            chrome.management.getAll(getAllCallback);
          });
    
          var getAllCallback = function(list) {
            // TODO: Something Awesome
          };
        </script>
      </head>
      <body>
        <div id="apps">
    
        </div>
      </body>
    </html>

    It is pretty standard HTML, with a simple call to a Chrome specific API called chrome.management.getAll – which as you guessed gets a list of all the Extensions and App installed on the your system. Like all methods in the extension subsystem, getAll doesn’t return data directly, rather the data is returned via a callback defined by you. The callback will recieve a list of ExtensionInfo objects

    Lets do something with this, because as it stands it is just a blank page. Lets populate the “apps” div with some content by padding out “getAllCallback” with some functionality.

    var getAllCallback = function(list) {
      var apps = document.getElementById("apps");
      for(var i in list) {
        // we don't want to do anything with extensions yet.
        var extInf = list[i];
        if(extInf.isApp && extInf.enabled) {
          var app = document.createElement("div");
    
          var img = new Image();
          img.className = "image";
          img.src = find128Image(extInf.icons);
    
          var name = document.createElement("div");
          name.className = "name";
          name.textContent = extInf.name;
    
          app.className = "app";
          app.appendChild(img);
          app.appendChild(name);
          apps.appendChild(app);
        }
      }
    };
    
    var find128Image = function(icons) {
      for(var icon in icons) {
        if(icons[icon].size == "128") {
          return icons[icon].url;
        }
      }
    
      return "/noicon.png";
    };

    Again, pretty simple – the output should look similar to the attached. Pretty nice, but there is one small problem – nothing is clickable, we can’t launch anything. That is pretty simple to solve thanks again to chrome.management API. The API has a simple method called “launchApp” which at its simplest takes an extension ID as its parameter.

    Lets get that added so we have a fully functioning New Tab Page and App launcher. We will just add a click handler to the image, no anchors needed.

    img.addEventListener("click", (function(ext) {
        return function() {
            chrome.management.launchApp(ext.id);
        };
    })(extInf));

    And that is it. We have a Chrome extension that provides a New Tab Page with app launcher functionality! Awesome

    The code for this post is on Github, so fork away and have a play and let me know if you create an awesome extension.

    • Tweet
  • Drag to Desktop in JS

    • 9 Dec 2010
    • 1 Response
    •  views
    • Edit
    • Delete
    • Tags
    • Autopost

    When I created appmator, I want to remove a lot of the traditional webism that we see in apps. Specifically, I am not overly keen on “Save As” buttons, so I made sure I didn’t include one in the application

    The question then becomes how do you get data quickly to a directory of the users choice. I have chosen two operations, a standard click of a url that downloads the data clientside (another post to come shortly). Chrome has a feature, its Drag and Drop implementation allows you to specify a URI or Data URI that is attached to a drag operation, specifically a drag operation that ends outside the browser. When Chrome detects that the drag operation has ended outside the browser

    var element = document.getElementById("anyoldelement");
    anyoldelement.addEventListener("dragstart", function(e) {
       e.dataTransfer.setData("DownloadURL",
           "application/zip:package.zip:data:image/png;base64," +
           Builder.output({"binary": false}));
    });

    It is pretty simple, we attach a function to the ondragstart event. That function simply sets the sets Data on the dataTransefer object in the event callback. The data is of a specific type called “DownloadURL” and attaches a valid URI – this URI can be anything in the domain, or it can be a DataURI (as in the case of this example). Prior to the URI is the meta-data about the URI, in this case it is application/zip:package.zip which is a simple MIME type and file name.

    The code that generates the dataURI is part of a library function that generates a ZIP file (JSZip in fact, which is an awesome library)

    And that is pretty much it.

    In honesty, this approach only works in Chrome, so be warned. Also, it is impossible to detect the presence of this feature.

    • Tweet
  • Flashing in to the Web Store

    • 9 Dec 2010
    • 0 Responses
    •  views
    • Edit
    • Delete
    • Tags
    • Autopost

    I have seen a lot of the talk in the media that the Chrome Web Store is been about pushing the web forwards with just HTML5 and Javascript.

    For example, there are some awesome HTML based games in the store, such as Canvas Rider, Shredder Chess and Sinous all accessible in any modern web browser written using HTML.

    It seems to me at least that Flash has been missed by the media elements reporting on the Web store. Flash is an unbelievably important part of the Web Eco-system and because of this is fully accessible though the Chrome Webstore.

    In my eyes, an excellent example is Vyew or Paltalk which have a heavy emphasis on Flash and are amazing applications that do a lot of things we can’t do just yet in HTML (web cam access for instance).

    If you have a Flash app or game it is really simple to get in the store, and you have two options:

    • You can use Appmator to take your site and create the package you need to get into the store, it literally takes about two minutes and your app is still hosted on the web and under your domain.
    • The second option, and a really powerful option is to package your SWF into the zip file that you upload to the store. Once it is in the store, we distribute it for you, bandwidth and all.

    Think about it for a minute, if you package your game and upload it to the store, we distribute that for you! And we also make sure that if you do an update it is delivered to all users of your app. If you had an amazingly popular app like Canabalt and that was hosted entirely in the store, you would have no bandwidth costs.

    I must strongly note, that the Web store is a platform for users to discover great applications. Users are in control here, if they like your app it will be rated highly, if not then it won’t be. It is important that you create high-quality apps and games for the store.

    Here are my tips….. actually it is a single tip for creating awesome Flash games for the store, heck it is not even just Flash, all apps and Games regardless of tech.

    Make it as immersive as possible. I test a lot of apps and games, and when I see them in a little box it makes me weep a little. You have an entire screen to work with – use it. When I see a menu bar and site navigation, I cringe a little.

    Canabalt is a great example of this – it is all game.

    If you have a Flash game or app, let me know and I can help you get it into the store.

    • Tweet
  • Relative times. A Chrome Extension I would love to see

    • 8 Dec 2010
    • 0 Responses
    •  views
    • Edit
    • Delete
    • Tags
    • Autopost
    I was walking back from the train station, it is late at night, well it is now very early morning, but you can't tell that from my post time... Well maybe you can. But anyway it gave me an idea for a Chrome Extension that I would love to see, but I don't have the time myself to create.

    I would love a Chrome Extension that detected dates and times in a web page and converted them into the Timezone I am currently in. So, for instance an event is time stamped 12pm PST, well that is 8pm where I am. Just a subtle change will really help.

    As the Meerkat says: "Simples"

    • Tweet
  • Announcing Appmator. Get your apps in the Web Store in under a minute!

    • 7 Dec 2010
    • 2 Responses
    •  views
    • Edit
    • Delete
    • Tags
    • Autopost

    I am excited (<– every “press release” starts like this, but this isn’t a press release) to announce Appmator, a tool that I have developed for you, developers of great web apps to help you get your apps in to the Chrome Web Store in under a minute!

    With the recent release of the Google Chrome Webstore (literally 10 minutes ago) developers now have an amazing channel to reach over 100 million users of Chrome.

    It is pretty easy to get your existing Web apps in to the store. Now it is even easier using Appmator, simply go to the App, add your URL and press enter. You will then have a Zip file that you can upload directly to the store. You can even modify the output to suit your needs!

    It is even in the Chrome Web Store so you can install it now. The great thing is, that this tool was packaged ready for the store using itself! Sweet.

    It does some pretty cool stuff, all of which I will cover in Javascript Tutorials in the next couple of days, here is a sneak peak:

    • Use of beautiful Webfonts to enhance the UI
    • Uses Modernizer to feature detect – I will probably do some workaround for other browsers soon.
    • Uses classList to quickly modify styling and state
    • Creates the package (zip file) all on the client side (this is my favorite bit) using the awesome jszip
    • Allows users to drag a zip file on to the desktop so that it can easily be uploaded
    • Uses ArrayBuffers to initiate a client side download (it is a bit of a hack – but pretty cool)
    • Detects favicon support so that you can quickly re-use your existing assets
    • Uses flexible box model for layout.
    • Mostly works offline (supports appcache) – ok, this is in the next version.

    And the best thing is, it is all open and the source code is available on GitHub

    All feedback and suggestions are welcome. And if you spot any bugs (and you will) please file a bug

    • Tweet
  • « Previous 1 2 Next »
  • About

    I help developers build really cool products on the Web.

    I work for Google as a Developer Advocate in London, specializing in Chrome, HTML5 and the Chrome Web Store.

    231036 Views
  • Archive

    • 2012 (5)
      • February (5)
    • 2011 (30)
      • July (1)
      • June (4)
      • May (10)
      • April (4)
      • March (1)
      • February (5)
      • January (5)
    • 2010 (35)
      • December (15)
      • November (5)
      • October (2)
      • August (5)
      • July (8)
    • 2009 (1)
      • January (1)
    • 2008 (8)
      • December (1)
      • April (2)
      • March (3)
      • February (2)
    • 2007 (17)
      • December (1)
      • September (1)
      • August (5)
      • May (1)
      • March (2)
      • February (4)
      • January (3)
    • 2006 (153)
      • November (3)
      • October (7)
      • September (11)
      • August (9)
      • July (9)
      • June (14)
      • May (11)
      • April (32)
      • March (23)
      • February (26)
      • January (8)
    • 2005 (274)
      • December (7)
      • November (43)
      • October (63)
      • September (54)
      • August (66)
      • July (13)
      • June (10)
      • May (10)
      • April (6)
      • January (2)
    • 2004 (1)
      • August (1)

    Get Updates

    Subscribe via RSS
    TwitterFriendfeedLinkedIn