Блог Сергея Байдачного

Мой блог о технологиях

Ассоциация с файлами и «глубокие» ссылки

2 комментария

 

Немного о классе Launcher

Разрабатывая приложения для Windows Phone и Windows 8, все время сталкиваюсь с необходимостью использовать класс Launcher. Ведь с помощью этого класса можно вызвать браузер, передав ему ссылку на страницу для загрузки. Таким образом, будь то «Политика конфиденциальности» или ссылка на детали о событии, класс Launcher всегда поможет. Вот простой код, который позволяет запустить браузер из кода:

 

private async void LauchSomething()
{
    bool b = await Launcher.LaunchUriAsync(new Uri("http://kp.ua"));
}

 

Обратите внимание на то, что тут используется асинхронный метод, поддерживающий новый, более простой сценарий вызова через async/await. Метод LaunchUriAsync класса Launcher возвращает значение типа bool, которое сигнализирует об успешности вызова браузера.

Но, если копнуть глубже, то можно обнаружить, что метод LaunchUriAsync никак не связан с браузером. Его задача отыскать приложение, которое поддерживает протокол, описываемый в Uri. В данном случае протоколом является http и приложение, которое с ним ассоциировано, это Internet Explorer. Следовательно, для другого протокола может быть и другое приложение.

В Windows Phone 8 описано несколько протоколов, которые позволяют вызывать разработчику какие-то встроенные приложения. Например, метод ниже вызовет приложение по отправке почты, и в поле TO установит указанный адрес:

 

private async void LauchSomething()
{
    bool b = await Launcher.LaunchUriAsync(
        new Uri("mailto:sbaydach@microsoft.com"));
}

 

Полный список доступных протоколов можно посмотреть тут: http://msdn.microsoft.com/en-us/library/windowsphone/develop/jj662937(v=vs.105).aspx.

Попробуйте указать в качестве адреса что-то типа такого:

 

bool b = await Launcher.LaunchUriAsync(new Uri("myprotocol:Hello"));

 

В результате запуска на экране появится вот такое сообщение:

image

Это означает, что метод не нашел ни одного приложения, которое ассоциировано с данным протоколом и предлагает поискать такие приложения в магазине.

При попытке найти приложение в магазине, пользователь получает не весь список, а только те приложения, которые задекларировали требуемый протокол. Если же на телефоне у пользователя уже стоит несколько приложений с требуемым протоколом, то у него появляется список доступных приложений и пользователь должен выбрать одно из них. Возможности задать приложение по умолчанию для того или иного протокола в Windows Phone 8 нет.

Из вышесказанного можно сделать вывод о том, что разработчик может ассоциировать свое приложение с придуманным им протоколом, чтобы позволить запускать свое приложение извне, используя своеобразный механизм «глубоких» ссылок (deep links). Такая возможность есть, но при одном важном ограничении: нельзя ассоциировать приложение с одним из зарезервированных протоколов. Например, http входит в число зарезервированных протоколов. Список зарезервированных слов можно посмотреть тут: http://msdn.microsoft.com/en-us/library/windowsphone/develop/jj207065(v=vs.105).aspx.

Обратите внимание на то, что у класса Launcher есть и второй метод LaunchFileAsync. Если при запуске приложения через «глубокую» ссылку можно передать лишь текст в качестве параметра, то в случае LaunchFileAsync передается файл. При этом основным критерием выбора приложения является расширение файла. Иными словами, в качестве параметра, методу достаточно передать ссылку на объект типа StorageFile:

 

private async void LauchSomething()
{
    StorageFolder local = ApplicationData.Current.LocalFolder;

    StorageFile bqfile = await local.GetFileAsync("myfile.myex");

    bool b=await Launcher.LaunchFileAsync(bqfile);
}

StorageFile и StorageFolder представляют собой два новых класса в Windows Phone 8, которые позволяют работать с локальным хранилищем приложения как с файловой системой. В результате, в новой версии API появилось множество различных методов, которые работают с объектами этих классов.

Как и для ссылок, при создании ассоциаций приложения с файлами, нельзя использовать зарезервированные расширения файлов.

Наконец, чтобы посмотреть примеры уже работающих ассоциаций, можно воспользоваться почтовым клиентом или браузером. Например, если использовать телефон для открытия вложений с неподдерживаемыми по умолчанию форматами (например, PDF), или при попытке открыть ссылку при чтении страницы в Internet Explorer, указывающую на какой-то документ, тут и срабатывают ассоциации. Иными словами, существует множество приложений, которые поддерживают класс Launcher, среди которых есть и стандартные.

