Setup

Installing Prerequisites - Chrome 20

Follow along

Pull the sample sources

Developer Mode/Unpacked Extensions

Developer Mode/Unpacked Extensions

Chrome Extension Settings Screen
Chrome screenshot

Install Filtamatic

Chrome Extension Settings Screen, Load Unpacked Button
Chrome screenshot

Install Filtamatic App

Filtamatic Extension
Chrome screenshot

Filtamatic App Installed

Filtamatic Extension Installed
Chrome screenshot

Install Picstore

Chrome Extension Settings Screen, Load Unpacked Button
Chrome screenshot

Install Picstore App

Picstore Extension
Chrome screenshot

Both Apps Installed

Both Extensions Installed
Chrome screenshot

Installed Apps

App NTP page with installed apps
Chrome screenshot

Kick the tires!

Main screen of Filtamatic sample application
Chrome screenshot

(Many thanks to Ilmari Heikkinen and html5rocks)

Store some pictures!

Main screen of Picstore application
Chrome screenshot

Sample pictures in $REPO/sample-pictures

Integrating with a service

The three chief virtues of a programmer are: Laziness, Impatience and Hubris.
Larry Wall

File picking, the old-fashioned way

Example picture of old code style - large
Mememator application source screenshot

File picking, the Web Intents way

      var intent = new WebKitIntent({
        "action":"http://webintents.org/pick",
        "type":"image/*"
      });

      navigator.webkitStartActivity(intent, onSuccess, onFailure);
    

Anatomy of an Intent

An Intent object has three parts:

  • Action: The Verb
    • Opaque string -- exactly matched
    • Open namespace
  • Type: What data is passed?
    • Either MIME type (i.e. "text/plain") or a string literal
    • Must describe the data!
    • Open namespace
  • Data
    • Any serializable JS object
    • Supports web messaging (Transferables)

Anatomy of an Intent Response

