Опубликован: 15.05.2013 | Доступ: свободный | Студентов: 265 / 9 | Длительность: 24:25:00
Специальности: Системный архитектор
Лекция 1:

Контракты

Лекция 1: 1234 || Лекция 2 >

Организация общего доступа к данным разных форматов

Как показано в Сценарии 3, не только разрешено, но и рекомендовано, предоставлять в общий доступ к данным во всех возможных форматах, таким образом, получая больше потенциальных целевых приложений. Все это значит, что вы вызываете все методы set*, которые имеют смысл, в обработчике datarequested. Это включает в себя вызов setData для пользовательских форматов и setDataProvider для отложенной обработки, как описано в следующих двух разделах.

Пользовательские форматы данных: schema.org

Я думаю, что много лет назад дизайнеры API решили, что пытаться предвидеть все форматы данных, которыми могут обмениваться приложение, это – бесполезное занятие. API WinRT следует этой идее, поэтому вместе с методами set* для конкретных форматов, в DataPackage мы можем найти и общий метод setData. Он принимает идентификатор формата (строку) и данные для общего доступа. Это показано в Сценарии 7 примера с использованием формата "http://schema.org/Book" и данных в строковом формате JSON (js/custom.js):

 request.data.setData(dataFormat, JSON.stringify(book));

Так как идентификатор пользовательского формата данных – это обычная строка, вы можете использовать здесь все, что хотите. Например, здесь могут быть полезны весьма специфичные форматы данных, например, в сценарии обмена данными, в котором участвует весьма специфичное целевое приложение, возможно то, которое вы сами и создали. Однако, если только вы не оповестили всех разработчиков о своем формате данных (и если только у вас нет бюджета для этого!), есть вероятность, что другие целевые приложения не будут иметь никакого понятия о ваших форматах данных.

К счастью, имеется растущее количество соглашений, касающихся пользовательских форматов данных, которые поддерживает http://schema.org/ . Этот сайт является местом, где хранятся соглашения по пользовательским форматам, таким образом, мы настоятельно рекомендуем, чтобы вы брали форматы оттуда. Их полный список можно найти http://schema.org/docs/schemas.html .

Вот JSON-данные в формате book, использованные в примере:

var book = {
type: "http://schema.org/Book", properties: {
image: "http://sourceuri.com/catcher-in-the-rye-book-cover.jpg", name: "The Catcher in the Rye",
bookFormat: "http://schema.org/Paperback",
author: "http://sourceuri.com/author/jd_salinger.html", numberOfPages: 224,
publisher: "Little, Brown, and Company", datePublished: "1991-05-01",
inLanguage: "English", isbn: "0316769487"
}
};
    

Вы можете легко выразить те же данные в виде обычного текста, HTML (или RTF), в виде ссылки (возможно, на страницу с этой информацией), и изображения (обложка книги). Так вы можете заполнить пакет данных полностью стандартными форматами вместе со специфическими пользовательскими форматами.

Отложенные операции и отложенный рендеринг