Очень часто ассоциации имеет смысл применять, когда Вы разрабатываете клиент для нескольких платформ, оперирующий файлами определенного формата. В этом случае один пользователь может создать файл на Windows 8, а кто-то другой использует приложение на Windows Phone 8, чтобы посмотреть на содержимое.

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

Перейдем теперь к вопросу создания ассоциаций с файлами и «глубоких» ссылок.

Создание «глубоких» ссылок

Итак, чтобы зарегистрировать протокол, необходимо выполнить ряд действий. Самое первое и самое простое действие состоит в редактировании манифеста приложения. Несмотря на появившийся графический редактор манифеста, следует вызвать редактор кода и внести следующий элемент:

 

<Extensions>
  <Protocol Name="myprotocol" 
      NavUriFragment="encodedLaunchUri=%s" TaskID="_default"/>
</Extensions> 

 

При этом элемент Extensions должен следовать сразу за элементом Tokens. Тут, с помощью вложенных элементов Protocol, прописываются все протоколы, на которые реагирует приложение. Общее число протоколов не должно превышать десяти, но обычно хватает одного. В качестве атрибутов элементу Protocol указывают имя протокола, идентификатор задачи, где прописывается страница запуска приложения, а также фиксированную строку (ее нужно просто скопировать), которая задает формат запроса.

Описав протокол в манифесте, Вы тем самым декларируете доступ к Вашему приложению по ссылке, через указанный протокол. Остается лишь определить тот момент, когда приложение запускается именно через ссылку и выбрать все возможные параметры. Вот тут разработчика может подстерегать сложность. Дело в том, что когда приложение запускается из меню или через плитку, то в качестве имени начальной страницы передается та, что прописана в манифесте внутри элемента Task. Скорее всего, это MainPage.xaml. Но, если приложение запускается с помощью класса Launcher, то приложение получает в качестве точки входа специально подготовленный адрес. Например, для следующей команды Launcher:

 

await Launcher.LaunchUriAsync(new Uri("myprotocol:Message?Say=Hello"));

 

точка входа будет выглядит следующим образом:

 

/Protocol?encodedLaunchUri=myprotocol%3AMessage%3FSay%3DHello

 

Естественно, если приложение оставить без изменений, то оно будет вылетать при запуске через Launcher с сообщением о том, что xaml страница не найдена. Действительно, в адресе выше нет и намека на XAML файл. Поэтому, чтобы переадресовать приложение на нужную страницу, необходимо написать собственный класс, который будет разбирать адрес выше и формировать правильную точку входа. Для этого необходимо создать класс, который будет наследоваться от класса UriMapperBase и реализовывать метод MapUri:

 

class AssociationUriMapper : UriMapperBase
{
    private string tempUri;

    public override Uri MapUri(Uri uri)
    {
        tempUri = System.Net.HttpUtility.UrlDecode(uri.ToString());

        if (tempUri.Contains("myprotocol:Message?Say="))
        {
            int categoryIdIndex = tempUri.IndexOf("Say=") + 4;
            string message = tempUri.Substring(categoryIdIndex);

            return new Uri("/MainPage.xaml?Message=" + 
                message, UriKind.Relative);
        }

        return uri;
    }
}

 

Тут метод MapUri принимает адрес точки входа. Если это какой-то «нормальный» адрес, например, при навигации в приложение из меню или навигации между страницами, то мы ничего не делаем и возвращаем исходный адрес. А вот если в строке содержится наше сообщение, то мы выбираем значение параметра и формируем новый адрес для точки входа в приложение. В нашем случае навигацию перенаправляем на страницу MainPage.xaml, передавая ей параметр.

Осталось создать объект типа AssociationUriMapper и заменить им стандартный механизм. Это можно сделать в файле App.xaml.cs сразу после создания основного фрейма:

 

RootFrame = new PhoneApplicationFrame();
RootFrame.Navigated += CompleteInitializePhoneApplication;

RootFrame.UriMapper = new AssociationUriMapper();

 

Вот и все, механизм «глубоких» ссылок запущен и теперь приложение можно вызывать извне через зарегистрированный протокол.

Ассоциация приложения с файлом

