您可以使用简单的 REST API 插入、更新、读取和删除静态卡片。此外,您还可以将对象附加到静态卡片,例如地点或媒体。
运作方式
默认情况下,静态卡片位于 Glass 时钟的右侧,并显示在交付时与用户相关的信息。不过,它们不像实时卡片那样需要立即处理,用户可以自行选择何时阅读或处理卡片。

当 Glassware 将静态卡片插入时间轴时,Glass 可能会播放通知声音来提醒用户。所有之前的静态卡片也会向右移动,并在 7 天后或当有 200 张新卡片时从时间轴中消失。
何时使用此类附加信息
静态卡片非常适合在发生重要事件时向用户发送周期性通知。例如,一个新闻推送服务,可随时发送热门新闻报道。Mirror API 静态卡片还可以通过 OPEN_URI 菜单项启动实时卡片或沉浸式体验。这样一来,您就可以创建混合互动,利用静态卡片作为通知,并利用实时卡片或沉浸式体验来提供更具互动性的体验。
如需查看时间轴项目的可能操作的完整列表,请参阅参考文档。
插入静态卡片
如需插入静态卡片(时间轴项),请向 REST 端点 POST 时间轴项的 JSON 表示形式。
时间轴项中的大多数字段都是可选的。时间轴项最简单的形式仅包含一条简短的文本消息,如以下示例所示:
原始 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()
成功后,您会收到 201 Created 响应代码以及所创建商品的完整副本。对于上一个示例,成功响应可能如下所示:
原始 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"
}
插入到用户时间轴中的内容如下所示:

插入带有附件的时间轴项
一张图片胜过千言万语,这比您能放入时间轴项目中的内容多得多。为此,您还可以将图片和视频附加到时间轴项中。以下示例展示了如何插入带有照片附件的时间轴项:
原始 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()
在 Glass 上,附加了图片的时间轴项如下所示:

附加视频
如果您要将视频文件附加到时间轴项,建议您流式传输视频,而不是一次性上传整个载荷。 Google Mirror API 支持通过 HTTP Live Streaming、渐进式下载和实时流协议 (RTSP) 进行流式传输。RTSP 经常会被防火墙屏蔽,因此请尽可能使用其他选项。
如需播放视频,请使用 PLAY_VIDEO 内置菜单项,并将视频的网址指定为菜单项的 payload。如需了解详情,请参阅添加内置菜单项和支持的媒体格式。
分页
您可以对无法显示在单个时间轴卡片上的时间轴项进行分页,但这些时间轴项应与同一张卡片相关联。分页项都共享相同的 timeline.id,因此具有相同的菜单项集。当用户点按分页时间轴项时,系统会显示阅读更多菜单项。
Glass 会自动对显示 text 的时间轴项进行分页。如需让 Glass 自动分页 html,请使用 article 标记,并将其 class 属性设置为 auto-paginate,如以下示例所示:
<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>
如需手动分页,请为要在每张卡片上显示的内容使用 article 标记。Glass 会在单独的子时间轴卡片中显示每个 article 标记的内容。例如,您可以使用以下 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>
默认情况下,分页时间轴项的第一张卡片会显示为封面卡片,并且当用户选择阅读更多菜单项时,系统会再次显示该卡片。如需防止在点按了解详情后再次显示第一张卡片,您可以为第一个 <article> 标记指定 cover-only CSS 类:
<article class="cover-only">
...
cover-only 类还支持自动分页的时间轴项:
<article class="auto-paginate cover-only">
...
捆绑
通过捆绑,您可以将相关但不同的项分组在一起,例如电子邮件会话中的各个消息。套装有一个主封面卡片,用户点按该卡片即可显示一个子时间轴,其中包含套装中的其他卡片。与普通时间轴卡片不同的是,合集的封面卡片右上角有折角。
如需将时间轴项捆绑在一起,请为它们创建相同的 bundleId 值。最近添加的项是套装的封面卡片。
以下图片显示了一张右上角有折角的套装封面卡片,以及位于其下方的两张套装卡片。


读取时间轴项目
您的服务可以访问其创建的所有时间轴项,以及与该服务共享的所有时间轴项。以下是如何列出您的服务可见的时间轴项。
原始 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()
访问附件
您可以通过名为 attachments 的数组属性访问时间轴项目的附件。然后,您可以通过附件的 contentUrl 属性或使用附件端点获取附件的二进制数据。
原始 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();
创建菜单项
借助菜单项,用户可以请求与时间轴卡片相关的操作,菜单项分为两种类型:内置菜单项和自定义菜单项。
内置菜单项可用于访问 Glass 提供的特殊功能,例如大声朗读时间轴卡片、前往某个位置、分享图片或回复消息:

