Artykuł

freeimages.com freeimages.com
paź 28 2015
0

Obsługa lokalnych notyfikacji w Universal Apps

Notyfikacje są obecnie czymś powszechnym w każdym liczącym się mobilnym systemie. Dzięki nim, jesteśmy na bieżąco z mailami, powiadomieniami z Facebooka, czy choćby z wynikiem wyczekiwanego meczu. Windows Phone nie odstaje niczym w tym obszarze od swojej konkurencji, a nawet oferuje kilka dodatkowych, ciekawych opcji.

W dzisiejszym tekście przedstawię przykładowe użycie lokalnych notyfikacji typu toast - zarówno w wersji harmonogramowanej (scheduled) jak i normalnej. Notyfikacje innego typu wysyła się w zbliżony sposób - zmieniamy rodzaj używanych klas oraz XMLa.

Kilka słów teorii

Notyfikacje typu toast, możemy wysyłać od razu bądź też w zaplanowanym czasie. Jeśli skorzystamy z tej drugiej opcji, to notyfikacja zostanie dostarczona również w sytuacji gdy aplikacja będzie wyłączona.

Notyfikacje w systemie Windows Phone budowane są w oparciu o zestaw predefiniowanych szablonów XML, które możemy edytować na potrzeby konkretnego powiadomienia. Dostępne rodzaje szablonów zapisano w enumeracji ToastTemplateType.

W każdej notyfikacji, oprócz standardowych pól typu tytuł, tekst itp., możemy również dodać atrybut launch, który pozwala na określenie argumentów, które zostaną przekazane do aplikacji po kliknięciu w powiadomienie. String tego typu nie powinien być zbyt długi, by nie spowodować wyjątku. Z reguły jest to id jakiegoś elementu w bazie danych, na temat którego szczegóły aplikacja pobiera sobie sama.

Przykład praktyczny

Zwieńczeniem tekstu będzie przykład praktyczny. Wykorzystałem w tym celu zrąb standardowego projektu, do którego wprowadziłem kilka poprawek. Zacznijmy od pliku App.xaml.cs oraz fragmentu metody OnLaunched:

if (rootFrame.Content == null)
{
    // ...
    if (!rootFrame.Navigate(typeof(MainPage), e.Arguments))
    {
        throw new Exception("Failed to create initial page");
    }
}
else
{
    if(rootFrame.Content is MainPage)
    {
        ((MainPage)rootFrame.Content).ReceivedValue = e.Arguments;
    }
}

W pierwszym fragmencie kodu, widzimy próbę nawigacji do strony MainPage, w sytuacji gdy aplikacja nie była wcześniej uruchomiona. To na co warto zwrócić uwagę w tym miejscu to fakt, że do strony przekazywane są argumenty aplikacji. Druga część kodu zostaje wywołana w sytuacji gdy program jest aktywny, a my kliknęliśmy na notyfikację. W takiej sytuacji sprawdzamy czy obecnie znajdujemy się na stronie MainPage i jeśli tak, to przekazujemy do jednej z właściwości tej strony, wartość pola Arguments. To oczywiście spore uproszczenie, w praktyce większość aplikacji posiada sporą liczbę różnych stron i powinniśmy te sytuację obsłużyć w jakiś bardziej zmyślny sposób, który będzie pasował do naszego kontekstu.

Pozostały kod aplikacji koncentruje się wokół strony MainPage. Zacznijmy od frontendu:

<Page
    x:Class="LocalNotifications.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:LocalNotifications"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

    <Grid x:Name="LayoutRoot">
        <Grid.Resources>
            <Style TargetType="Button">
                <Setter Property="HorizontalAlignment" Value="Stretch" />
            </Style>
        </Grid.Resources>
                
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <StackPanel Margin="19,0,0,0">
            <TextBlock Text="NOTIFICATIONS APP" Style="{ThemeResource TitleTextBlockStyle}" Margin="0,12,0,0"/>
            <TextBlock Text="test page" Margin="0,-6.5,0,26.5" Style="{ThemeResource HeaderTextBlockStyle}" 
                       CharacterSpacing="{ThemeResource PivotHeaderItemCharacterSpacing}"/>
        </StackPanel>

        <StackPanel Grid.Row="1" x:Name="ContentRoot" Margin="19,9.5,19,0">
            <Button x:Name="ButtonToast" Click="ButtonToast_Click" Content="instant toast" />
            <Button x:Name="ButtonScheduledToast" Click="ButtonScheduledToast_Click" 
                    Content="scheduled toast (10s)" />
            <TextBlock Style="{StaticResource BaseTextBlockStyle}">
                <Run Text="received value:" />
                <Run Text="{Binding ReceivedValue}" />
            </TextBlock>
        </StackPanel>
    </Grid>
