С помощью простых REST API можно добавлять, обновлять, читать и удалять статические карточки. Кроме того, к статической карточке можно прикреплять объекты, такие как местоположение или медиафайлы.
Как они работают
Статические карточки по умолчанию располагаются справа от стеклянных часов и отображают информацию, актуальную для пользователя на момент доставки. Однако они не требуют немедленного внимания, как динамические карточки , и пользователи могут прочитать карточку или выполнить какие-либо действия с ней в удобное для них время.

Когда Glassware добавляет статические карточки на временную шкалу, Glass может воспроизводить звуковой сигнал, чтобы предупредить пользователей. Все предыдущие статические карточки также смещаются вправо и исчезают с временной шкалы через 7 дней или когда количество новых карточек не превышает 200.
Когда их использовать
Статические карточки отлично подходят для периодической отправки уведомлений пользователям о важных событиях. Например, новостной сервис, который рассылает главные новости по мере их появления. Статические карточки Mirror API также могут запускать динамические карточки или интерактивные окна через пункт меню OPEN_URI . Это позволяет создавать гибридные взаимодействия, использующие статические карточки в качестве уведомлений и динамические карточки или интерактивные окна для более интерактивного опыта.
Полный список возможных операций с элементами временной шкалы см. в справочной документации .
Вставка статических карт
Для вставки статических карточек (элементов временной шкалы) отправьте JSON-представление элемента временной шкалы методом POST на 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()
Элемент хронологии с прикрепленным изображением на Glass выглядит примерно так:

Прикрепляем видео
Если вы прикрепляете видеофайлы к элементам вашей временной шкалы, мы рекомендуем транслировать видео целиком, а не загружать весь контент сразу. API Google Mirror поддерживает потоковую передачу с использованием HTTP-трансляции в реальном времени, прогрессивной загрузки и протокола потоковой передачи в реальном времени (RTSP). Протокол RTSP часто блокируется брандмауэрами, поэтому по возможности используйте другие варианты.
Для потоковой передачи видео используйте встроенный пункт меню PLAY_VIDEO и укажите URL-адрес видео в качестве 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>
По умолчанию первая карточка элемента постраничной хронологии отображается в качестве обложки и снова появляется, когда пользователь выбирает пункт меню « Читать далее» . Чтобы первая карточка не отображалась снова после нажатия кнопки «Читать далее» , можно указать CSS-класс cover-only для первого тега <article> :
<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()
Для получения , обновления и удаления элементов временной шкалы можно использовать и другие REST-операции.
Доступ к вложениям
Доступ к вложениям к элементу временной шкалы можно получить через свойство массива с именем attachments . Затем двоичные данные вложения можно получить через свойство contentUrl вложения или с помощью конечной точки attachments .
Необработанный 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 при их вставке. Чтобы использовать встроенный пункт меню, вам нужно всего лишь указать action для каждого menuItem .
Необработанный 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"
}
]
}
Определение пользовательских пунктов меню
Если встроенные пункты меню вам не подходят, вы можете создать собственные пункты меню с вашими действиями, выполнив следующие действия при вставке или обновлении элемента временной шкалы:
- Укажите
CUSTOMдляmenuItem.action. - Укажите
menuItem.id. Когда пользователи нажимают на пользовательский пункт меню, ваше приложение Glassware получает уведомление с заполненным значениемmenuItem.id. Это позволяет определить источник уведомления. - Укажите
menuItem.values, чтобы добавитьiconUrlиdisplayName, которые будут отображаться на Glass. В качествеiconUrlукажите изображение PNG размером 50 x 50 пикселей, белого цвета с прозрачным фоном. Укажите
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"
}]
}
]
}
Предоставление пользователям возможности закреплять карточку с временной шкалой.
Вы можете создать пункт меню, который позволит пользователям закрепить карточку временной шкалы, благодаря чему карточка будет постоянно отображаться слева от основной карточки часов. Пользователи также могут открепить карточку, используя тот же пункт меню.
Пункт меню «Закрепление» является встроенным пунктом меню, поэтому все, что вам нужно сделать, это указать action TOGGLE_PINNED для menuItem .
Необработанный 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"
}
...
]
}
Подписки
API Mirror позволяет подписаться на уведомления , отправляемые при выполнении пользователем определенных действий с элементом временной шкалы или при обновлении местоположения пользователя. При подписке на уведомление вы указываете URL-адрес обратного вызова, который обрабатывает уведомление.
Получение уведомлений
Уведомление от 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)
Если ошибок не произошло, ваш сервис должен ответить API кодом состояния HTTP 200 OK . Если ваш сервис отвечает кодом ошибки, Mirror API может попытаться повторно отправить уведомление вашему сервису.
Типы уведомлений
API Mirror отправляет разные данные уведомления для разных событий.
Отвечать
Пользователь ответил на вашу запись в ленте, используя встроенный пункт меню 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 устанавливается равным идентификатору удаленного элемента. Элемент больше не содержит метаданных, кроме своего идентификатора и свойства isDeleted .
Выбран пункт пользовательского меню
Пользователь выбрал пункт пользовательского меню, заданный вашей службой:
{
"collection": "timeline",
"itemId": "3hidvm0xez6r8_dacdb3103b8b604_h8rpllg",
"operation": "UPDATE",
"userToken": "harold_penguin",
"userActions": [
{
"type": "CUSTOM",
"payload": "PING"
}
]
}
Атрибут itemId устанавливается равным идентификатору пункта меню, выбранного пользователем.
Массив userActions содержит список пользовательских действий, которые пользователь совершил с этим элементом. Ваш сервис должен обрабатывать эти действия соответствующим образом.
Обновление местоположения
Для текущего пользователя доступно новое местоположение:
{
"collection": "locations",
"itemId": "latest",
"operation": "UPDATE",
"userToken": "harold_penguin",
"verifyToken": "random_hash_to_verify_referer"
}
Когда ваше устройство Glassware получает обновление местоположения, отправьте запрос к конечной точке glass.locations.get , чтобы получить последнее известное местоположение. Ваше устройство Glassware получает обновления местоположения каждые десять минут.
Голосовая команда
Пользователь активировал голосовую команду, например: «Окей, Glass, запиши, Cat Stream, завтра день рождения Chipotle». На ваше устройство Glassware отправляется следующее уведомление:
{
"collection": "timeline",
"operation": "INSERT",
"userToken": "chipotle's_owner",
"verifyToken": "mew mew mew",
"itemId": "<ITEM_ID>",
"userActions": [
{“type”: "LAUNCH"}
]
}
Это уведомление отличается от других уведомлений значением LAUNCH в свойстве userActions .
Затем вы можете использовать значение в itemId , чтобы получить элемент временной шкалы:
{
"id": "<ITEM_ID>",
"text": "Chipotle's birthday is tomorrow",
"recipients": [
{"id": "CAT_STREAM"}
]
}
Свойство recipients содержит id контакта, представляющего использованную голосовую команду.