Контракты
Общий доступ
Хотя чудо-кнопка Поиск расположена первой на Панели чудо-кнопок, певый контракт, который я хочу подробно рассмотреть – это Общий доступ. В конце концов, это одна из первых вещей, которую узнают дети! Я начинаю с Общего доступа, так как мы уже видели его со стороны источника, в Главе 2 курса "Введение в разработку приложений для Windows 8 с использованием HTML, CSS и JavaScript", в приложении "Here My Am!", а здесь, в конце этого раздела, мы так же коснемся старого доброго буфера обмена.
Вот, что мы уже знаем об Общем доступе, более подробно процесс работы с ним показан на рис. 1.1.
- Приложение с содержимым, подходящим для общего доступа, прослушивает событие datarequested объекта, возвращаемого Windows.ApplicationModel.DataTransfer.DataTransferManager.getForCurrentView(). Это событие WinRT (при работе с которым вы должны помнить об использовании removeEventListener) вызывается, когда пользователь активирует чудо-кнопку Общий доступ. Заметим, что если приложение вовсе не прослушивает это событие, панель Общего доступа отобразит сообщение по умолчанию "Это приложение не поддерживает общий доступ "unable to share" (это сообщение, несомненно, разочарует пользователей!).
- В обработчике события приложение определяет, есть ли в его текущем состоянии что-то для общего доступа. Если есть, оно заполняет Windows.ApplicationModel.DataTransfer.DataPackage, предоставленный в событии. (Здесь возможны варианты в зависимости от наличия или отсутствия выделения. Если пользователю нужно сделать выделение для того, чтобы общий доступ заработал, приложение может отобразить сообщение об этом).
- На основании форматов данных в пакете, Windows, то есть – брокер общего доступа, управляющий контрактом, определяет целевые приложения общего доступа для отображения пользователю. Пользователь так же может управлять тем, какие приложения следует отображать, посредством параметра Изменение параметров компьютера (Change PC Settings) > Общий доступ (Share).
- Когда пользователь выбирает цель общего доступа, активируется соответствующее приложение и принимает пакет данных для обработки.
Рис. 1.1. Обработка реализации контракта Общий доступ, инициированная после активации пользователем чудо-кнопки Общий доступ
Приложение-источник (Source App)
Прослушивает событие datarequested объекта DataTransferManager (Listens for datarequested event on DataTransferManager)
Принимает событие и заполняет DataPackage (Receives events and fills DataPackage)
Завершает асинхронный вызов (посредством отложенной операции) и возвращается из операции обработки (Completes async calls (via deferrals) and returns)
Необязательный прослушиватель для события targetApplicationChosen объекта DataTransferManager (Optional listen for targetApplicationChosen event on DataTransferManager)
Брокер общего доступа(Share Broker)
Чудо-кнопка Общий доступ активирована, вызвано событие datarequested для приложения переднего плана (Share charm is invoked, datarequested fired to foreground app)
Отфильтрованный список целевых приложений и быстрых ссылок (Filters list of target apps and Quicklinks base on format)
Пользователь выбирает целевое приложение или быструю ссылку (User selects target app or Quicklink)
Активация целевого приложения с пакетом данных (Activate target app with data package)
Целевое приложение общего доступа (Share Target App)
Активация для общего доступа (Activated for sharing)
Обрабатывает содержиме DataPackage (вывод запросов) (Processes DataPackage contents (requests rendering))
Сообщает о завершении операции и операция завершена (Reports complete and sharing is complete)
Все это предоставляет пользователям удобный способ взять что-то, что им нравится, в одном приложении и передать в другое приложение, воспользовавшись простым жестом у края экрана и выбором целевого приложения. Это похоже на буфер обмена с широкими семантическими возможностями, работая с которым, вы не задумываетесь о том, как подключиться к другим приложениям. Что особенно приятно в случае с контрактом Общий доступ, другими словами, что приложение-источник не должно заботиться о том, что произойдет с данными – его единственная задача – предоставить данные, подходящие для общего доступа в тот момент, когда пользователь активирует чудо-кнопку Общий доступ (если, на самом деле, это подходящие данные, а иногда это не так). Это освобождает приложение-источник от необходимости предсказывать, предвидеть или предугадывать то, что пользователи могут захотеть сделать с данными. Может быть, пользователь решит отправить их по электронной почте, может быть – в социальную сеть, или захочет поместить их в приложение для управления контентом… кто знает?
Да, только пользоваель и знает, и брокер общего доступа делает с данными то, что решит пользователь! Получив пакет данных из источника, брокер сравнивает формат данных в пакете с форматами, которые поддерживают целевые приложения, объявив о них в манифесте. Затем брокер показывает список этих приложений пользователю. Этот список может содержать приложения, и то, что называется быстрой ссылкой (quicklink) (если быть точным, то это объекты Windows.ApplicationModel.DataTransfer.ShareTarget.Quicklink), которые поддерживают некоторые приложения, но более конкретным образом. Например, когда приложение электронной почты отображается в качестве целевого для нужд общего доступа, лучшее, что оно может сделать – это создать новое сообщение без указания конкретного адресата. Быстрая ссылка, однако, может идентифицировать конкретных получателей электронного письма, например, человека или людей, которым вы часто пишете письма. Быстрая ссылка, таким образом, это возможность выбрать целевое приложение с добавлением конкретной конфигурационной информации.
В любом случае, когда пользователь выбирает цель, запускается какое-то приложение. В случае с контрактом Общий доступ, приложение запускается с видом активации shareTarget. Это сообщает ему о том, что ему не нужно показывать свой интерфейс по умолчанию, но, вместо этого, нужно показать специальную панель общего доступа (с возможностью автоматического скрытия при неактивности). С ее помощью пользователь может уточнить, что и как следует добавить в приложение из данных общего доступа. Например, на панели общего доступа для приложений социальных сетей часто предоставляют место для ввода комментария к данным перед их отправкой. Приложение электронной почты может предоставить средства для правки сообщения перед отправкой. Клиент сервиса для работы с фотографиями может позволить добавить подпись, указать место, где была снята фотография, людей, изображенных на фото и так далее. В общем, вы поняли. Все это, собрано вместе для обеспечения плавного взаимодействия пользователя и системы – от выбора данных, которыми он хочет поделиться, до указания приложения и настройки этих данных.
В целом, таким образом, контракт Общий доступ позволяет связывать приложения друг с другом для целей организации общего доступа к данным, и, при этом, приложениям не нужно ничего знать друг о друге. Это создает отлично расширяемую и масштабируемую среду: так как все потенциальные целевые приложения отображаются лишь в панели чудо-кнопки Общий доступ, им не нужно загромождать приложения-источники, как обычно делается на веб-страницах. Это – принцип "сначала содержимое, а потом уже внешнее оформление" в действии.
Приложениям-источникам так же не нужно обновляться, когда, например, новая цель общего доступа становится популярной (например – новая социальная сеть): все, что нужно – это единственное целевое приложение. Что касается этих целевых приложений, им не нужно возвещать о себе всему миру: посредством контрактов приложения-источники автоматически получают возможность использовать любые целевые приложения, которые станут доступны в будущем. И, с точки зрения конечного пользователя, работа с чудо-кнопкой Общий доступ становится все интереснее и интереснее, когда он устанавливает больше программ, поддерживающих возможность Общего доступа.
В то же время, приложение-источник может кое-что узнать о том, как использованы данные, предоставленные для общего доступа. Вместе с событием datarequested, DataTransferManager так же запускает событие targetApplicationChosen для прослушивающего это событие приложения-источника. В данном случае eventArgs содержит лишь одно свойство – applicationName (имя приложения). Это не особо полезно для любых других API WinRT, однако, эти сведения вы можете использовать для целей собственной аналитики. Подобные данные могут помочь вам понять, обеспечиваете ли вы лучший опыт взаимодействия, предоставляя в общий доступ больше форматов данных, например, или, если типичные целевые приложение поддерживают пользовательские форматы, вы можете включить их поддержку в будущих обновлениях программы.
Приложения-источники
Придем к окончательному пониманию приложений-источников, изучив некоторые подробности, которые мы еще не рассматривали, преимущественно касающиеся того, как приложение-источник заполняет пакет данных, и параметров, которые у него есть для обработки запроса. Для этой цели я предлагаю вам загрузить и запустить пару примеров: "Приложение-источник для общего доступа" (http://code.msdn.microsoft.com/windowsapps/Sharing-Content-Source-App-d9bffd84) и "Приложение-цель для общего доступа" (http://code.msdn.microsoft.com/windowsapps/Sharing-Content-Target-App-e2689782). Мы рассмотрим оба, а последний содержит хороший пример того, как целевое приложение принимает пакет данных, созданный в приложении-источнике.
Пример реализации приложения-источника предоставляет несколько сценариев, показывающих, как предоставлять общий доступ данные различных типов. Он так же показывает, как программно активировать чудо-кнопку Общий доступ. Это делать не рекомендуется, но это возможно. Если это соответствует сценарию вашего приложению, вот как это делается:
Windows.ApplicationModel.DataTransfer.DataTransferManager.showShareUI();
Такой вызов, как и в случае, когда пользователь нажимает на чудо-кнопку, вызывает событие datarequested, где eventArgs.request – это объект Windows.ApplicationModel.DataTransfer.DataRequest (http://msdn.microsoft.com/library/windows/apps/windows.applicationmodel.datatransfer.datarequest.aspx). Объект запроса содержит два свойства и два метода:
data имеет тип DataPackage и его нужно заполнить. Он содержит методы для предоставления доступа к данным различных форматов, важно отметить, что не все форматы будут немедленно обработаны, вместо этого, их обработка произойдет только тогда, когда целевое приложение их запросит.
deadline – это свойство типа Data, показывающее время в будущем, когда данные, которые вы сделали доступными, будут неактуальны (таким образом, не будут обработаны). Это позволяет учесть ситуацию, когда между запросом данных и реальной попыткой их использовать может пройти неопределенное время. В случае с отложенной обработкой данных, как отмечено выше для свойства data, возможно, что некоторые непостоянные источники данных могут исчезнуть. Когда это время отмечено в deadline, запросы на обработку, которые происходят позже него, будут проигнорированы.
failWithDisplayText – это метод, который сообщает брокеру общего доступа, что в данный момент предоставление данных в общий доступ невозможно, так же предоставляется строка, которая сообщит пользователю, почему это так (возможно, ему нужно выделить что-нибудь). Вы вызываете это тогда, когда нет выделенных данных, формат которых подходит для предоставления в общий доступ, если произошла ошибка при заполнения пакета данных по любой другой причине. Текст, который вы предоставите, будет отображен в панели чудо-кнопки Общий доступ (и, таким образом, его следует локализовывать). Сценарий 8 примера о приложении-источнике показывает использование этого подхода в простом случае, когда он не предоставляет данных в ответ на событие datarequested.
getDeferral предоставляется для асинхронных операций, которые могут вам понадобиться для заполнения пакета данных (это те же самые отложенные операции, которые используются везде в WinRT API). Отметим, что событие datarequested имеет тайм-аут в 200 мс, после чего в интерфейсе чудо-кнопки Общий доступ отобразится сообщение о невозможности немедленного предоставления данных в общий доступ. Запрос отложенной операции не меняет этот тайм-аут, он лишь предотвращает ситуацию, когда datarequested считает, что пакет данных готов как только вы возвратились из обработчика.
Обычная структура обработчика datarequested, таким образом, направлена на заполнение минимальных свойств eventArgs.request.data и вызов eventArgs.request.failWithDisplayText по возникновению ошибки. Мы встретимся с этой структурой в большинстве сценариев примера:
var dataTransferManager = Windows.ApplicationModel.DataTransfer.DataTransferManager.getForCurrentView(); // Удалите этот прослушиватель в соответствии с требованиями dataTransferManager.addEventListener("datarequested", dataRequested); function dataRequested(e) { var request = e.request; // Нужен заголовок (Title) var dataPackageTitle = document.getElementById("titleInputBox").value; if ( /* Проверяем, есть ли подходящие для целей общего доступа данные */ ) { request.data.properties.title = dataPackageTitle; // Параметр description необязателен. var dataPackageDescription = document.getElementById("descriptionInputBox").value; request.data.properties.description = dataPackageDescription; // Вызываем request.data.setText, setUri, setBitmap, setData, и так далее. } else { request.failWithDisplayText(/* Сообщение об ошибке */ ); } }
Как мы здесь видим, объект request.data.properties (имеющий тип DataPackagePropertySet ( http://msdn.microsoft.com/library/windows/apps/windows.applicationmodel.datatransfer.datapackagepropertyset.aspx)), это то место, куда вы записываете данные наподобие заголовка и описания пакета данных. Вот другие свойства:
- applicationListingUri – это URI страницы вашего приложения в Магазине Windows, которое должно быть записано в возвращаемое значение Windows.ApplicationModel.Store.CurrentApp.linkUri (http://msdn.microsoft.com/library/windows/apps/windows.applicationmodel.store.currentapp.linkuri.aspx).
- applicationName – это строка, которая помогает целевому приложению получить ту же информацию, которую источник может получить из события targetApplicationChosen.
- fileTypes – строковой вектор, строки которого должны принадлежать перечислению StandardDataFormats ( http://msdn.microsoft.com/library/windows/apps/windows.applicationmodel.datatransfer.standarddataformats.aspx), но он может содержать и сведения о пользовательских форматах.
- size – количество элементов, если данные в пакете – это коллекция, например, коллекция файлов.
- thumbnail – поток, содержащий изображение-эскиз. Обычно именно из-за надобности получения этого изображения и используют метод DataRequest.getDeferral.
Помимо этого объект data.properties так же поддерживает пользовательские свойства, посредством своих методов insert, remove и других. Это делает для приложения-источника возможным передавать пользовательские свойства вместе с пользовательскими форматами, делая все это расширяемым, если новые форматы данных станут широко распространенными в будущем.
В вышеприведенном коде, основная задача приложения-источника заключается в заполнении пакета данных с помощью вызова различных set*-методов пакета. Для стандартных форматов, которые описаны в перечислении StandardDataFormats, это отдельные методы: setText, setUri, setHtmlFormat, setRtf (rich text format, довольно ранний предшественник HTML), setBitmap, и setStorageItems (для папок и файлов). Все эти методы, кроме setRtf, представлены в коде примера.
Общий доступ к тексту – Сценарий 1 (js/text.js):
var dataPackageText = document.getElementById("textInputBox").value; request.data.setText(dataPackageText);
Передача в общий доступ ссылки – Сценарий 2 (js/link.js). Это можно использовать и для локального и для удаленного содержимого:
request.data.setUri(new Windows.Foundation.Uri(document.getElementById("linkInputBox").value));
Общий доступ к изображению и элементу хранения – Сценарий 3 (js/image.js):
var imageFile; // StorageFile, полученный через средство выбора файлов //В событии datarequested var streamReference = Windows.Storage.Streams.RandomAccessStreamReference.createFromFile(imageFile); request.data.properties.thumbnail = streamReference; // Рекомендуется всегда, для предоставления общего доступа, использовать и setBitmap и setStorageItems // для изображения, так как целевое приложение может поддерживать либо то, либо другое // Поместим файл изображения в массив и передадим его setStorageItems request.data.setStorageItems([imageFile]); // Метод setBitmap требует RandomAccessStreamReference request.data.setBitmap(streamReference);
Общий доступ к файлам – Сценарий 4 (js/file.js)
var selectedFiles; // Коллекция объектов StorageFile, полученных из средства выбора файлов // В событии datarequested request.data.setStorageItems(selectedFiles);
Что касается общего доступа к HTML, то это может быть очень простой задачей, если HTML у вас есть в виде строки:
request.data.setHtmlFormat(someHtml);
Для этой цели вы можете воспользоваться полезным объектом Windows.ApplicationModel.DataTransfer.HtmlFormatHelper (http://msdn.microsoft.com/library/windows/apps/hh738437.aspx), который предоставляет методы для создания правильно отформатированной разметки. Справедливо по отношению к HTML и то, что он часто ссылается на другое содержимое, наподобие изображений, которые не включены непосредственно в разметку. Что же делать в такой ситуации? К счастью, дизайнеры API поработали над этим: вы можете использовать свойство пакета данных resourceMap для связывания относительных URI в HTML с потоком изображения. Мы видим это в Сценарии 6 (js/html.js):
var path = document.getElementById("htmlFragmentImage").getAttribute("src"); var imageUri = new Windows.Foundation.Uri(path); var streamReference = Windows.Storage.Streams.RandomAccessStreamReference.createFromUri(imageUri); request.data.resourceMap[path] = streamReference;
Еще одна интересная часть Сценария 6 – это та, которая заменяет пакет данных в eventArgs на новый, который она создает следующим образом:
var range = document.createRange(); range.selectNode(document.getElementById("htmlFragment")); request.data = MSApp.createDataPackage(range);
Как вы можете видеть, метод MSApp.createDataPackage (http://msdn.microsoft.com/library/windows/apps/Hh831247.aspx) берет диапазон DOM (в данном случае – часть текущей страницы) и создает из него пакет данных, где, при обработке, вызывается метод пакета setHtmlFormat (поэтому вы не увидите явного вызова этого метода в Сценарии 6). Ценно заметить, что имеется еще и метод MSApp.createDataPackageFromSelection (http://msdn.microsoft.com/library/windows/apps/hh831248.aspx), который выполняет то же самое с текущим выделенным фрагментом DOM. Очевидно, вы воспользуетесь этим, если у вас есть элемент, который можно редактировать, данные которого вы хотите предоставить в общий доступ.