借助自定义菜单项,您的应用可以公开特定于 Glassware 的行为,您还可以提供与品牌相符的菜单项图标。
添加内置菜单项
您可以在插入时间轴项时填充 menuItems array,从而向时间轴项添加内置菜单项。
如需使用内置菜单项,您只需填充每个 menuItem 的 action。
原始 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"
}
]
}
定义自定义菜单项
如果内置菜单项不适合您,您可以在插入或更新时间轴项时执行以下操作,以创建包含您自己的操作的自定义菜单项:
- 为
menuItem.action指定CUSTOM。 - 指定
menuItem.id。当用户点按自定义菜单项时,Glassware 会收到一条通知,其中填充了menuItem.id。这样,您就可以确定通知的来源。 - 指定
menuItem.values以添加显示在 Glass 上的iconUrl和displayName。指向一个 50 x 50 的 PNG 图片,该图片为白色,具有透明背景,用于iconUrl。 指定
displayTime。如果您未指定displayTime,则每当用户点按自定义菜单项时,时间轴项都会移到时间轴的前面。
原始 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"
}]
}
]
}
允许用户固定您的时间轴卡片
您可以创建一个菜单项,让用户固定时间轴卡片,这样时间轴卡片就会永久显示在主时钟卡片的左侧。用户也可以使用相同的菜单项取消固定卡片。
固定菜单项是内置菜单项,因此您只需为 menuItem 提供 TOGGLE_PINNED
action 即可。
原始 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"
}
...
]
}
订阅
借助 Mirror API,您可以订阅通知,以便在用户对时间轴项执行特定操作或用户位置信息更新时收到通知。订阅通知时,您需要提供一个用于处理通知的回调网址。
接收通知
Mirror API 的通知会以 POST 请求的形式发送到订阅的端点,其中包含 JSON 请求正文。
原始 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)
如果未发生任何错误,您的服务必须以 200 OK HTTP 状态代码响应 API。如果您的服务以错误代码进行响应,Mirror API 可能会尝试将通知重新发送给您的服务。
通知类型
Mirror API 会针对不同的事件发送不同的通知载荷。
回复
用户使用内置的 REPLY 菜单项回复了您的时间轴项:
{
"collection": "timeline",
"itemId": "3hidvm0xez6r8_dacdb3103b8b604_h8rpllg",
"operation": "INSERT",
"userToken": "harold_penguin",
"verifyToken": "random_hash_to_verify_referer",
"userActions": [
{
"type": "REPLY"
}
]
}
itemId 属性设置为包含以下内容的商品的 ID:
inReplyTo属性设置为相应时间轴项的ID。text属性设置为文本转写。recipients属性设置为相应时间轴项的creator(如果存在)。
示例:
{
"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"
]
}
]
}
删除
用户删除了时间轴项:
{
"collection": "timeline",
"itemId": "3hidvm0xez6r8_dacdb3103b8b604_h8rpllg",
"operation": "DELETE",
"userToken": "harold_penguin",
"verifyToken": "random_hash_to_verify_referer",
"userActions": [
{
"type": "DELETE"
}
]
}
itemId 属性设置为已删除商品的 ID。相应商品不再包含除其 ID 和 isDeleted 属性之外的元数据。
自定义菜单项被选中
用户已选择您的服务设置的自定义菜单项:
{
"collection": "timeline",
"itemId": "3hidvm0xez6r8_dacdb3103b8b604_h8rpllg",
"operation": "UPDATE",
"userToken": "harold_penguin",
"userActions": [
{
"type": "CUSTOM",
"payload": "PING"
}
]
}
itemId 属性设置为用户所选菜单项的 ID。
userActions 数组包含用户对此商品执行的自定义操作的列表。您的服务应相应地处理这些操作。
位置信息更新
当前用户可使用的新位置:
{
"collection": "locations",
"itemId": "latest",
"operation": "UPDATE",
"userToken": "harold_penguin",
"verifyToken": "random_hash_to_verify_referer"
}
当 Glassware 收到位置信息更新时,向 glass.locations.get 端点发送请求,以检索最新的已知位置信息。Glassware 每 10 分钟接收一次位置信息更新。
语音命令
用户已启动语音指令,例如:“Ok Glass,记事,Cat Stream,Chipotle 的生日是明天”。系统会向您的 Glassware 发送以下通知:
{
"collection": "timeline",
"operation": "INSERT",
"userToken": "chipotle's_owner",
"verifyToken": "mew mew mew",
"itemId": "<ITEM_ID>",
"userActions": [
{“type”: "LAUNCH"}
]
}
此通知通过 userActions 属性中的 LAUNCH 值与其他通知区分开来。
然后,您可以使用 itemId 中的值来提取时间轴项:
{
"id": "<ITEM_ID>",
"text": "Chipotle's birthday is tomorrow",
"recipients": [
{"id": "CAT_STREAM"}
]
}
recipients 属性包含表示所用语音指令的联系人的 id。