The Intent type parameter governs the passed data and the result type

  • MIME types pass Blob objects for binary types (image/*, video/*, audio/*, application/*)
  • MIME types pass plain text for text types (text/*)
  • String literal types should be microdata specifiers.
    • Data will typically be a Javascript object.
    • Example: schema.org types
  • See the Web Intents W3C Wiki for type documentation.
  • Propose your own!

Reading the picked file

      navigator.webkitStartActivity(intent, onSuccess, onFailure);  

      function onSuccess(result) {
        // For the "image/*" MIME type, result is a Blob or a URL
        if (result instanceof Blob) {
          ...
        } else {
          ...
        }
      }
    

Demo

Assignment!

Replace implementation for this:

Screenshot of pick button
Chrome screenshot

Coding Time!

Recipe

  • Add a new "Pick" button to the Filtamatic app (index.html)
  • Attach a Javascript handler for your new button (see the loaded() function)
  • Implement the handler function to invoke a Web Intent
  • On the result callback from the intent invocation, handle Blobs and URL return types.
  • Get the URL (remember to handle Blob!) and forward into the existing handler code path (See loadImageFromUrl)

You can reload your app to get your new code.
(See the chrome://extensions url; the "Reload" button in your app)
... or just hard-reload the page

Shhh! No-one look at the 1_pick directory if you get stuck!

Migrating "Save"

Migrating existing functionality to Intents

Demo

Recap: Intent Invocation

var intent = new window.WebKitIntent({
  "action": "http://webintents.org/save",
  "type": "image/*",
  "data": blob});     // <-- the image to be saved!
  
var onSuccess = function(data) {}
var onError = function(data) {}
  
window.navigator.webkitStartActivity(intent, onSuccess, onError);

// No, really. That’s it.

Explicit Intent Invocation

var canvas = $('#container canvas')[0];
var blob = createBlobFromCanvas(canvas); 

var intent = new window.WebKitIntent({
  “action” : “http://webintents.org/save”,
  “type” : “image/*”,
  “service”: “chrome-extension://npoipmeppdioagbkigdlnpxxxhnolaog/save.html”
  “data”: blob });

var onSuccess = function(data) {}
var onError = function(data) {}

window.navigator.webkitStartActivity(intent, onSuccess, onError);

Receiving an Intent in your service

  • The Intent appears in the window.webkitIntent object.
  • Delivered intents will match the filters you declare in your manifest.
      if (window.webkitIntent) {
        // We are servicing an Intent!
        handleIntent(window.webkitIntent.data);
      }
    

Registration via Extension Manifest

      {
        "intents" : {
          "http://webintents.org/save": [{
            "type": [ "image/*" ],
            "href": "save.html",
            "title": "Filtamatic",
            "disposition": "window"
          }]
        }
        …
      }
    

See manifest.json documentation for Chrome App development

Anatomy of a Registration

  • Action and Type(s)
    • Establish the filter for intents delivered to your service
  • href
    • The URL of your service
  • Title
    • The name of your service
  • Disposition
    • window : A new tab. Nothing novel.
    • inline : Replace the picker UI and display overlapped.

Assignment!

Replace this functionality:

Screenshot of save button
Chrome screenshot

Coding Time!

Recipe

  • Change the "save" button to invoke a "save" Intent by calling a new JS handler.
  • Get your extension ID for the explicit url from the extensions page.
  • Add a manifest entry to register a "save.html" page as a service.
  • Edit the "save.js" used by the service page to read the Intent data and display it (just like the existing functionality).

Protip: You can see the calling and handling code in the "2_save" directory.

Discovery

How do users find my Intent Service?

Discovery & Installation via Chrome Web Store

Screenshot installing an extension from the web store
Chrome screenshot

Discovery & Installation via Intent Picker

CWS suggestion list in Intent Picker
Chrome screenshot

Discovery & Installation via Intent Picker

Confirmation dialog for installation from picker
Chrome screenshot

The code - save.js

// Display the image to be saved.
var loaded = function() {
  if (window.webkitIntent.data instanceof Blob) {
    imageURL = webkitURL.createObjectURL(window.webkitIntent.data);
  } else {
    imageURL = window.webkitIntent.data;
  }
  $('#image').attr("src", imageURL);

  $('#close').click(closeSave);
}

var closeSave = function() {
  window.webkitIntent.postResult("");
}

The markup - save.html

  <html><body onload="loaded()"><center>
    Right click and use <b>Save Image As...</b> to save.
    <p>
    <img src="">
    <p>
    <input type=button id=close>Close</input>
  </center></body></html>

The Manifest

{
  "name": "SimpleSave",
  "icons": { "16": "save16.png" },
  "version": "2",
  "intents": {
    "http://webintents.org/save" : [{
      "type": ["image/jpg", "image/png", "image/gif"],
      "title": "Save Image",
      "path": "save.html",
      "disposition": "inline"
    }]
  }
}

See manifest.json documentation for Chrome App development

Registration via <intent> tag (Soon!)

<intent
  action=”http://webintents.org/save”
  type=”image/*”
  href=”services/save”
  disposition=”window”
  title=”Save an image” />

One More Thing

Creating an image-editing intent

Intents allow you to focus on core functionality

  • In the case of the sample app, editing images.
  • How can I extend reach for my core functionality?
  • By exposing it as intent!

Demo

Trivial API

  • Image goes in...
  • Image goes out...
  • Can't explain that!

Recap: Send/Receive image data

Send Intent
  var intent = new WebKitIntent({
     "action":"http://webintents.org/save", "type":"image/png", "data":blob});
   navigator.webkitStartActivity(intent, onSuccess, onError);
Receive Intent
  if (window.webkitIntent) {
     handleIntent(window.webkitIntent.data);
   }
Send Response
  window.webkitIntent.postResult(reply);
Receive Response
  function onSuccess(data) { handleResponse(data); }

The new main page: Just Intents!

    
<button id="pick" onclick=”intent-pick”>Pick image ...</button>
<button id="save" onclick=”intent-save”>Save ...</button>
function intent-pick() {
  var intent = new WebKitIntent("action":"http://webintents.org/pick", "type":"image/*");
  navigator.webkitStartActivity(intent, onSuccess, onFailure);
}

function intent-save() {
  var intent = new WebKitIntent({"action":"http://webintents.org/save", "type":"image/jpg",
       "data":getImageData());
  navigator.webkitStartActivity(intent, onSuccess, onFailure);
}

Modifying images via canvas

    
// Get a canvas for the image element
var ctx = document.getElementById('canvas').getContext('2d');

// Get image data from canvas. Pixels are in imageData.data
// width and height are in pixels.width and pixels.height
var imageData = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height)

// Write image data to canvas.
ctx.putImageData(imageData, 0, 0);

// See http://www.html5rocks.com/en/tutorials/canvas/imagefilters/ for
// details and example filters.

Other Ideas

  • Rotate an image
  • Crop an image
  • Share via your favorite social service
  • Integrate your favorite image site for pick/save
  • and so much more …

And don’t forget the manifest!

{
  "name": "Filtamatic Image Editor",
  "version": "2",
  "icons": { "16": "edit16.png",
             "128": "edit128.png" },
  "intents": {
    "http://webintents.org/edit" : [{
      "type": ["image/jpg", "image/jpeg", "image/png", "image/gif"],
      "title": "Filtamatic",
      "path": "edit.html",
      "disposition": "window"
    }]
  }
}

See manifest.json documentation for Chrome App development

Assignment!

Expose the existing functionality of Filtamatic as an "edit" intent service.

... or do something else exciting

Coding Time!

Recipe

  • Can start with the included "edit.html" service page.
  • Add the code to "edit.js" as in the save intent handler to handle incoming intent data.
  • Don't forget to handle Blobs!
  • Let the user perform the image editing you have in mind.
  • Replace "save" functionality with a postResult to the intent
  • Add the "edit.html" service to the manifest!
  • You can try out your creation by using the "Edit" button below each picture on the image page in the PicStore app.

You can reload your app to get your new code.
(See the chrome://extensions url; the "Reload" button in your app)
... or just hard-reload the page

Psst! If you look at the 3_edit directory for ideas, you won't be the only one!

You’re part of the ecosystem now

  • Filtamatic, obviously
  • PicStore
  • Mememator
  • Cloud File Picker
  • Your app here!

Demo

Questions?