</Page>

W tym przypadku nic nadzwyczajnego. Dwa proste przyciski, a każdy z nich wysyła inną notyfikację. Ostatnim elementem formatki jest TextBlock, który wyświetla argumenty przekazane do aplikacji - jeśli oczywiście takowe występują.

Najważniejsze elementy znajdują się w części Code-Behind. Poniżej skrócony kod:

// usingi
namespace LocalNotifications
{
    [ImplementPropertyChanged]
    public sealed partial class MainPage : Page
    {
        private NavigationHelper navigationHelper;

        public MainPage()
        {
            // ...
            this.DataContext = this;
        }

        public string ReceivedValue
        {
            get;
            set;
        }

        // ...

        #region NavigationHelper registration
        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            this.navigationHelper.OnNavigatedTo(e);
            if(e.Parameter != null)
            {
                this.ReceivedValue = e.Parameter.ToString();
            }
        }

        // ...
        #endregion

        private void ButtonToast_Click(object sender, RoutedEventArgs e)
        {
            ToastTemplateType toastTemplate = ToastTemplateType.ToastText02;
            XmlDocument toastXml = ToastNotificationManager.GetTemplateContent(toastTemplate);
            var toastElement = ((XmlElement)toastXml.SelectSingleNode("/toast"));
            var toastTextElements = toastXml.GetElementsByTagName("text");
            toastElement.SetAttribute("launch", "regulararg");
            toastTextElements[0].AppendChild(toastXml.CreateTextNode("Regular Toast!"));

            try
            {
                ToastNotification toast = new ToastNotification(toastXml);
                var toastNotifier = ToastNotificationManager.CreateToastNotifier();
                toastNotifier.Show(toast);
            }
            catch (Exception ex)
            {
                // Handle exception
            }
        }

        private void ButtonScheduledToast_Click(object sender, RoutedEventArgs e)
        {
            ToastTemplateType toastTemplate = ToastTemplateType.ToastText02;
            XmlDocument toastXml = ToastNotificationManager.GetTemplateContent(toastTemplate);
            var toastElement = ((XmlElement)toastXml.SelectSingleNode("/toast"));
            var toastTextElements = toastXml.GetElementsByTagName("text");
            toastElement.SetAttribute("launch", "scheduledarg");
            toastTextElements[0].AppendChild(toastXml.CreateTextNode("Scheduled Toast!"));

            try
            {
                ScheduledToastNotification scheduledToast = new 
                    ScheduledToastNotification(toastXml, DateTimeOffset.Now.AddSeconds(10));
                var toastNotifier = ToastNotificationManager.CreateToastNotifier();
                toastNotifier.AddToSchedule(scheduledToast);
            }
            catch (Exception ex)
            {
                // Handle exception
            }
        }
    }
}

Aby nie bawić się w implementację interfejsu INotifyPropertyChanged, postanowiłem wykorzystać sprawdzoną bibliotekę Fody (4). W kolejnym kroku ustawiamy kontekst danych (12) oraz definiujemy właściwość ReceivedValue (15-19), do której odnosiliśmy się już wcześniej zarówno z poziomu frontendu jak i pliku App.xaml.cs. W przypadku czystego uruchomienia aplikacji, powinniśmy sprawdzić, czy argumenty zostały przekazane do strony (24-31) i w razie czego podstawić je do wspomnianej wyżej właściwości.

Całą brudną robotę odwalają dwie metody na końcu listingu, które wysyłają notyfikacje. Jest to na tyle prosty i czytelny kod, że odpuszczę sobie jego szerszy opis.

Aby przykład zadziałał (a będzie działać poprawnie nawet na emulatorze), musimy pamiętać o ustawieniu flagi Toast capable w manifeście aplikacji.

Jak widać na załączonym przykładzie, cały mechanizm wysyłania lokalnych notyfikacji nie należy do skomplikowanych. Proces wysyłania pushy jest równie przyjazny, co postaram się udowodnić w jednym z kolejnych wpisów;-)

Cały kod projektu użytego w przykładzie, znajdziecie w dziale download.

Podoba Ci się ten wpis? Powiedz o tym innym!

Send to Kindle

Komentarze

blog comments powered by Disqus