Attach Images to Glass Timeline Content
When pushing text-based Timeline content to Glass, you can attach images, which will become the background image of the Timeline card.
Why Attach an Image?
Maybe you want to annotate an image that the user has shared with you or you want to present the user with information that can be better represented with images than with words alone.
How it works
In order to add a post timeline content, the user must first authorize you to access their timeline. You do this by requesting access to this scope:
https://www.googleapis.com/auth/glass.timeline
Once the user has authorized you to interact with their Timeline, you can built a multipart MIME message to send mixed JSON and binary encoded image attachments to the Glass user's timeline.
I talk about multipart MIME encoded content in Send HTML Emails using PHP, where MIME is used to encode HTML emails and attachments.
Basically, MIME works by defining boundaries that separate plain text content from other formats (typically binary or base64 encoded content). The Boundaries allow supporting programs to look for content defined within those boundaries and process them one by one.
Let's take an example where we have a single image (google-glass.jpg) and supporting text-only Timeline content ("This is an annotation"We need to send the binary and the HTTP headers for the image as well as attaching the following Timeline Card JSON:
{ "text":"This is an annotation" }
We do this by first reading in the image content and determining it's mimetype:
$image_content = file_get_contents('google-glass.jpg'); // this function is deprecated but easy to read // we will use newer code in the tutorial $image_mimetype = mime_content_type('google-glass.jpg');
Then we define a MIME boundary - this is a string that is unlikely to show up in the rest of the content, so let's make it long and unusual:
$mime_boundary = '<<--==+X['.md5( time() ).']';
Then we assemble the MIME-encoded data:
// validate our user's oauth2 verification $header .= "Authentication: Bearer ".$oauth2_token; // set the mimetype boundary $mime_boundary = '<<--==+X['.md5( time() ).']'; $header .= 'Content-type: multipart/related; boundary="'.$mime_boundary.'"'."\r\n"; // send the rest of the content - // separate all content with $output .= "--".$mime_boundary . "\r\n"; $output .= "Content-type: application/JSON\r\n"; $output .= "\r\n"; $output .= '{"text":"This is an annotation"}' . "\r\n"; // attach the image $image_contents = file_get_contents('google-glass.jpg'); $image_mimetype = mime_content_type('google-glass.jpg'); $output .= "\r\n"; $output .= "--".$mime_boundary . "\r\n"; // define the image header $output .= "Content-Type: ".$image_mimetype . "\r\n"; $output .= "Content-Transfer-Encoding: binary\r\n"; $output .= "\r\n"; $output .= $image_contents . "\r\n"; $output .= "\r\n"; // adding the "--" to the end of the mime_boundary // signifies the end of the message $output .= "--".$mime_boundary."--";
The resulting string looks a bit like this:
Authentication: Bearer ca9acb4045810bcN492VDo32 Content-Type: multipart/related; boundary="<<<--==+X[a942cc2015df420208]" --<<<--==+X[a942cc2015df420208] Content-Type: application/json {"text":"This is an annotation"} --<<<--==+X[a942cc2015df420208] Content-Type: image/jpeg Content-Transfer-Encoding: binary astYYe+u<aseuc&hensu94r'hauaeu-<shaseu....... --<<<--==+X[a942cc2015df420208]--
To post this to the Glass Timeline, we post the resulting string to this URL
https://www.googleapis.com/upload/mirror/v1/timeline?uploadType=multipart
If everything was successful, you’ll recieve JSON back from Google describing a timeline card and the attachment.
{ "kind":"mirror#timelineItem", "id":"80cc7a49-285e-41f9-93a4-6d1a938b616f", "selfLink":"https://www.googleapis.com/mirror/v1/timeline/80cc7a49-285e-41f9-93a4-6d1a938b616f", "created":"2013-12-24T19:31:03.195Z", "updated":"2013-12-24T19:31:03.195Z", "etag":"1387913463195", "text":"This is an annotation", "attachments":[ { "id":"bs:cc54d76c-7644-4a93-b22c-076b5b8e9df3", "contentType":"image/jpeg", "contentUrl":"https://www.googleapis.com/mirror/v1/timeline/80cc7a49-285e-41f9-93a4-6d1a938b616f/attachments/bs:cc54d76c-7644-4a93-b22c-076b5b8e9df3?alt=media" } ], "notification":{ "level":"DEFAULT" } }
Also you'll see something like this in your Google Glass:
This method works well for using text as an annotation. HTML Timeline content will have it's own formatting and styles, and therefore won't display the image attachment in the background.
Implementation
We of course want to make an MVC architecture so that we can modularize and reuse our code in future projects, rather than the procedural version shown above.
Building on the previous tutorials, we will create the following file structure, which will allow us to create the OAuth2 authentication, then post a Timeline Card
assets
img
google-glass.jpg
classes
Google_Contact.class.php
Google_Location.class.php
Google_Menu_Item.class.php
Google_Menu_Item_Value.class.php
Google_OAuth2.class.php
Google_OAuth2_Token.class.php
Google_Timeline_Item.class.php
Google_Timeline_Item_Attachment.class.php
HttpPost.class.php
index.php
oauth2callback
index.php
settings.php
Assets
For this demo we are using a lovely picture of Google Glass, google-glass.jpg:
https://github.com/backupbrain/glass-timeline-attach-image-php/blob/master/assets/img/google-glass.jpg
Settings
In order to move forward, we will need to set up our OAuth2 App settings.
See the source code at: https://github.com/backupbrain/glass-timeline-attach-image-php/blob/master/settings.php
Classes
If you’ve been following these posts, you already know that we use class classes/HttpPost.class.php, that uses cURL to communicate with the Google Mirror servers. You also know that we have classes classes/Google_OAuth2.class.php and classe/Google_Oauth2_Token.class.php to handle the OAuth2 authentication process.
HttpPost.class.php allows us to execute HTTP requests through PHP and cURL, a critical component of talking to the Google Mirror API. https://github.com/backupbrain/glass-timeline-attach-image-php/blob/master/classes/HttpPost.class.php
classes/Google_OAuth2.class.php makes the initial request to begin the user’s authentication process. https://github.com/backupbrain/glass-timeline-attach-image-php/blob/master/classes/Google_OAuth2.class.php
classes/Google_OAuth2_Token.class.php stores the post-authentication token. We use this to verify that we are speaking on behalf of the user when we talk to the Google Mirror API https://github.com/backupbrain/glass-timeline-attach-image-php/blob/master/classes/Google_OAuth2_Token.class.php
If you aren’t familiar with these classes or how they work, you can check out the following tutorials to see how it works:
Post Content to the Glass Timeline
Adding Menu Options to Timeline Cards
Fetch Glass Timeline Items
Once you’ve set up these classes, you can create the classes that describe how to process and manage Timeline Cards.
Google uses JSON POST to communicate information about Glass Timeline content with the Mirror API. We can make working with the Timeline easier by building as series of PHP objects that mirror the JSON data structures understood by Google.
The basic Timeline data structure looks like this:
classes/Google_Timeline_Item.class.php lets us process a Timeline Card. A Timeline_Item also contains Menu_Items, Notifications, and Attachments.
https://github.com/backupbrain/glass-timeline-attach-image-php/blob/master/classes/Google_Timeline_Item.class.php
This features a new function to add an attachment:
public function addAttachment($filename) { $Attachment = new Google_Timeline_Item_Attachment($this->Google_OAuth2_Token); $Attachment->parseFile($filename); $this->attachments[] = $Attachment; }
And an updated insert() function that assembles the multipart/mime content:
public function insert() { // format the post data as JSON: $jsonPost = json_encode($this->toJSONObject()); $headers[] = 'Authorization: '.$this->Google_OAuth2_Token->token_type.' '.$this->Google_OAuth2_Token->access_token; if (count($this->attachments)) { $url = self::UPLOAD_URL.'?uploadType=multipart'; $mime_boundary = '<<<==+X['.md5(time()).']'; $mime_boundary = md5(time()); $headers[] = 'Content-Type: multipart/related; boundary="'.$mime_boundary.'"'; $postParms[] = "--".$mime_boundary; $postParms[] = 'Content-Type: application/json'; $postParms[] = ""; } else { $url = self::URL.'?uploadType=multipart'; } $postParms[] = $jsonPost; if (count($this->attachments)) { foreach ($this->attachments as $Attachment) { $postParms[] = ""; $postParms[] = "--".$mime_boundary; $postParms[] = 'Content-Type: '.$Attachment->contentType; $postParms[] = 'Content-Transfer-Encoding: binary'; $postParms[] = ""; $postParms[] = $Attachment->content; $postParms[] = ""; } $postParms[] = "--".$mime_boundary.'--'; } $postData = implode("\r\n", $postParms); $headers[] = 'Content-Length: '.strlen($postData); $this->HttpPost = new HttpPost($url); $this->HttpPost->setHeaders( $headers ); $this->HttpPost->setRawPostData( $postData ); if ($this->Google_OAuth2_Token->authenticated) { $this->HttpPost->send(); $response = json_decode($this->HttpPost->httpResponse); } else { throw new Exception ("Google_OAuth2_Token needs to be authenticated before you can insert timeline items."); } // is there an error here? if ($response->error) { throw new Exception("The server reported an error: '".$response->error->errors[0]->message."'"); } else { $this->fromJSONObject($response); $this->fetched = true; } }
classes/Google_Timeline_Item_Attachment.class.php describes a Timeline Card attachment and how to fetch it from Google. https://github.com/backupbrain/glass-timeline-attach-image-php/blob/master/classes/Google_Timeline_Item_Attachment.class.php
This features a new function to parse the contents of a file and determine it's mimetype:
public function parseFile($filename) { $finfo = finfo_open(FILEINFO_MIME_TYPE); // return mime type ala mimetype extension $this->contentType = finfo_file($finfo, $filename); finfo_close($finfo); if (!$this->content = file_get_contents($filename)) { throw new Error("Could not open file: '".$filename."'"); } }
classes/Google_Menu_Item.class.php describes a menu item for that Timeline Card. You'll remember from the Menu tutorial that you can insert menu items with the Timeline Card, providing users with options for how to engage with your content https://github.com/backupbrain/glass-timeline-attach-image-php/blob/master/classes/Google_Menu_Item.class.php
classes/Google_Menu_Item_Value.class.php describes the text and icons values for a custom menu item. https://github.com/backupbrain/glass-timeline-attach-image-php/blob/master/classes/Google_Menu_Item_Value.class.php
classes/Google_Notification.class.php describes the notification level for the Timeline Card. https://github.com/backupbrain/glass-timeline-attach-image-php/blob/master/classes/Google_Notification.class.php
classes/Google_Location.class.php describes location that the Timeline Card was shared from https://github.com/backupbrain/glass-timeline-attach-image-php/blob/master/classes/Google_Location.class.php
classes/Google_Contact.php describes the recipient of the shared Timeline item. https://github.com/backupbrain/glass-timeline-attach-image-php/blob/master/classes/Google_Contact.class.php
Controllers
Besides index.php which performs sets the scope and initiates the OAuth2 authentication request, there are two controllers for this tutorial: one to submit the subscription request, and one callback to handle Mirror notifications of a user’s timeline interaction.
index.php sets the OAuth2 scope and initiates the authorization request: https://github.com/backupbrain/glass-timeline-attach-image-php/blob/master/index.php
When you encounters this for the first time, you will see a screen that looks like this:
The oauth2callback/index.php fetches a Timeline Card, then fetches the attachment. https://github.com/backupbrain/glass-timeline-attach-image-php/blob/master/oauth2callback/index.php
When you log in and gets to the oauth2callback/index.php page, you will see something like this, showing the details of the Timeline Card you just inserted.
And your Google Glass will display this:
That's it. Now you can attach images to your Timeline Cards.
More Information
The source code for this tutorial is available on GitHub:
https://github.com/backupbrain/glass-timeline-attach-image-php
Grab Timeline Card By Id http://20missionglass.tumblr.com/post/70914020349/grab-timeline-card-by-id Learn to fetch a shared Timeline card by ID. This tutorial sets up all the classes used in this tutorial and lets you inspect all your user's shared timeline cards including downloading their attachments.
Post Content to the Glass Timeline http://20missionglass.tumblr.com/post/61150784385/post-content-to-the-glass-timeline Learn to post content to the Glass Timeline.
Send HTML Emails using PHP http://tonygaitatzis.tumblr.com/post/62426747491/send-html-emails-using-php Learn how MIME formatting works in my tutorial for multipart MIME formatted emails
Adding Menu Options to Timeline Cards http://20missionglass.tumblr.com/post/70031065981/adding-menu-options-to-timeline-cards Learn to add menu items to Glass Timeline content, empowering users to engage with your service in a variety of ways.
Programming an OAuth2 Client in PHP http://20missionglass.tumblr.com/post/60787835108/programming-an-oauth2-client-app-in-php Learn to use cURL to perform an OAuth2 authentication in PHP
Timeline Items https://developers.google.com/glass/develop/mirror/timeline Google Mirror API reference for inserting Timeline items and attaching pictures and video
About Media Upload https://developers.google.com/glass/media-upload Google Mirror API Documentation for how to build a multipart mime attachment









