Google I/O 2016: WatchFace 2.0 — Complications

Привет, Хабр! Совсем недавно прошёл Google I/O (если кто пропустил наш репортаж — вам сюда), где увидел свет новый API для отображения данных на циферблатах часов. Название его пришло из часовой отрасли: по-русски их традиционно зовут «Усложнения», ну, а по-английски —«Complications».

b6f6b89c33c34f522da59e3873aa4ccb.png

Если вкратце — это механизм отображения какой-либо дополнительной информации на часах, помимо, собственно времени: в реальных могут быть всякие планеты-звёзды-календари, ну, а в нашем случае — всё, что придёт вам в голову. Сегодня мы покажем, на что способен API Watch Face Complications и как с ним работать.


Прим.: текст и обзор API подготовил для вас лидер GDG Ростова-на-Дону Александр Куликовский, далее весь текст — от его лица. :)

Знакомимся с WatchFace 2.0


Привет, я Александр Куликовский, занимаюсь разработкой для Android с 2010 года. На данный момент работаю в компании Sebbia в должности Senior Android Engineer и возглавляю GDG Ростова-на-Дону.

Приступаем к работе


Итак, чтобы попробовать новые возможности Android Watch 2.0 нам потребуется чистый проект. Создаём его, указываем API N в качестве минимальной версии SDK (minSdk) для телефона и часов.

ac3b84e9e3716dc693e8a537ec9e373a.png
Activity для телефона нам не нужна, поэтому выбираем шаблон «Add no activity». Для часов же возьмём стандартный шаблон Watch Face. Дадим имя сервису ComplicationWatchFaceService, стиль — аналоговый.

5428c395053395fc5856dc2d9ab6a57b.png

После этого получим стандартный проект.

Перед тем как продолжим, нам необходимо убедиться в том, что в gradle-файле, описывающем модуль wear, в разделе зависимостей wearable подключены не ниже версии 2.0.0:

compile 'com.google.android.support:wearable:2.0.0-alpha1'

Подключаем «Усложнения»


Добавляем complication к нашему watchface. В реальном приложении пользователь сможет выбирать источники информации для получения complication-данных, нам же надо обеспечить получение и отображения такого рода информации.

Чтобы начать получать complication-данные, мы вызваем метод setActiveComplications в ComplicationWatchFaceService.Engine, и получаем список идентификаторов посадочных мест для отображения информации:

// Unique IDs for each complication.
private static final int LEFT_DIAL_COMPLICATION = 0;
private static final int RIGHT_DIAL_COMPLICATION = 1;

// Left and right complication IDs as array for Complication API.
public static final int[] COMPLICATION_IDS = {LEFT_DIAL_COMPLICATION, RIGHT_DIAL_COMPLICATION};

….
public void onCreate(SurfaceHolder holder) {
...
setActiveComplications(COMPLICATION_IDS);
...
}


Данные для отображения приходят в методе onComplicationDataUpdate нашего ComplicationWatchFaceService.Engine.

Это важно:


По спецификации мы вольны отображать полученные данные как угодно, но для каждого типа данных есть набор обязательных полей для отображения. Более детально об этом можно посмотреть в таблице.

Возвращаемся к нашему примеру. Метод onComplicationDataUpdate дал нам набор данных, которые мы можем отобразить. Так какотрисовка нашего watchface происходит только в методе onDraw, то полученные данные надо предварительно куда-то сохранить. Для этого создадим SparseArray, инициализируем его в onCreate и будем все значения складывать в него в методе onComplicationDataUpdate:

@Override
public void onComplicationDataUpdate(int watchFaceComplicationId, ComplicationData data) {
   super.onComplicationDataUpdate(watchFaceComplicationId, data);
   mActiveComplicationDataSparseArray.put(watchFaceComplicationId, data);
   invalidate();
}


Само рисование на canvas«е нам не интересна — про это уже сто раз рассказывали. А вот на что надо обратить внимание, так это на то, откуда, собственно, взять данные, которые мы будем отображать. В случае с TYPE_SHORT_TEXT код будет выглядеть следующим образом:

private void drawComplications(Canvas canvas, long currentTimeMillis) {
   ComplicationData complicationData;

   for (int i = 0; i < COMPLICATION_IDS.length; i++) {

       complicationData = mActiveComplicationDataSparseArray.get(COMPLICATION_IDS[i]);

       if ((complicationData != null)
               && (complicationData.isActive(currentTimeMillis))
               && (complicationData.getType() == ComplicationData.TYPE_SHORT_TEXT)) {

           ComplicationText mainText = complicationData.getShortText();
           ComplicationText subText = complicationData.getShortTitle();

           CharSequence complicationMessage =
                   mainText.getText(getApplicationContext(), currentTimeMillis);

           if (subText != null) {
               complicationMessage = TextUtils.concat(
                       complicationMessage,
                       " ",
                       subText.getText(getApplicationContext(), currentTimeMillis));
           }
……..


Но теперь, если запустить проект, мы никаких complications не увидим. А всё потому, что пользователь не задал источники информации для отображения. Потому что не мог — меню настроек-то мы ему не выделили… Исправим это: создадим активити настроек и пропишем её в манифесте:


….

 
   
       
       
       
   



В данной активити мы отобразим список информации о доступных посадочных местах для complications, апо клику на элемент будем запускать стандартный экран выбора доступных источников данных:

Integer tag = (Integer) viewHolder.itemView.getTag();
ComplicationItem complicationItem = mAdapter.getItem(tag);

startActivityForResult(ProviderChooserIntent.createProviderChooserIntent(
       complicationItem.watchFace,
       complicationItem.complicationId,
       complicationItem.supportedTypes), PROVIDER_CHOOSER_REQUEST_CODE);

Если вы всё сделали правильно, то в результате в момент выбора у вас получится примерно такая картина:

9f37936693fd4799a5b281e7f69c5a2b.png

EkV3DjwiAih9xoJTsv__qOYk_SOUA9Zjptrls8vcПроверим интерфейс в деле: выбираем для левого complication отображение даты, а для правого — просто произвольное число.

Теперь, как только появятся новые данные в выбранных пользователем источниках информации, будет вызван метод onCompliationDataUpdate, все данные будут сохранены в SparseArray и при следующем вызове onDraw будут отображены на экране нашего циферблата.

Как видите, работа с «усложнениями» максимально проста: десяток-другой строк кода, и всё работает. Дело за малым — обновить ваши проекты и добавить поддержку новых возможностей. Удачи!

© Habrahabr.ru