Static Cards

You can insert, update, read, and delete static cards using simple REST APIs. In addition, you can attach objects to a static card, such as a location or media.

How they work

Static cards reside to the right of the Glass clock by default and display information relevant to the user at the time of delivery. However, they do not require immediate attention like live cards and users can choose to read or act on the card at their own leisure.

When Glassware inserts static cards into the timeline, Glass may play a notification sound to alert users. All previous static cards also shift to the right and disappear from the timeline after 7 days or when 200 cards are newer.

When to use them

Static cards are great for delivering periodic notifications to users as important things happen. For example, a news delivery service that sends the top news stories as they happen. Mirror API static cards can also start live cards or immersions through the OPEN_URI menu item. This allows you to create hybrid interactions that utilize static cards as notifications and a live card or immersion for a more interactive experience.

For a full list of possible operations for timeline items, see the reference documentation.

Inserting static cards

To insert static cards (timeline items), POST a JSON representation of a timeline item to the REST endpoint.

Most of the fields in a timeline item are optional. In its simplest form, a timeline item contains only a short text message, like in this example:

Raw HTTP

POST /mirror/v1/timeline HTTP/1.1
Host: www.googleapis.com
Authorization: Bearer {auth token}
Content-Type: application/json
Content-Length: 26

{ "text": "Hello world" }

Java

TimelineItem timelineItem = new TimelineItem();
timelineItem.setText("Hello world");
service.timeline().insert(timelineItem).execute();

Python

timeline_item = {'text': 'Hello world'}
service.timeline().insert(body=timeline_item).execute()

On success, you receive a 201 Created response code with a full copy of the created item. For the previous example, a successful response might look like this:

Raw HTTP

HTTP/1.1 201 Created
Date: Tue, 25 Sep 2012 23:30:11 GMT
Content-Type: application/json
Content-Length: 303

{
 "kind": "glass#timelineItem",
 "id": "1234567890",
 "selfLink": "https://www.googleapis.com/mirror/v1/timeline/1234567890",
 "created": "2012-09-25T23:28:43.192Z",
 "updated": "2012-09-25T23:28:43.192Z",
 "etag": "\"G5BI0RWvj-0jWdBrdWrPZV7xPKw/t25selcGS3uDEVT6FB09hAG-QQ\"",
 "text": "Hello world"
}

The inserted item that would appear in the user's timeline looks like this:

Inserting a timeline item with an attachment

A picture is worth a thousand words, which is a lot more than you can fit into a timeline item. To this end, you can also attach images and video to a timeline item. Here's an example of how to insert a timeline item with a photo attachment:

Raw HTTP

POST /upload/mirror/v1/timeline HTTP/1.1
Host: www.googleapis.com
Authorization: Bearer {auth token}
Content-Type: multipart/related; boundary="mymultipartboundary"
Content-Length: {length}

--mymultipartboundary
Content-Type: application/json; charset=UTF-8

{ "text": "A solar eclipse of Saturn. Earth is also in this photo. Can you find it?" }
--mymultipartboundary
Content-Type: image/jpeg
Content-Transfer-Encoding: binary

[binary image data]
--mymultipartboundary--

Java

TimelineItem timelineItem = new TimelineItem();
timelineItem.setText("Hello world");
InputStreamContent mediaContent = new InputStreamContent(contentType, attachment);
service.timeline().insert(timelineItem, mediaContent).execute();

Python

timeline_item = {'text': 'Hello world'}
media_body = MediaIoBaseUpload(
    io.BytesIO(attachment), mimetype=content_type, resumable=True)
service.timeline().insert(body=timeline_item, media_body=media_body).execute()

A timeline item with an attached image looks something like this on Glass:

Attaching video

If you are attaching video files to your timeline items, we recommend that you stream the video instead of uploading the entire payload at once. The Google Mirror API supports streaming with HTTP live streaming, progressive download, and the real time streaming protocol (RTSP). RTSP is frequently blocked by firewalls, so use the other options when possible.

To stream video, use the PLAY_VIDEO built-in menu item and specify the video's URL to be the menu item's payload. See Adding built-in menu items and supported media formats for more information.

Paginating

You can paginate timeline items that do not fit on a single timeline card, but should otherwise be associated with the same card. Paginated items all share the same timeline.id and therefore have the same set of menu items. When a user taps a paginated timeline item, a Read more menu item appears.

Glass automatically paginates timeline items that display text. To have Glass automatically paginate html, use the article tag with its class property set to auto-paginate like in the following example:

<article class="auto-paginate">
 <h3>Very long list</h3>
 <ul>
   <li>First item</li>
   <li>Second item</li>
   <li>Third item</li>
   <li>Fourth item</li>
   <li>Fifth item</li>
   <li>Sixth item</li>
   <li>...</li>
 </ul>
<article>

To paginate manually, use the article tag for the content that you want to display on each card. Glass displays the contents of each article tag in a separate sub-timeline card. For example, you can create a paginated timeline item with the following HTML:

<article>
 <section>
   <p>First page</p>
 </section>
</article>

<article>
 <section>
   <p>Second page</p>
 </section>
</article>

<article>
 <section>
   <p>Third page</p>
 </section>
</article>

By default, the paginated timeline item's first card is displayed as the cover card and is again displayed when the user selects the Read more menu item. To prevent the first card from appearing again after tapping Read more, you can specify the cover-only CSS class for the first <article> tag:

<article class="cover-only">
...

The cover-only class also supports auto-paginated timeline items:

<article class="auto-paginate cover-only">
...

Bundling

Bundling allows you to group related but distinct items together, like for individual messages in an email thread. Bundles have a main cover card that a user taps to display a sub-timeline that contains the other cards in the bundle. Bundles are distinguished from normal timeline cards by a corner fold in the upper right corner of the bundle's cover card.

To bundle timeline items, create them with the same value for bundleId. The most recently added item is the bundle's cover card.

The following images show a bundle cover card with the corner fold at the top right corner and two bundled cards below it.

Reading timeline items

Your service can access all timeline items that it created, and all timeline items that were shared with it. Here's how to list the timeline items that are visible to your service.

Raw HTTP

GET /mirror/v1/timeline HTTP/1.1
Host: www.googleapis.com
Authorization: Bearer {auth token}

Java

TimelineItem timelineItem = new TimelineItem();
service.timeline().list().execute();

Python

service.timeline().list().execute()

You can use other REST operations to get, update and delete timeline items.

Accessing attachments

You can access attachments to a timeline item through an array property named attachments. You can then obtain the binary data of an attachment through the contentUrl property of the attachment or with the attachments endpoint.

Raw HTTP

GET /mirror/v1/timeline/{itemId}/attachments/{attachmentId} HTTP/1.1
Host: www.googleapis.com
Authorization: Bearer {auth token}

Java

TimelineItem item = service.timeline().get(itemId).execute();
String attachmentId = item.getAttachments().get(0).getId();
service.attachments().get(itemId, attachmentId).executeAsInputStream();

Creating menu items

Menu items allow users to request actions that are related to the timeline card, and come in two types: built-in menu items and custom menu items.

Built-in menu items provide access to special functionalities provided by Glass, such as reading a timeline card aloud, navigating to a location, sharing an image, or replying to a message:

Custom menu items allow your application to expose behavior that is specific to your Glassware, and you can also provide a menu item icon to match your branding.

Adding built-in menu items

You can add built-in menu items to your timeline items by populating the menuItems array when you insert them. To use a built-in menu item, you only need to populate the action of each menuItem.

Raw HTTP

HTTP/1.1 201 Created
Date: Tue, 25 Sep 2012 23:30:11 GMT
Content-Type: application/json
Content-Length: 303

{
  "text": "Hello world",
  "menuItems": [
    {
      "action": "REPLY"
    }
  ]
}

Defining custom menu items

If built-in menu items don't work for you, you can create custom menu items with your own actions by doing the following when inserting or updating a timeline item:

  • Specify CUSTOM for menuItem.action.
  • Specify a menuItem.id. When users tap the custom menu item, your Glassware receives a notification with menuItem.id populated. This allows you to determine the source of the notification.
  • Specify menuItem.values to add an iconUrl and displayName that appears on Glass. Point to a 50 x 50 PNG image that is white in color with a transparent background for the iconUrl.
  • Specify a displayTime. If you do not specify a displayTime, the timeline item moves to the front of the timeline whenever users tap the custom menu item.

Raw HTTP

HTTP/1.1 201 Created
Date: Tue, 25 Sep 2012 23:30:11 GMT
Content-Type: application/json
Content-Length: 303

{
  "text": "Hello world",
  "displayTime": "2013-08-08T22:47:31-07:00",
  "menuItems": [
    {
      "action": "CUSTOM",
      "id": "complete"
      "values": [{
        "displayName": "Complete",
        "iconUrl": "http://example.com/icons/complete.png"
      }]
    }
  ]
}

Allowing users to pin your timeline card

You can create a menu item that lets your users pin the timeline card, which permanently displays the timeline card to the left of the main clock card. Users can unpin the card as well, by using the same menu item.

The pinning menu item is a built-in menu item, so all you need to do is provide the TOGGLE_PINNED action for a menuItem.

Raw HTTP

HTTP/1.1 201 Created
Date: Tue, 25 Sep 2012 23:30:11 GMT
Content-Type: application/json
Content-Length: 303

{
  "text": "You can pin or unpin this card.",
 "menuItems": [
    {
      "action": "TOGGLE_PINNED"
    }
  ...
 ]
}

Subscriptions

The Mirror API allows you to subscribe to notifications that are sent when the user takes specific actions on a Timeline Item or when the user location has been updated. When you subscribe to a notification, you provide a callback URL that processes the notification.

Receiving notifications

A notification from the Mirror API is sent as a POST request to the subscribed endpoint containing a JSON request body.

