Pull to refresh
0
Microsoft
Microsoft — мировой лидер в области ПО и ИТ-услуг

Обновления Live Tiles в фоновых агентах

Reading time 7 min
Views 4.6K

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

Одна из ключевых концепций – Live Tiles или «живые тайлы» — «иконки приложений на стероидах». Правильное использование Live Tiles привязывает пользователя к приложению и даёт совсем другие ощущения при использовании. Например, когда я выбирал приложение для прогноза погоды, ключевым критерием для меня являлось то, чтобы приложение имело Live Tile и оперативно обновляло прогноз. В результате, после настройки приложения и закрепления Live Tile на стартовом экране, я практически больше это приложение не запускал, но при этом, сразу же после снятия лок-скрина вижу оперативную информацию о погоде.

Хороший пример, подумаете вы, но как нам сделать так?


Приложение, которым я пользуюсь обновляет свои Live Tile (а их может быть несколько, т.к. я могу закрепить Live Tile для нескольких городов) используя периодического фонового агента.

Давайте на примере разберёмся, как реализовать обновление Live Tile из фонового агента.

Сначала я подготовим 2 фоновые картинки для тайлов. Я сделал следующие:

и

Это PNG файлы, размера 173х173 на прозрачном фоне, чтобы через него была видна подложка цвета темы, установленной сейчас на телефоне.

Потом создадим простой проект на основе Windows Phone Application на C#. Добавим подготовленные картинки в него. Я их добавил под простыми названиями 1.png и 2.png. В настройках нужно установить для них тип Content и условия копирования Copy if newer.

Перейдём непосредственно к разработке приложения.

У нас будет обновляться основной тайл приложения и один вторичный тайл.

Замечание: У приложения всегда есть тайл приложения, даже если он не закреплён на стартовом экране. Вторичных тайлов может быть несколько и их необходимо создавать из приложения.

Добавим вторую страницу в приложение и назовём её LandingPage.xaml — она будет открываться, когда будет нажиматься вторичный тайл нашего приложения.

Перейдём к стартовой странице MainPage.xaml приложения и добавим на неё кнопку:
<!--TitlePanel contains the name of the application and page title-->
<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
   <TextBlock x:Name="ApplicationTitle" Text="LIVE TILES UPDATE DEMO" Style="{StaticResource PhoneTextNormalStyle}"/>
   <TextBlock x:Name="PageTitle" Text="настройки" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
</StackPanel>

<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
   <StackPanel>
      <Button Height="100" Content="Добавить ещё тайл" Name="TileAdder" Click="TileAdder_Click"></Button>
    </StackPanel>            
</Grid>


В код добавим обработчик нажатия кнопки, в котором будем добавлять вторичный тайл с некой «демо-логикой»:
private void TileAdder_Click(object sender, RoutedEventArgs e)
{
    IsolatedStorageFile CounterStorage = IsolatedStorageFile.GetUserStoreForApplication();
    IsolatedStorageFileStream CounterStream = CounterStorage.OpenFile("counter", System.IO.FileMode.OpenOrCreate);
    int count = CounterStream.ReadByte();
    if (count < 0) count = 1;
            
            
    StandardTileData appTileData = new StandardTileData();

    if ((count % 2) == 0)
    {
        appTileData.Title = "Агент 002";
        appTileData.BackgroundImage = new Uri("/1.png", UriKind.RelativeOrAbsolute);
    }
    else
        if ((count % 7) == 0)
        {
            appTileData.Title = "Агент 007";
            appTileData.BackgroundImage = new Uri("", UriKind.RelativeOrAbsolute);
        }
        else
        {
            appTileData.Title = "Агент 000";
            appTileData.BackgroundImage = new Uri("/2.png", UriKind.RelativeOrAbsolute);
        }

    appTileData.Count = count;
            
    CounterStream.Seek(0, SeekOrigin.Begin);
    count = count + 5;
    if (count > 99) count = 99;
    CounterStream.WriteByte((byte)count);
    CounterStream.Close();

    ShellTile.Create(new Uri("/LandingPage.xaml", UriKind.RelativeOrAbsolute), appTileData);
}