При создании ассоциации с файлами, необходимо выполнить немного больше работы. Начнем с того, что если с каким-то типом файлов ассоциировано приложение, то существует несколько возможностей его запуска:

· Из интерфейса другого приложения через класс Launcher;

· Открыть файл как вложение письма в почтовом клиенте;

· Загрузить файл через браузер;

· При работе с хабом Office, где могут присутствовать файлы Вашего типа;

Если первый вариант предполагает, что разработчик сам определит механизм запуска приложения, ассоциированного с файлом в своем интерфейсе, то почтовый клиент, браузер и Office Hub имеют устоявшиеся интерфейсы и поведение. Так, при просмотре вложений в почтовом клиенте, рядом с именем файла отображается иконка. Аналогично, иконка отображается и в Office Hub и в браузере. Поэтому, при определении ассоциации в манифесте, следует указать не только разрешение, но и все возможные иконки. Вот пример описания манифеста:

 

<Extensions>
   <FileTypeAssociation Name="myas" TaskID="_default" 
       NavUriFragment="fileToken=%s">
       <Logos>
           <Logo Size="small" IsRelative="true">33x33.png</Logo>
           <Logo Size="medium" IsRelative="true">69x69.png</Logo>
           <Logo Size="large" IsRelative="true">176x176.png</Logo>
       </Logos>
       <SupportedFileTypes>
         <FileType ContentType="application/text">.myex1</FileType>
       </SupportedFileTypes>
   </FileTypeAssociation>
</Extensions>

 

Тут, как и в случае со ссылками, тут указывается имя ассоциации (но тут оно не особо на что-то влияет), идентификатор задачи, связанной с запуском приложения и NavUriFragment, который просто нужно скопировать. Внутри этого элемента указываются логотипы, которые будут использоваться в ситуациях, описанных выше. Если логотипы не указаны, то будет подставлена иконка по умолчанию. Стандартные приложения отображают иконку по умолчанию и тогда, когда с одним расширением файла связано более одного приложения (или нет приложения вовсе). После логотипов идет самый важный элемент всей этой конструкции, а именно, сами ассоциации с расширениями, которые задаются с помощью элементов FileType. Тут задается тип контента и само расширение. Якобы подобных расширений может быть до 20 штук для одного приложения, хотя это актуально только для «читалок» различных форматов документов.

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

 

/FileTypeAssociation?fileToken=89819279-4fe0-4531-9f12-d612f0323a20

 

Тут приложению передается уникальный токен файла, по которому можно получить и сам файл. Для этого в Windows Phone 8 есть класс SharedStorageAccessManager, который содержит всего два метода: GetSharedFileName и CopySharedFileAsync.

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

Итак, первым делом нужно определить расширение и выполнить правильную переадресацию. Для этого создаем класс, наследник от класса UriMapperBase, как это было со ссылками:

 

class AssociationFileMapper : UriMapperBase
{
 private string tempUri;

 public override Uri MapUri(Uri uri)
 {
     tempUri = uri.ToString();

     if (tempUri.Contains("/FileTypeAssociation"))
     {
         int fileIDIndex = tempUri.IndexOf("fileToken=") + 10;

         string fileID = tempUri.Substring(fileIDIndex);

         string incomingFileName =
             SharedStorageAccessManager.GetSharedFileName(fileID);

         string incomingFileType = Path.GetExtension(incomingFileName);

         if (incomingFileType.Equals(".myex1"))
         {
             return new Uri(
                 "/MainPage.xaml?fileToken=" + fileID, UriKind.Relative);
         }
     }

     return uri;
 }
}

 

Как и в случае со ссылками, устанавливаем свойство UriMapper фрейма в объект созданного класса. Далее, в коде страницы MainPage.xaml (в нашем случае) можно скопировать файл в локальное хранилище и приступить к работе с ним.

Реклама

Written by Sergiy Baydachnyy

07.12.2012 в 13:54

Опубликовано в Windows Phone

Tagged with

комментария 2

Subscribe to comments with RSS.

  1. If some one needs to be updated with newest technologies therefore he must be
    go to see this web page and be up to date daily.

  2. Есть ли возможность через LaunchUriAsync передавать http-запросы с нужными заголовками? Например, когда требуется аутентификация или указать user-agent?


Добавить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход / Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход / Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход / Изменить )

Google+ photo

Для комментария используется ваша учётная запись Google+. Выход / Изменить )

Connecting to %s

%d такие блоггеры, как: