您可以使用简单的 REST API 插入、更新、读取和删除静态卡片。此外,您还可以将对象附加到静态卡,例如位置信息或媒体。
运作方式
静态卡片默认位于 Glass 时钟右侧,并在分发时显示与用户相关的信息。但是,它们不需要像动态卡片那样需要立即关注,用户可以选择在闲暇时阅读卡片或对其执行操作。
当 Glassware 将静态卡片插入时间轴时,Glass 可能会播放通知提示音来提醒用户。所有以前的静态卡片也会向右移动,并在 7 天后或当 200 张新卡片较新时从时间轴上消失。
适用情形
静态卡片非常适合在发生重要事件时向用户发送定期通知。例如,某个新闻传送服务会即时发送热门新闻报道。Mirror API 静态卡片也可以通过 OPEN_URI
菜单项启动实时卡片或沉浸式。这样,您就可以创建混合互动,利用静态卡片作为通知和实时卡片或沉浸式体验,打造更具互动性的体验。
如需查看时间轴项的可能操作的完整列表,请参阅参考文档。
插入静态卡片
如需插入静态卡片(时间轴项),请将时间轴项的 JSON 表示法发布到 REST 端点。
时间轴项中的大多数字段都是选填的。按照最简单的形式,时间轴项仅包含一条简短的文本消息,如下例所示:
原始 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()
附加了图片的时间轴项在 Google Glass 上如下所示:
正在附加视频
如果您要将视频文件附加到时间轴项,我们建议您流式传输视频,而不是一次性上传整个载荷。Google Mirror API 支持通过 HTTP 实时流式传输、渐进式下载和实时流式传输协议 (RTSP) 进行流式传输。RTSP 经常被防火墙阻止,因此请尽可能使用其他选项。
如需流式传输视频,请使用 PLAY_VIDEO
内置菜单项,并将视频的网址指定为菜单项的 payload
。如需了解详情,请参阅添加内置菜单项和支持的媒体格式。
正在分页
如果时间轴项不适合放在单个时间轴卡片上,但应该与同一卡片相关联,您可以对这些时间轴项进行分页。分页项均共享相同的 timeline.id
,因此具有相同的菜单项集。当用户点按分页的时间轴项时,系统会显示了解详情菜单项。
Glass 会自动对显示 text
的时间轴项进行分页。如需让 Glass 自动对 html
进行分页,请使用 article
标记,并将其类属性设置为 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();
创建菜单项
菜单项可让用户请求与时间轴卡片相关的操作,有两种类型:内置菜单项和自定义菜单项。
借助内置菜单项,您可以使用 Google 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
即可添加 Google 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 每十分钟接收一次位置信息更新。
语音命令
您的用户激活了语音指令,例如:“Ok Glass,添加记事,Cat Stream,Chipotle 的生日是明天”。系统会向您的玻璃软件发送以下通知:
{
"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
。