Чтобы создать вторичный тайл, сначала мы создаём объект, содержащий данные тайла, затем вызываем метод, которому передаём URI страницы, которая будет вызываться и объект с данными — всё предельно просто.

Логика вариации содержимого вторичного тайла предназначена просто для того, чтобы при демонстрации приложения, тайл создавался с разными параметрами.

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

Поскольку, если свойство Count больше, чем 99, отображаться будет всё равно 99, мы считаем только до 99 и спокойно можем конвертировать из int в byte.

Теперь нам нужно создать фоновый агент, который бы обновлял данные на наших тайлах. Доавим в решение проект типа Windows Phone Scheduler Task Agent, после чего добавим в основной проект ссылку на него и добавим его namespace в блок using главной страницы основного приложения.

Также в блок using главной страницы осинового приложения надо не забыть добавить namespace для работы с Isolated Storage и агентами. Я назвал проект с агентом UpdateTileAgent и в моём случае этот блок выглядит следующим образом:
using UpdateTileAgent;

using Microsoft.Phone.Scheduler;

using System.IO.IsolatedStorage;
using System.IO;


Итак, нам нужно создать и зарегистрировать фоновый агент. Я буду это делать в обработчике события Loaded основной страницы. Для этого я добавлю этот обработчик, а в обработчик добавлю код создания агента:
const string UpdateTileAgentName = "Agent-Tile";
        
// Constructor
public MainPage()
{
    InitializeComponent();

    this.Loaded += new RoutedEventHandler(MainPage_Loaded);
}

void MainPage_Loaded(object sender, RoutedEventArgs e)
{
    PeriodicTask myPeriodicTask = ScheduledActionService.Find(UpdateTileAgentName) as PeriodicTask;

    if (myPeriodicTask != null)
    {
        try
        {
            ScheduledActionService.Remove(UpdateTileAgentName);
        }
        catch (Exception ex)
        {
            MessageBox.Show("Невозможно удалить ранее созданный сервис:" + ex.Message);
        }
    }

    myPeriodicTask = new PeriodicTask(UpdateTileAgentName);
    myPeriodicTask.Description = "Agent-Tile";

    try
    {
        ScheduledActionService.Add(myPeriodicTask);

#if DEBUG
        ScheduledActionService.LaunchForTest(UpdateTileAgentName, TimeSpan.FromSeconds(10));
#endif
    }
    catch (Exception ex)
    {
        MessageBox.Show("Невозможно создать сервис:" + ex.Message);
    }

    if (ShellTile.ActiveTiles.Count() > 1) TileAdder.IsEnabled = false;

}


В целом, код достаточно простой. Хочу обратить внимание на несколько особенностей.

Периодические агенты запускаются раз в полчаса. Для отладки это не очень удобно, поэтому я использую условную директиву компиляции, в которой в отладочной сборке прошу запустить агента через 10 секунд.
#if DEBUG
        ScheduledActionService.LaunchForTest(UpdateTileAgentName, TimeSpan.FromSeconds(10));
#endif


Я не хочу, чтобы можно было создать несколько вторичных тайлов со ссылкой на одну и ту же страницу. Поэтому, я проверяю количество тайлов и в зависимости от этого включаю/выключаю кнопку добавления тайлов.
if (ShellTile.ActiveTiles.Count() > 1) TileAdder.IsEnabled = false;

Поле Description агента является обязательным и без него он не добавится в планировщик.

Всё, с самим приложением мы закончили. Теперь можно приступать к агенту.

Основная работа в агенте ведётся в методе OnInvoke:
 protected override void OnInvoke(ScheduledTask task)


Для того, чтобы удобно работать с тайлами, не забудьте добавить необходимые директивы в using блок кода агента. У меня он выглядит следующим образом:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using Microsoft.Phone.Scheduler;

using Microsoft.Phone.Shell;
using System.IO.IsolatedStorage;
using System.IO;


Добавим в код агента обновление тайла исходя из «внешних» данных в виде счётчика и добавим немного разнообразия, используя логику, аналогичную логике создания вторичного тайла.