Raw HTTP

{
  "collection": "timeline",
  "itemId": "3hidvm0xez6r8_dacdb3103b8b604_h8rpllg",
  "operation": "UPDATE",
  "userToken": "harold_penguin",
  "verifyToken": "random_hash_to_verify_referer",
  "userActions": [
    {
      "type": "<TYPE>",
      "payload": "<PAYLOAD>"
    }
  ]
}

Java

import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson.JacksonFactory;
import com.google.api.services.mirror.model.Notification;

import java.io.IOException;
import java.io.InputStream;
// ...

public class MyClass {
  // ...

  /**
    * Parse a request body into a Notification object.
    *
    * @param requestBody The notification payload sent by the Mirror API.
    * @return Parsed notification payload if successful, {@code null} otherwise.
    */
  static Notification parseNotification(InputStream requestBody) {
    try {
      JsonFactory jsonFactory = new JacksonFactory();

      return jsonFactory.fromInputStream(requetBody, Notification.class);
    } catch (IOException e) {
      System.out.println("An error occurred: " + e);
      return null;
    }
  }

  // ...
}

Python

import json

def parse_notification(request_body):
  """Parse a request body into a notification dict.

  Params:
    request_body: The notification payload sent by the Mirror API as a string.
  Returns:
    Dict representing the notification payload.
  """
  return json.load(request_body)

Your service must respond to the API with a 200 OK HTTP status code if no error occurred. If your service responds with an error code, the Mirror API might try to resend the notification to your service.

Notification types

The Mirror API sends a different notification payload for different events.

Reply

The user has replied to your timeline item using the built-in REPLY menu item:

{
  "collection": "timeline",
  "itemId": "3hidvm0xez6r8_dacdb3103b8b604_h8rpllg",
  "operation": "INSERT",
  "userToken": "harold_penguin",
  "verifyToken": "random_hash_to_verify_referer",
  "userActions": [
    {
      "type": "REPLY"
    }
  ]
}

The itemId attribute is set to the ID of the item containing:

  • inReplyTo attribute set to the ID of the timeline item it is a reply to.
  • text attribute set to the text transcription.
  • recipients attribute set to the creator of the timeline item it is a reply to, if it exists.

Example:

{
  "kind": "glass#timelineItem",
  "id": "3hidvm0xez6r8_dacdb3103b8b604_h8rpllg",
  "inReplyTo": "3236e5b0-b282-4e00-9d7b-6b80e2f47f3d",
  "text": "This is a text reply",
  "recipients": [
    {
      "id": "CREATOR_ID",
      "displayName": "CREATOR_DISPLAY_NAME",
      "imageUrls": [
        "CREATOR_IMAGE_URL"
      ]
    }
  ]
}

Delete

The user has deleted a timeline item:

{
  "collection": "timeline",
  "itemId": "3hidvm0xez6r8_dacdb3103b8b604_h8rpllg",
  "operation": "DELETE",
  "userToken": "harold_penguin",
  "verifyToken": "random_hash_to_verify_referer",
  "userActions": [
    {
      "type": "DELETE"
    }
  ]
}

The itemId attribute is set to the ID of the deleted item. The item no longer contains metadata other than its ID and the isDeleted property.

Custom menu item selected

The user has selected a custom menu item set by your service:

{
  "collection": "timeline",
  "itemId": "3hidvm0xez6r8_dacdb3103b8b604_h8rpllg",
  "operation": "UPDATE",
  "userToken": "harold_penguin",
  "userActions": [
    {
      "type": "CUSTOM",
      "payload": "PING"
    }
  ]
}

The itemId attribute is set to the ID of the menu item that the user selected.

The userActions array contains the list of custom actions that the user took on this item. Your service should handle those actions accordingly.

Location update

A new location is available for the current user:

{
  "collection": "locations",
  "itemId": "latest",
  "operation": "UPDATE",
  "userToken": "harold_penguin",
  "verifyToken": "random_hash_to_verify_referer"
}

When your Glassware receives a location update, send a request to the glass.locations.get endpoint to retrieve the latest known location. Your Glassware receives location updates every ten minutes.

Voice command

Your user has activated a voice command, for example: "Ok Glass, take a note, Cat Stream, Chipotle's birthday is tomorrow". The following notification is sent to your Glassware:

{
  "collection": "timeline",
  "operation": "INSERT",
  "userToken": "chipotle's_owner",
  "verifyToken": "mew mew mew",
  "itemId": "<ITEM_ID>",
  "userActions": [
    {type: "LAUNCH"}
  ]
}

This notification is distinguished from other notifications by the LAUNCH value in the userActions property.

You can then use the value in itemId to fetch the timeline item:

{
  "id": "<ITEM_ID>",
  "text": "Chipotle's birthday is tomorrow",
  "recipients": [
    {"id": "CAT_STREAM"}
  ]
}

The recipients property contains the id of the contact that represents the voice command used.