Откладывание, как упомянуто ранее, это простой механизм для задержки завершения события datarequested до того момента, когда метод complete отложенной операции будет вызван внутри обработчика завершения асинхронной операции. Документация по DataRequested.getDeferral (http://msdn.microsoft.com/library/windows/apps/windows.applicationmodel.datatransfer.datarequest.getdeferral.aspx) показывает пример использования этого механизма при загрузке файла изображения:

var deferral = request.getDeferral();

Windows.ApplicationModel.Package.current.installedLocation.getFileAsync( "images\\smalllogo.png")
.then(function (thumbnailFile) {
request.data.properties.thumbnail = Windows.Storage.Streams.
RandomAccessStreamReference.createFromFile(thumbnailFile);
return Windows.ApplicationModel.Package.current.installedLocation.getFileAsync( "images\\logo.png");
})
.done(function (imageFile) {
request.data.setBitmap( Windows.Storage.Streams.RandomAccessStreamReference.createFromFile(imageFile));
deferral.complete();
});
    

Отложенный рендеринг – это другое дело, хотя процесс обычно использует откладывание. Его цель зкалючается в предотвращении рендеринга данных общего доступа до тех пор, пока целевое приложение не получит их, иногда это называют операциями отложенного общего доступа (pull operations). Методы set*, которые мы видели, выполняют копирование полных данных в пакет. Отложенный рендеринг (обработка) подразумевает вызов метода пакета данных setDataProvider (http://msdn.microsoft.com/library/windows/apps/windows.applicationmodel.datatransfer.datapackage.setdataprovider.aspx) с идентификатором формата данных и функцией, которую нужно вызвать, когда понадобятся данные. Вот как это реализовано в Сценарии 5 примера приложения-источника, где imageFile выбирают с помощью средства выбора файлов (js/image.js):

// Обеспечивая общий доступ к изображению, не забудьте установить эскиз для DataPackage
var streamReference =
Windows.Storage.Streams.RandomAccessStreamReference.createFromFile(imageFile);
request.data.properties.thumbnail = streamReference;
request.data.setDataProvider(
Windows.ApplicationModel.DataTransfer.StandardDataFormats.bitmap,
onDeferredImageRequested);
    

Как показано в комментариях, хорошо будет предоставить экскиз при использовании операции отложенного рендеринга, таким образом, у целевого приложения будет что показать пользователю. Таким образом, когда целевому приложению понадобятся полные данные, будет вызвана функция провайдера данных, в данном случае – onDeferredImageRequsted:

function onDeferredImageRequested(request) {
if (imageFile) {
// Здесь мы предоставляем обновленные данные изображения с использованием отложенного рендендеринга
var deferral = request.getDeferral();

var imageDecoder, inMemoryStream;

imageFile.openAsync(Windows.Storage.FileAccessMode.read).then(function (stream) {
// Декодируем изображение
return Windows.Graphics.Imaging.BitmapDecoder.createAsync(stream);
}).then(function (decoder) {
// Перекодируем изображение до 50% ширины и высоты
inMemoryStream = new Windows.Storage.Streams.InMemoryRandomAccessStream();
imageDecoder = decoder;
return Windows.Graphics.Imaging.BitmapEncoder.createForTranscodingAsync(
inMemoryStream, decoder);
}).then(function (encoder) {
encoder.bitmapTransform.scaledWidth = imageDecoder.orientedPixelWidth * 0.5;
encoder.bitmapTransform.scaledHeight = imageDecoder.orientedPixelHeight * 0.5;
return encoder.flushAsync();
}).done(function () {
var streamReference = Windows.Storage.Streams.RandomAccessStreamReference
.createFromStream(inMemoryStream);
request.setData(streamReference);
deferral.complete();
}, function (e) {
// операция не выполнена, но нам нужно выйти из отложенной операции deferral для того
//чтобы целевое приложение не зависло
deferral.complete();
});
}
}
    

Обатите внимание на то, что эта функция принимает упрощенный гибрид объектов DataRequest и DataPackage: DataProviderRequest (http://msdn.microsoft.com/library/windows/apps/windows.applicationmodel.datatransfer.dataproviderrequest.aspx), который содержит свойства deadline и formatId, метод getDeferral и метод setData, посредством которого вы предоставляете данные, которые соответствуют formatId. Свойство deadline, как вы можете догадаться, это то же свойство, что обработчик datarequested может сохранить в объекте DataRequest.

Целевые приложения

Возвращаясь к рис. 1.1, мы можем видеть, что хотя взаимодействие между приложением-источником и брокером общего доступа к данным управляется единственным событием datarequested, взаимодействие между брокером и целевым приложением устроено немного сложнее. С одной стороны, брокер должен определить, какое приложение потенциально может обработать конкретный пакет данных, для чего каждое целевое приложение содержит соответствующие детали в своем манифесте. Когда приложение выбрано, оно запускается с видом активации shareTarget, в ответ на это оно должно показать специальный пользовательский интерфейс общего доступа, вместо полного интерфейса приложения.

Посмотрим, как все это работает с помощью примера "Приложение-цель для общего доступа" (http://code.msdn.microsoft.com/windowsapps/Sharing-Content-Target-App-e2689782), вид которого показан на рис. 1.2. Убедитесь в том, что сначала запустили приложение в Visual Studio, чтобы оно было установлено и появилось бы в списке приложений, когда мы активируем чудо-кнопку Общий доступ.

Внешний вид примера целевого приложения

Рис. 1.2. Внешний вид примера целевого приложения

Первый шаг целевого приложения заключается в объявлении форматов данных, которые оно может принимать, в разделе Объявления (Declarations) манифеста, вместе со страницей, которая будет открыта, когда предложение будет выбрано в качестве целевого. Как показано на рис. 1.3., в примере целевого приложения указано, что приложение может работать с текстом, URI, изображениями, HTML и форматом http://schema.org/Book , так же заявлено о том, что оно может обрабатывать любые файлы, которые могут содержаться в пакете данных (вы можете задать здесь конкретные типы файлов). В нижней части панели указано, что страница приложения target.html будет использоваться в качестве страницы, выводимой на панель Общего доступа.

Объявления в манифесте приложения из примера целевого приложения

увеличить изображение
Рис. 1.3. Объявления в манифесте приложения из примера целевого приложения

Начальная страница для общего доступа – это обычная HTML-страница с любым необходимым вам макетом для выполнения задачи общего доступа. Эта страница обычно функционирует независимо от основного приложения. Когда приложение выбирают в качестве цели для общего доступа, эта страница загружается и активируется самостоятельно и, таким образом, имеет полностью собственный скриптовый контекст. На этой странице не следует размещать навигационные элементы, ведущие к другим частям приложения, и она должа содержать лишь код, который необходим для задач общего доступа к данным (Параметры Исполняемый файл (Executable) и Точка входа (Entry point) не используются приложениями, написанными на HTML и JavaScript, они существуют для приложений, написанных на других языках).

Большая часть ее структуры может быть создана автоматически с использованием Шаблона элемента контракта получателя данных (Share Target Contract item template), который предоставляется Visual Studio и Blend, как показано на рис. 1.4. Диалоговое окно появляется, после того, как вы щелкнете правой кнопкой мыши по проекту и выберете команду Добавить > Создать элемент (Add > New Item), или воспользуетесь командой меню Проект > Создать новый элемент (Project > Add New Item).

Шаблона элемента контракта получателя данных в Visual Studio и Blend

Рис. 1.4. Шаблона элемента контракта получателя данных в Visual Studio и Blend

Данный шаблон элемента предоставит вам HTML, JS и CSS-файлы для создания страницы целевого приложения и добавит эту страницу в объявление манифеста вместе с указанием поддержки текстового формата и формата URI. Вам лишь останется соответствующим образом отредактировать эти данные.

Прежде чем мы рассмотрим код, вот несколько замечаний о дизайне страницы для приложения-получателя данных, они представляют собой краткое изложение материала "Руководство по общему доступу к содержимому" ( http://msdn.microsoft.com/library/windows/apps/hh465251.aspx):

  • Сохраняйте индивидуальность приложения, его внешний вид, и удобство работы с ним, на уровне основного приложения.
  • Обеспечьте простоту взаимодействий для быстрого выполнения задач по общему доступу. Избегайте форматирования текста, пометок людей на фотографиях или задач по установке, но предоставьте возможности по редактированию содержимого, особенно, если отправляете данные в социальные сети или отправляете сообщение. (Посмотрите на рис. 12.5., где в качестве примера приведено приложение Mail (Почта)). Целевые приложения социальных сетей обычно подразумевают возможность комментирования, в приложения-получателя фотографий, возможно, есть смысл включить функцию добавления подписи к фотографии.
  • Сведите к минимуму навигацию: задача общего доступа предусматривает собственную рабочую среду, поэтому используйте встроенные элементы управления и встроенные сообщения об ошибках вместо переключения на другие страницы. Другая причина избегать этого заключается в том, что страница обеспечения функциональности общего доступа исполняется в собственном скриптовом контексте, поэтому возможность перехода в приложение, в другой контекст, может смутить пользователя.
  • Избегайте использования ссылок, которые могут отвлечь пользователя от задачи общего доступа или увести его в другое место. Помните, что общий доступ – это способ упростить процесс получения данных из одних приложений в другие, который традиционно является достаточно утомительным. Поэтому сконцентрируйте все усилия интерфейса общего доступа целевого приложения на этой задаче.
  • Избегайте использования автоматического скрытия неактивных всплывающих элементов, так как интерфейс чудо-кнопки Общий доступ уже обладает подобной функциональностью.
  • Подтверждайте действия пользователя, когда вы начинаете отправлять данные (на сетевой сервис, например), чтобы пользователь знал, что что-то действительно происходит.
  • Расположите важные кнопки в пределах досягаемости больших пальцев. Обратитесь к материалу "Состояние сенсорного ввода Windows 8" (http://msdn.microsoft.com/library/windows/apps/hh465415.aspx#touch_posture) для того, чтобы прочесть руководство по размещению элементов.
  • Постарайтесь сделать то, как выглядят элементы в режиме предварительного просмотра, соответствующим их реальному внешнему виду. Другими словами, не показывайте пользователю фокусы!

В случае с дизайном этой страницы, полезно знать, что вам не нужно беспокоиться о различных режимах просмотра – страница существует лишь в одном состоянии (как всплывающий элемент, она не может отображаться в прикрепленном режиме). Однако, ей нужно подстраиваться под различные размеры экрана, помните об этом, но не под различные режимы просмотра. Здесь хорошо подойдет макет, основанный на CSS-сетке с дробными размерами строк и столбцов.

Предупреждение. Так как целевое приложение может принимать данные от любого приложения-источника, ему следует расценивать такое содержимое как недоверенное и потенциально опасное, особенно когда речь идет об HTML, URI и файлах. Целевое приложение должно избегать добавления подобного HTML или содержимого файлов в DOM, исполнять код из URI, переходить по URI или к каким-то другим страницам на основе URI, модифицировать записи базы данных, использовать eval с данными и так далее.

Интерфейс общего доступа в приложении Windows Mail (Почта Windows) (нижняя пустая часть была обрезана). Этот пользовательский интерфейс позволяет править адрес получателя, тему и тело сообщения и отправлять изображение в виде вложения или ссылки на файл в SkyDrive

Рис. 1.5. Интерфейс общего доступа в приложении Windows Mail (Почта Windows) (нижняя пустая часть была обрезана). Этот пользовательский интерфейс позволяет править адрес получателя, тему и тело сообщения и отправлять изображение в виде вложения или ссылки на файл в SkyDrive

Подсмотрим на полное содержимое JavaScript-файла шаблона, так как это показывает нам основные механизмы целевого приложения. Во-первых, как вы можете видеть, у нас есть те же структуры, что и в обычном default.js приложения, мы используем методы и события объекта WinJS.Application.

(function () { "use strict";

var app = WinJS.Application;
var share;

function onShareSubmit() {
document.querySelector(".progressindicators").style.visibility = "visible";
document.querySelector(".commentbox").disabled = true;
document.querySelector(".submitbutton").disabled = true;

// TODO: Выполните какие-либо действия с данными общего доступа из переменной 'share'.

share.reportCompleted();
}

// Эта функция ответственна за различные виды активации приложения.
app.onactivated = function (args) {
var thumbnail;

if (args.detail.kind ===
Windows.ApplicationModel.Activation.ActivationKind.shareTarget) {
document.querySelector(".submitbutton").onclick = onShareSubmit;
share = args.detail.shareOperation;

document.querySelector(".shared-title").textContent =
share.data.properties.title;
document.querySelector(".shared-description").textContent =
share.data.properties.description;

thumbnail = share.data.properties.thumbnail;
if (thumbnail) {
// Если данные общего доступа содержат эскиз, отобразим его.
args.setPromise(thumbnail.openReadAsync().then(
function displayThumbnail(stream) {
document.querySelector(".shared-thumbnail").src =
window.URL.createObjectURL(stream, { oneTimeOnly: true });
}));
} else {
// Если эскиз не представлен, развернем элементы описания и
// заголовка для того, чтобы заполнить неиспользуемое пространство.
document.querySelector("section[role=main] header").style
.setProperty("-ms-grid-columns", "0px 0px 1fr");
document.querySelector(".shared-thumbnail").style.visibility = "hidden";
}
}
};

app.start();
})();
    

Когда загружается и активируется эта страница, в течение этого времени отображается экран-заставка приложения, выполняется ее событие WinJS.Application.onactivated – снова, независимо от обработчика activated основного приложения, который обычно находится в default.js. Так как мы работаем с целевым приложением для общего доступа к данным, мы проверяем, является ли тип активации shareTarget, после чего ваша основная задача – предоставить предварительный просмотр данных, которые поступают в приложение вместе с любым пользовательским интерфейсом, который предусмотрен для редактирования данных, комментирования и так далее. Обычно здесь так же имеется кнопка для завершения операции или передачи данных, нажатие на которую сообщает брокеру общего доступа, что операция завершена.

Ключевым моментом здесь является объект args.detail.shareOperation, предоставленный обработчику activated. Это объект типа Windows.ApplicationModel.DataTransfer.ShareTarget.ShareOperation ( http://msdn.microsoft.com/library/windows/apps/windows.applicationmodel.datatransfer.sharetarget.shareoperation.aspx), свойство data которого содержит пакет данных только для чтения, который называется DataPackageView (http://msdn.microsoft.com/library/windows/apps/windows.applicationmodel.datatransfer.datapackageview.aspx), откуда вы получаете все, что вам нужно:

  • Для того чтобы проверить, содержатся ли в пакете данные в форматах, которые может принять ваше приложение, используйте метод contains или коллекцию availableFormats.
  • Для того, чтобы получить данные из пакета, используйте его методы get*, такие, как getTextAsync, getBitmapAsync, и getDataAsync (для пользовательских форматов). При вставке HTML вы так же можете использовать метод getResourceMapAsync для получения URI связанных ресурсов. Свойства (properties), имеющие отношение к отображению, такие как thumbnail, так же полезны для предоставления возможности предварительного просмотра данных.

Как видите, код из шаблона элемента контракта получателя данных, приведенный выше, ничего не делает с полученными данными, лишь отображает заголовок, описание и эскиз. Очевидно, ваше приложение будет делать нечто большее, чем просто запрашивать данные из пакета, как этот пример. Его файл js/target.js содержит обработчик activated для страницы target.html (в корне проекта) и она так же отображает эскиз из пакета данных по умолчанию. Затем они просматривает пакет на предмет различных форматов данных и выводит это содержимое, если оно присутствует:

if (shareOperation.data.contains(Windows.ApplicationModel.DataTransfer.StandardDataFormats.
text)) {
shareOperation.data.getTextAsync().done(function (text) {
displayContent("Text: ", text, false);
});
}
Похожий код появляется и для простых форматов. Прием данных изображения требует немного больше работы, но он весьма прост:
if (shareOperation.data.contains(Windows.ApplicationModel.DataTransfer.StandardDataFormats.
bitmap)) {
shareOperation.data.getBitmapAsync().done(function (bitmapStreamReference) {
bitmapStreamReference.openReadAsync().done(function (bitmapStream) {
if (bitmapStream) {
var blob = MSApp.createBlobFromRandomAccessStream(bitmapStream.contentType, bitmapStream);
document.getElementById("imageHolder").src = URL.createObjectURL(blob,
{ oneTimeOnly: true });
document.getElementById("imageArea").className = "unhidden";

}
});
});
}
    

Для обработки HTML, он просматривает разметку на предмет поиска элементов img, затем устанавливает их атрибуты src в соответствии с картой ресурсов (iframe к этому моменту уже содержит HTML-содержимое из пакета)

var images = iFrame.contentDocument.documentElement.getElementsByTagName("img");
if (images.length > 0) {
shareOperation.data.getResourceMapAsync().done(function (resourceMap) {
if (resourceMap.size > 0) {
for (var i = 0, len = images.length; i < len; i++) {
var streamReference = resourceMap[images[i].getAttribute("src")];
if (streamReference) {
// Вызов вспомогательной функции для установки src элементов изображений
// на соответствующие URL больших двоичных объектов, созданных из streamReference 
setResourceMapURL(streamReference, images[i]);
}
}
}
});
}

Вспомогательная функция setResourceMapURL выполняет довольно много операций, специфичных для обработки изображений. В частности, в ней вызывается openReadAsync для потока, осуществляется вызов MSApp.createBlobFromRandomAccessStream, полученный большой двоичный объект передается URL.createObjectURL, результат записывается в img.src, и поток закрывается.

После того, как целевое приложение завершило операцию получения данных, оно должно вызвать метод ShareOperation.reportCompleted, как показано ранее в коде шаблона. Это позволяет системе узнать, что пакет данных принят, набор операций по предоставлению общего доступа к данным завершен, и все связанные с этим ресурсы можно освободить. Целевое приложение в примере выполняет это, когда вы нажмете на кнопку, предназначенную для этой цели, но обычно вы автоматически вызываете метод, когда вы завершаете операцию общего доступа. Хочу предупредить вас, что вызов reportCompleted закроет интерфейс работы с общими данными целевого приложения, поэтому старайтесь не вызывать его как только целевое приложение активировалось: желательно, чтобы пользователь чувствовал уверенность в том, что операция была проведена.

Лекция 1: 1234 || Лекция 2 >