Я выделил код обновления тайла в отдельную функцию.
void TileUpdater(ShellTile tile, int count)
{
    StandardTileData appTileData = new StandardTileData();
            
    if ((count % 2) == 0)
    {
        appTileData.Title = "Агент 002";
        appTileData.BackgroundImage = new Uri("/1.png", UriKind.RelativeOrAbsolute);
    }
    else
        if ((count % 7) == 0)
        {
            appTileData.Title = "Агент 007";
            appTileData.BackgroundImage = new Uri("", UriKind.RelativeOrAbsolute);
        }
        else
        {
            appTileData.Title = "Агент 000";
            appTileData.BackgroundImage = new Uri("/2.png", UriKind.RelativeOrAbsolute);
        }

    appTileData.Count = count+1;
            

    tile.Update(appTileData);
        
}


Сам же метод OnInvoke выглядит так:
protected override void OnInvoke(ScheduledTask task)
{
    ShellTile AppTile = ShellTile.ActiveTiles.FirstOrDefault();

    IsolatedStorageFile CounterStorage = IsolatedStorageFile.GetUserStoreForApplication();
    IsolatedStorageFileStream CounterStream = CounterStorage.OpenFile("counter", System.IO.FileMode.OpenOrCreate);
    int count = CounterStream.ReadByte();
    if (count < 0) count = 1;

    //специальным образом обновляем тайл приложения
    TileUpdater(AppTile, count+1);

    //у нас есть тайлы кроме тайла приложения
    if (ShellTile.ActiveTiles.Count() > 1)
    {
        //первый тайл - этой тайл приложения
        IEnumerable<ShellTile> tiles = ShellTile.ActiveTiles.Skip(1);
                
        //обовляем все доступные Secondary Tiles
        foreach (ShellTile st in tiles)
        {
            TileUpdater(st, count);
        }
    }

    CounterStream.Seek(0, SeekOrigin.Begin);
    count++;
    if (count > 99) count = 99;
    CounterStream.WriteByte((byte)count);
    CounterStream.Close();


#if DEBUG
    ScheduledActionService.LaunchForTest(UpdateTileAgentName, TimeSpan.FromSeconds(10));
#endif
    NotifyComplete();
}


Что мы здесь видим? Уже знакомую нам просьбу запустить агент через 10 секунд и разделённую логику обновления основного тайла приложения и дополнительных. Хочу напомнить, что это можно реализовать, потому что, тайл приложения первый в списке и есть всегда, даже если приложение не закреплено на стартовом экране.

Также вы здесь можете обновить в агенте и другие свойства тайла. Подробнее о тайлах и свойствах можно прочитать здесь: http://msdn.microsoft.com/ru-ru/windowsphone/hh505822.

Теперь же можно собрать и запустить приложение в эмуляторе. Закрепите само приложение на стартовом экране, не забудьте создать второй тайл. Посмотрите, как меняются тайлы со временем.

Теперь, вооружившись этими знаниями, я думаю, вы без проблем добавите продвинутую функциональность Live Tiles в свои Windows Phone приложения.

UPD: Спасибо max_sokolov за напоминание. Периодически запускающиеся фоновые агенты имеют следующие свойства:
  • запускаются приблизительно раз в 30 минут
  • время их работы ограничено 25 секунадами (некоторые источники говорят о 15 секундах)
  • потребление ими мощностей процессора ограничено
  • потребление ими памяти ограничено (зависит от платформы, мин. значение — 6Мб)
  • система может решить не запускать агента, если ресурсов недостаточно (например, телефон разряжен)
  • если агент 3 раза завершится с ошибкой, система перестанет его запускать
  • агент добавляется в расписание запуска на 14 дней, потом можно/нужно обновить

Полезные ссылки:
Центр разработки Windows Phone на MSDN
Windows Phone SDK 7.1
Форумы по разработке под Windows Phone на русском языке
Tags:
Hubs:
+37
Comments 24
Comments Comments 24

Articles

Information

Website
www.microsoft.com
Registered
Founded
Employees
Unknown
Location
США