company_banner

Интеграция с хабом «Фотографии» на Windows Phone

    Сегодня я хотел бы затронуть одну тему, которая относится к сфере максимального использования возможностей мобильной платформы. В частности, речь пойдёт о том, как интегрировать в хаб «Фотографии» приложение, работающей с изображениями. Как вы, наверное, знаете, эта возможность появилась ещё в Windows Phone 7 и успешно перекочевала в восьмую версию ОС.

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

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

    1. Регистрация приложения-«приемщика»

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

    <Extension ExtensionName="Photos_Extra_Hub" 
               ConsumerID="{5B04B775-356B-4AA0-AAF8-6491FFEA5632}" 
               TaskID="_default" />


    Для демонстрации создадим приложение (в моем случае это будет WPShareDemo) и открываем файл WMAppManifest.xml через xml-редактор. Для этого в контекстом меню выбираем пункт XML (Text) editor:



    Далее нужна небольшая хитрость. В первую очередь необходимо прописать расширение внутри тега Deployment\App, и так как в манифесте строго прописана последовательность тегов, то добавить это расширение следует только после тега <Tokens />.
    Это расширение, как и любые другие, должно быть прописано внутри тега .

    <Extensions>
          <Extension ExtensionName="Photos_Extra_Share" ConsumerID="{5B04B775-356B-4AA0-AAF8-6491FFEA5632}" TaskID="_default" />
        </Extensions>


    Далее у нас есть небольшое различие между WP7 и WP8.
    В разделе Capabilities необходимо прописать следующий тег для WP7:

    />

    И для WP8:

    />

    Их также можно проставить галочками в графическом интерфейсе, которая открывается двойным щелчком по файлу манифеста.
    В итоге у вас должен получиться примерно такой код манифеста:

    спойлер
    <?xml version="1.0" encoding="utf-8"?>
    <Deployment xmlns="http://schemas.microsoft.com/windowsphone/2012/deployment" AppPlatformVersion="8.0">
      <DefaultLanguage xmlns="" code="en-US" />
      <App xmlns="" ProductID="{dcef4a48-7464-43cf-a4f7-157debfe6447}" Title="WPShareDemo" RuntimeType="Silverlight" Version="1.0.0.0" Genre="apps.normal" Author="WPShareDemo author" Description="Sample description" Publisher="WPShareDemo" PublisherID="{7f1b10ab-ad1e-4d51-9c30-5b893453ace7}">
        <IconPath IsRelative="true" IsResource="false">Assets\ApplicationIcon.png</IconPath>
        <Capabilities>
          <Capability Name="ID_CAP_NETWORKING" />
          <Capability Name="ID_CAP_MEDIALIB_AUDIO" />
          <Capability Name="ID_CAP_MEDIALIB_PLAYBACK" />
          <Capability Name="ID_CAP_SENSORS" />
          <Capability Name="ID_CAP_WEBBROWSERCOMPONENT" />
          <Capability Name="ID_CAP_MEDIALIB_PHOTO" />
        </Capabilities>
        <Tasks>
          <DefaultTask Name="_default" NavigationPage="MainPage.xaml" />
        </Tasks>
        <Tokens>
          <PrimaryToken TokenID="WPShareDemoToken" TaskName="_default">
            <TemplateFlip>
              <SmallImageURI IsRelative="true" IsResource="false">Assets\Tiles\FlipCycleTileSmall.png</SmallImageURI>
              <Count>0</Count>
              <BackgroundImageURI IsRelative="true" IsResource="false">Assets\Tiles\FlipCycleTileMedium.png</BackgroundImageURI>
              <Title>WPShareDemo</Title>
              <BackContent>
              </BackContent>
              <BackBackgroundImageURI>
              </BackBackgroundImageURI>
              <BackTitle>
              </BackTitle>
              <DeviceLockImageURI>
              </DeviceLockImageURI>
              <HasLarge>
              </HasLarge>
            </TemplateFlip>
          </PrimaryToken>
        </Tokens>
        <Extensions>
          <Extension ExtensionName="Photos_Extra_Share" ConsumerID="{5B04B775-356B-4AA0-AAF8-6491FFEA5632}" TaskID="_default" />
        </Extensions>
        <ScreenResolutions>
          <ScreenResolution Name="ID_RESOLUTION_WVGA" />
          <ScreenResolution Name="ID_RESOLUTION_WXGA" />
          <ScreenResolution Name="ID_RESOLUTION_HD720P" />
        </ScreenResolutions>
      </App>
    </Deployment>


    Теперь, если мы откроем фотографию и выберем пункт Отправить/Share, то можем увидеть наше приложение в списке тех, кто может «принять» отправляемую фотографию.

    спойлер



    2. Прием фотографии

    Теперь наше приложение будет запущено как обычно, с указанной стартовой страницы в манифесте (по умолчанию MainPage.xaml), но с дополнительными параметрами вида:

    /MainPage.xaml?Action=ShareContent&FileId=%7B5870C5AE-FF18-4ABC-996F-D6CE3F773F56%7D

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

    Для этого добавим в проект новую страницу PhotoShare.xaml, которая будет «принимать» запуск приложения с расшариваемой фотографией. Далее добавляем класс с именем CustomUriMapper и пишем для него следующий код:

    using System;
    using System.Windows.Navigation;
    
    namespace WPShareDemo
    {
        class CustomUriMapper : UriMapperBase
        {
            public override Uri MapUri(Uri uri)
            {
                string tempUri = uri.ToString();
                string mappedUri;
    
                // Launch from the photo share picker.
                // Incoming URI example: /MainPage.xaml?Action=ShareContent&FileId=%7BA3D54E2D-7977-4E2B-B92D-3EB126E5D168%7D
                if ((tempUri.Contains("ShareContent")) && (tempUri.Contains("FileId")))
                {
                    // Redirect to PhotoShare.xaml.
                    mappedUri = tempUri.Replace("MainPage", "PhotoShare");
                    return new Uri(mappedUri, UriKind.Relative);
                }
    
                // Otherwise perform normal launch.
                return uri;
            }
        }
    }


    Как несложно понять, если в Uri содержатся ключевые слова ShareContent и FileId, то в адресной строке MainPage будет заменен на PhotoShare. Для регистрации маппера необходимо в app.xaml.cs в методе InitializePhoneApplication прописать следующую строчку:

    RootFrame.UriMapper = new CustomUriMapper();

    Полный код метода InitializePhoneApplication:

    спойлер
    private void InitializePhoneApplication()
            {
                if (phoneApplicationInitialized)
                    return;
    
                // Create the frame but don't set it as RootVisual yet; this allows the splash
                // screen to remain active until the application is ready to render.
                RootFrame = new PhoneApplicationFrame();
                RootFrame.Navigated += CompleteInitializePhoneApplication;
    
                // Handle navigation failures
                RootFrame.NavigationFailed += RootFrame_NavigationFailed;
    
                // Handle reset requests for clearing the backstack
                RootFrame.Navigated += CheckForResetNavigation;
    
                RootFrame.UriMapper = new CustomUriMapper();
                // Ensure we don't initialize again
                phoneApplicationInitialized = true;
            }



    3. Чтение фотографии
    После того, как определились, на какой странице будем принимать фотографию (в нашем случае PhotoShare.xaml), мы можем уже прочитать полученное изображение в нашем коде. В данном случае мы просто отобразим фотографию.

    Для этого добавим на нашу страницу в XAML элемент Image:

    <Image x:Name="ShareImage">

    И в коде страницы добавим метод OnNavigateTo:

    protected override void OnNavigatedTo(NavigationEventArgs e)
            {
                if (NavigationContext.QueryString.ContainsKey("FileId"))
                {
                    var library = new MediaLibrary();
                    var photoFromLibrary = library.GetPictureFromToken(NavigationContext.QueryString["FileId"]);
    
                    var bitmapFromPhoto = new BitmapImage();
                    bitmapFromPhoto.SetSource(photoFromLibrary.GetPreviewImage());
                    ShareImage.Source = bitmapFromPhoto;
                }
            }


    В этом участке кода ключевыми методами являются:

    library.GetPictureFromToken
    и
    photoFromLibrary.GetPreviewImage()

    Метод GetPreviewImage() возвращает картинку, оптимизированную для полноэкранного отображения на экране Windows Phone. В том случае, если мы захотим получить картинку в оригинальном размере, необходимо воспользоваться методом GetImage():

    protected override void OnNavigatedTo(NavigationEventArgs e)
            {
                if (NavigationContext.QueryString.ContainsKey("FileId"))
                {
                    var library = new MediaLibrary();
                    var photoFromLibrary = library.GetPictureFromToken(NavigationContext.QueryString["FileId"]);
    
                    var bitmapFromPhoto = new BitmapImage();
                    bitmapFromPhoto.SetSource(photoFromLibrary.GetImage());
                    ShareImage.Source = bitmapFromPhoto;
                }
            }


    Однако в этом случае помните, что объем потребляемой памяти может существенно увеличиться, и если вы до этого «скушали» своим приложением большую часть памяти, то ваше приложение может просто вылететь при расшаривании картинки большого объема.
    В конечном итоге, если все было сделано правильно, то, «расшарив» какую либо фотографию в нашем приложении, мы должны увидеть нечто вроде этого:



    4. Баг платформы

    Как я уже говорил, возможность расшаривания фотографий появилась еще в Windows Phone 7. А в WP8 появилась такая замечательная фича, как Fast app resume.

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

    К сожалению, на данный момент у меня нет решения этой проблемы. Единственный способ обхода этого препятствия, что я нашёл – отказаться от Fast app resume, если необходимо использовать возможность отправки фотографий в приложении.

    Обновлено
    Если телефон пользователя получил последние обновления платформы (а именно WP8 Update 3) то этой баги у пользователя не будет наблюдаться.
    • +40
    • 6,8k
    • 7
    Mail.Ru Group 813,06
    Строим Интернет
    Поделиться публикацией
    Комментарии 7
    • 0
      А можно ли интегрироваться с хабом сообщений? Мне в Windows Phone это больше всего понравилось — все сообщения от одного человека в одном месте. Но передумал покупать из-за невозможности добавить в WP7 свои чатики.
      • 0
        думаю, что если бы это было бы возможно, то всякие вконтактики итд делали бы так. тоже очень хочется такого, очень удобно было бы.
        • +1
          Это же сильная сторона Майкрософта — интеграция всего со всем, они же придумали все эти буквосочетания типа OLE, DDE, COM, DCOM etc.
          • +1
            Да но с WP открывают возможности с осторожностью, возможно черезмерной осторожностью. Я сейчас изучаю WP8.1 и возможно в нем увидим возможность интеграции с сообщениями.
            • 0
              на сколько я понимаю, это могут лишь аккаунты, которые могут создавать лишь партнеры MS

              Я до сих пор жду нормальной интеграции Skype в Messges
      • 0
        1. Утечка памяти при использовании BitmapImage и/или элемента управления Image довольно известная проблема, решения описаны в куче статей.
        2. Вы хотите сказать что если стороннее приложение через MediaShareTask расшаривает фотку в ваше приложение, которое уже работает, то параметры не приходят?
        • 0
          1. Полностью с вами согласен — однако в статье нужен был минимум кода — изначально был вариант с сохранением в IsolatedStorage и заливкой полученной картинки в облако. Решил убрать все это и показать пример попроще с минимумом кода, так как на мой взгляд все это слишком «засоряет» тему.
          2. MediaShareTask все таки нужен совсем для другого сценария. Т.е. он для кейса когда мы сидим в приложении и решили отправить фотографию и мы можем добавить какую нибудь кнопку в UI приложения который предоставит возможность получить изображение. Но очень часто (по крайне мере у меня) сценарий с точностью до наоборот: Увидел какой то интересный момент из жизни — достал телефон и решил сфотографировать и уже потом хочется поделиться полученной фотографией, если нам самим эта фотография понравилась — собственно для этого и нужна интеграция в хаб с фотографиями — что бы пользователь мог быстро поделиться полученной фоткой через установленные приложения, например клиенты соц. сетей.

        Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

        Самое читаемое
        Интересные публикации