Makale Özeti

Bu makalemizde Windows Phone 7 işletim sistemli telefonlarda kullanılan Push Notification (Türkçede 'bildirim itme' ya da 'dürtme' anlamında) konusunu 3 farklı bildirim tipi (notification types) ile ele alacağız.

Makale

Bu makalemizde Windows Phone 7 işletim sistemli telefonlarda kullanılan Push Notification (Türkçede 'bildirim itme' ya da 'dürtme' anlamında) konusunu 3 farklı bildirim tipi (notification types) ile ele alacağız. Bu bildirim tiplerini sırasıyla;

1. Tile Notification
2. Toast Notification
3. Raw Notification

olarak 3 farklı gruba ayırarak inceleyelim. Öncelikle, kısaca ve ekran görüntüleri ile bu bildirim tiplerinin ne olduğunu anlamaya çalışalım.

1. Tile Notification

Windows Phone 7 açılış ekranında gösterimi yapılacak olan bildirim tipidir. Görsel olarak dikkat çekici konumdadır ve uygulamaları çalıştırdığımız ana ekranda yer alacaktır. Windows Phone 7 içerisinde her uygulama için ana ekranda bir 'Tile' (giriş ekranında yer alan ve application quick launchers olarak adlandırılan ekran ) bulunabilir.

Bir Tile Backgraund Image, Count ve Title'dan oluşur. Backgraund Image: Arka plan resmi için local veya bir uzak sunucuda bulunan imaj dosyası kullanılabilir. Tabiiki burada daha fazla performans istiyorsak local dosyada olması tercih nedeni olacaktır. Count: Integer sayıdır. Bildirim sayısını göstermek için kullanacağız. Title: Alt kısımda yer alan ve yalnızca bir satırlık string bilgi gösterilebilecek alandır. Bu sebepten dolayı tile genişliğini aşmayacak uzunlukta string bildirimler gönderilmesi gerekecektir.

2. Toast Notification (Tost :)

Bir bildirim geldiğinde ekranın üst kısmında belirecektir. Aşağıdaki resimde de görüleceği gibi turuncu arka plan renginde bir title ve bir de sub-title'dan oluşur. Title bold font, sub-title ise normal font olacaktır.

3. Raw Notification

Eğer yukarıda belirttiğimiz Tile ve Toast bildirimde bulunmak yerine direk olarak uygulamıza bildirimde bulunmak istiyorsak raw notification kullanacağız. Eğer gönderim sırasında uygulamamız kapalı ise Microsoft Push Notification servisinde bu bildirim iptal edilecektir ve cihaza iletilmeyecektir.

( Önemli Not : Tüm bu bildirimlerle ilgili UI tasarlamadan önce Microsoft' un belirttiği UI Guidelines'ı bir sıkıntı yaşamamak için (Phone Marketplace için) incelemekte fayda var. İlgili Guidelines'ı buradan indirebilirsiniz. )

Bildirim türlerinden kısaca bahsettikten sonra şimdi de bir örnek ile notification türlerini daha yakından incelemeye çalışalım. Örneğimiz için 2 farklı proje oluşturacağız. Öncelikle bir WPF projesi ile Notification Sender tarafını daha sonrada Windows Phone projesi ile bildirimlerin cihaza nasıl ulaştığını ve cihazda nasıl gösterildiğinden bahsedeceğiz.

Notification Sender Side (WPF Application)

Visual Studio 2010 / File / New / Project / Visual C# / Windows / WPF Application seçeneği ile yeni bir proje oluşturarak notification göndereceğimiz tarafı oluşturmaya başlayalım. MainWindows ekranımızı aşağıdaki gibi oluşturalım.

MainWindows XAML

<Window x:Class="SenderApplication.MainWindow"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      Title="Sender Application" Height="433" Width="563">
  <Grid>
      <Border BorderBrush="#FF8AC0C0" BorderThickness="1" Height="43"
  HorizontalAlignment
="Left" Margin="13,10,0,0" Name="border1" VerticalAlignment="Top" Width="507">           <TextBlock Height="21" Name="textBlock1" Text="Windows Phone 7 URI" Width="133" HorizontalAlignment="Left" Margin="5" />       </Border>       <TextBox Height="23" HorizontalAlignment="Left" Margin="152,21,0,0" Name="txtUri" VerticalAlignment="Top" Width="357" />       <GroupBox Header="Tile Notification" Height="100" HorizontalAlignment="Left"
  Margin
="13,59,0,0" Name="groupBox1" VerticalAlignment="Top" Width="513">           <Grid>               <TextBox Height="23" HorizontalAlignment="Left" Margin="133,6,0,0"                       Name="txtTileTitle" VerticalAlignment="Top" Width="142" />               <TextBlock Height="21" HorizontalAlignment="Left" Margin="10,8,0,48"                           Name="txt" Text="Title" Width="91" />               <TextBox Height="23" HorizontalAlignment="Left" Margin="133,35,0,0"                         Name="txtTileBackgraundimage" VerticalAlignment="Top" Width="252" />               <TextBlock Height="21" HorizontalAlignment="Left" Margin="10,35,0,21"                           Name="textBlock3" Text="Basckbraund Image" Width="109" />               <TextBox Height="23" HorizontalAlignment="Left" Margin="331,5,0,0"                         Name="txtCount" VerticalAlignment="Top" Width="54" />               <TextBlock Height="21" HorizontalAlignment="Left" Margin="281,7,0,49"                           Name="textBlock4" Text="Count" Width="44" />               <Button Content="Gönder" Height="23" HorizontalAlignment="Left" Margin="415,48,0,0"                        Name="btnSendTile" VerticalAlignment="Top" Width="75" Click="btnSendTile_Click" />           </Grid>       </GroupBox>       <GroupBox Header="Toast Notification" Height="100" HorizontalAlignment="Left" Margin="13,165,0,0"                  Name="groupBox2" VerticalAlignment="Top" Width="513">           <Grid>               <TextBox Height="23" HorizontalAlignment="Left" Margin="133,10,0,0"                         Name="txtToastValue1" VerticalAlignment="Top" Width="252" />               <TextBlock Height="21" HorizontalAlignment="Left" Margin="10,10,0,46"                           Name="textBlock2" Text="String Value" Width="124" />               <TextBox Height="23" HorizontalAlignment="Left" Margin="133,39,0,0"                         Name="txtToastValue2" VerticalAlignment="Top" Width="252" />               <TextBlock Height="21" HorizontalAlignment="Left" Margin="10,39,0,17"                           Name="textBlock5" Text="Sub-String Value" Width="124" />               <Button Content="Gönder" Height="23" HorizontalAlignment="Left" Margin="415,48,0,0"                        Name="btnSendToast" VerticalAlignment="Top" Width="75" Click="btnSendToast_Click" />           </Grid>       </GroupBox>       <GroupBox Header="Raw Notification" Height="100" HorizontalAlignment="Left" Margin="13,271,0,0"                 Name="groupBox3" VerticalAlignment="Top" Width="513">           <Grid>               <TextBox Height="23" HorizontalAlignment="Left" Margin="133,10,0,0"                         Name="txtRawValue" VerticalAlignment="Top" Width="252" />               <TextBlock Height="21" HorizontalAlignment="Left" Margin="10,10,0,46"                           Name="textBlock6" Text="String Value" Width="124" />               <Button Content="Gönder" Height="23" HorizontalAlignment="Left" Margin="415,48,0,0"                        Name="btnSendRaw" VerticalAlignment="Top" Width="75" Click="btnSendRaw_Click" />           </Grid>       </GroupBox>   </Grid> </Window>

UI hazırladıktan sonra Tile, Toast ve Raw buttonların click eventları altına ilgi notification gönderim kodlarını ekleyelim. Tile notification özelliklerine uygun olacak şekilde bir title, count ve arkaplanda çıkacak imaj adresini parametre olarak olan methodumuzu hazırlayalım. Imaj olarak ApplicationIcon.png dosyasını kullanabiliriz. "ApplicationIcon.png" dışında client windows phone uygulamamız tarafına content olarak eklenmiş farklı bir imaj dosyası da kullanabilirdik. (Windows Phone projesine eklenecek imaj dosyasının build action özelliğinin content olmasına dikkat edelim.)

Tile Notification Sender Method ve Button Click Event;

private void sendTile(Uri phoneUri, string title, int count, string backgroundImage)
{
    string xmlMsg = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
                    "<wp:Notification xmlns:wp=\"WPNotification\">" +
                    "<wp:Tile>" +
                    "<wp:BackgroundImage>" + backgroundImage + "</wp:BackgroundImage>" +
                    "<wp:Count>" + count.ToString() + "</wp:Count>" +
                    "<wp:Title>" + title + "</wp:Title>" +
                    "</wp:Tile>" +
                    "</wp:Notification>";
    byte[] data = new UTF8Encoding().GetBytes(xmlMsg);
 
    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(phoneUri);
    request.Method = WebRequestMethods.Http.Post;
    request.ContentLength = data.Length;
    request.Headers["X-MessageID"] = Guid.NewGuid().ToString();
 
    request.Headers["X-WindowsPhone-Target"] = "token";
    request.ContentType = "text/xml";
    request.Headers["X-NotificationClass"] = "1";

    Stream requestStream = request.GetRequestStream();
    requestStream.Write(data, 0, data.Length);
    requestStream.Close();
    HttpWebResponse response = request.GetResponse() as HttpWebResponse;
}
private void btnSendTile_Click(object sender, RoutedEventArgs e)
{
    int count = Convert.ToInt32(txtCount.Text);
    sendTile(new Uri(txtUri.Text), txtTileTitle.Text, count ,txtTileBackgraundimage.Text);
    count++;
    txtCount.Text = count.ToString();
}

Yukarıdaki sendTile methoduna parametre olarak vereceğimiz phoneUri değerinin de önceden WindowsPhone tarafından verilmiş olması gerekiyor. uri adresinin nasıl elde edileceğini Windows Phone Application tarafında göreceğiz.

Şimdi de Toast notification gönderecek olan methodumuzu ve click event'ımızı ekleyelim.

private void sendToast(Uri phoneUri, string str, string subStr)
{
    string msg =
        "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
        "<wp:Notification xmlns:wp=\"WPNotification\">" +
        "<wp:Toast>" +
        "<wp:Text1>" + str + "</wp:Text1>" +
        "<wp:Text2>" + subStr + "</wp:Text2>" +
        "</wp:Toast>" +
        "</wp:Notification>";
    byte[] data = new UTF8Encoding().GetBytes(msg);
    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(phoneUri);
    request.Method = WebRequestMethods.Http.Post;
    request.ContentLength = data.Length;
    request.Headers["X-MessageID"] = Guid.NewGuid().ToString();
    request.Headers["X-WindowsPhone-Target"] = "toast";
    request.ContentType = "text/xml";
    request.Headers["X-NotificationClass"] = "2";
    Stream requestStream = request.GetRequestStream();
    requestStream.Write(data, 0, data.Length);
    requestStream.Close();
    HttpWebResponse response = request.GetResponse() as HttpWebResponse;
}
private void btnSendToast_Click(object sender, RoutedEventArgs e)
{
    sendToast(new Uri(txtUri.Text), txtToastValue1.Text, txtToastValue2.Text);
}

Yukarıdaki kod satırlarında görüleceği gibi Toast gönderime uygun olacak şekilde sendToast parametre değerine bir string ve sub-string değer gönderiyoruz. Tile gönderimde olduğu gibi burada da uri adresini windows phone tarafında elde ediyor olacağız.

HttpWebResponse response = request.GetResponse() as HttpWebResponse

HttpWebResponse dönen kod satırında ise eğer dilerseniz gönderim sonrası durum bilgilerini ekrana yazdırabilirsiniz. Örneğin response.Header["X-DeviceConnectionStatus"] ile cihaz bağlantı durum bilgisi elde edilebilir.

Raw Notification gönderimi için de aşağıdaki kod satırlarını ekleyelim;

private void sendRaw(Uri phoneUri, string str)
{
    MemoryStream stream = new MemoryStream();
    BinaryWriter writer = new BinaryWriter(stream, Encoding.UTF8);
    writer.Write(str);
    byte[] data = stream.ToArray();
    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(phoneUri);
    request.Method = WebRequestMethods.Http.Post;
    request.ContentLength = data.Length;
    request.Headers["X-MessageID"] = Guid.NewGuid().ToString();
    request.Headers["X-NotificationClass"] = "3";
    Stream requestStream = request.GetRequestStream();
    requestStream.Write(data, 0, data.Length);
    requestStream.Close();
}
private void btnSendRaw_Click(object sender, RoutedEventArgs e)
{
    sendRaw(new Uri(txtUri.Text), txtRawValue.Text);
}

Raw notification gönderiminde de benzer şekilde string değerimiz ile windows phone kanal adresimizi parametre olarak gönderiyoruz.

Şimdi de Windows Phone tarafında yeni bir bağlantı kanalı oluşturarak sender uygulamıza verelim. Hemen ardından bildirimlerin nasıl gözükeceğine göz atalım.

Windows Phone Application Side

Öncelikle private kanal değişkenimizi, kanala vereceğimiz adı ve constractorümüz içerisinde Loaded event ekleyerek başlayalım. HttpNotificationChannel using Microsoft.Phone.Notification namespace altında bulunmaktadır. bu namespace default olarak projemize eklenen Microsoft.Phone.dll altında bulunmaktadır.

private HttpNotificationChannel httpChannel;
const string channelName = "MyPushChannel";
const string serviceName = "MyPushServis";
public MainPage()
{
    InitializeComponent();
    this.Loaded += MainPage_Loaded;
}

MainPage Loaded event handler altında çalışacak kod satırlarımızı ekleyelim. Tanımladığımız isimle yeni bir notification kanalı oluşturulmasını sağlayalım. Eğer bu isimde bir kanal tanımlı ise hazır durumda olan kanalımızı debug modda iken output penceremize yazdıracağız(Debug.WriteLine ile). Bu event handler içerisinde, yeni bir kanal oluşturma ve mevcut kanalın kontrol edilmesi Microsoft Push Notification Servisi tarafından verilmektedir. Bu servisin nasıl çalıştığı konusunda bilgi almak isteyenler buradan ulaşabilirler.

private  void MainPage_Loaded(object sender, RoutedEventArgs e)
{
    httpChannel = HttpNotificationChannel.Find(channelName);
    if (null == httpChannel)
    {
        Debug.WriteLine("Yeni kanal oluşturuldu.");
 
        httpChannel = new HttpNotificationChannel(channelName, serviceName);
        httpChannel.ChannelUriUpdated += 
            new EventHandler<NotificationChannelUriEventArgs>(httpChannel_ChannelUriUpdated);
        httpChannel.Open();
    }
    else
    {
        Debug.WriteLine("Var olan kanal : " +
                        httpChannel.ChannelUri.ToString());
    }
 
    // Raw event handler ekleniyor
    httpChannel.HttpNotificationReceived += httpChannel_HttpNotificationReceived;
            
    // Toast event handler ekleniyor
    httpChannel.ShellToastNotificationReceived += httpChannel_ShellToastNotificationReceived;
 
    // Hata için event handler
    httpChannel.ErrorOccurred += httpChannel_ErrorOccurred;
}

ChannelUriUpdated event handler Microsoft Push Notification servisinden gelen yanıtları yakalayacaktır;

private void httpChannel_ChannelUriUpdated(object sender, NotificationChannelUriEventArgs e)
{
    Debug.WriteLine("Güncellenen kanal:" + httpChannel.ChannelUri.ToString());
    httpChannel.BindToShellToast();
    httpChannel.BindToShellTile();
}

Şimdide sırasıyla Raw ve Toast bildirimleri yakalayan event handler kod satırlarımızı ekleyelim. Gelen bildirimler asenkron olarak çalışacağı için Windows Phone ekranına koyacağımız bir textblock'da görüntüleyebilmek için textblock'un Dispatcher.BeginInvoke methodundan faydalanacağız.

private void httpChannel_HttpNotificationReceived(object sender, HttpNotificationEventArgs e)
{
    BinaryReader reader = new BinaryReader(e.Notification.Body, System.Text.Encoding.UTF8);
    string ntext = reader.ReadString();
    txtMessageBox.Dispatcher.BeginInvoke(new Action(delegate() {
        txtMessageBox.Text += "Raw Notification" + Environment.NewLine;
        txtMessageBox.Text += ntext + Environment.NewLine;
     } ) );
}
 
private void httpChannel_ShellToastNotificationReceived(object sender, NotificationEventArgs e)
{
    txtMessageBox.Dispatcher.BeginInvoke(new Action(delegate() {
        txtMessageBox.Text += "Toast Notification:" + Environment.NewLine;
    }));
    if (e.Collection != null)
    {
        Dictionary<stringstring> collection = (Dictionary<stringstring>)e.Collection;
        foreach (string elementName in collection.Keys)
        {
            txtMessageBox.Dispatcher.BeginInvoke(new Action(delegate() {
              txtMessageBox.Text += elementName + " - " + collection[elementName] + Environment.NewLine;
            }));
        }
    }
}

ErrorOccured event handler da bir hata oluşması durumunda, hatayı yakalayacak ve kanalı kapatıp açarak tekrar bağlantının kurulmasını deneyecektir.

private void httpChannel_ErrorOccurred(object sender, NotificationChannelErrorEventArgs e)
{
    Debug.WriteLine("Hata oluştu: " + e.Message);
    Debug.WriteLine("Kanal tekrar açılacak...");
    httpChannel.Close();
    httpChannel.Open();
}

Windows Phone kod satırlarımızı da eklediğimize göre, şimdi debug modda F5 ile projemizi çalıştırıp, olup biteceklere göz atalım. Windows Phone 7 Emulatör seçili durumda ve debug modda uygulamamızı çalıştıralım.  MainWindows Loaded event handler altına eklediğimiz Debug.WriteLine kod satırları ile gönderdiğimiz bilgilere output penceresinden bakıyoruz.

Output penceresinden Microsoft Push Notification Servisinin gönderdiği kanal adresini kopyalayarak sender programımızı çalıştırıp ilgili URI alanına, kanal adresini kopyalayalım.

Raw Notification string alanına "Merhaba Dünya :)" yazarak Gönder butonu ile bildirimimizi gönderelim. Windows Phone uygulamamız açık ve ilgili event handler eklenmiş olduğu için gönderilen bildirim aşağıdaki ekran görüntüsünde olduğu gibi ekrana yansıyacaktır.

Raw notification ile gönderdiğimiz bildirim mesajı yalnızca uygulama ekranı açık olduğunda iletilecektir. Toast ve Tile notification'lar için uygulama ekranının açık olmasına gerek yoktur. İlgili bildirim Toast ise ekranın üst kısmında belirecek, Tile ise quick launch ekranında uygulama tile alanında yazdırılacaktır.

Aşağıdaki ekranda Windows Phone Toast notification görüntüsü yer alıyor. Phone uygulamamızın icon özelliğini project properties'den "warning.png" olarak değiştirdiğimiz için bir ünlem işareti ile toast bildirimi almış olduk ve ilgili bildirim mesajıyla "Ekmek Almayı unutma :)" mesajını göndermiş olduk.

Kolay gelsin.

Faydalı Linkler:

http://msdn.microsoft.com/en-us/library/ff941124(v=vs.92).aspx
http://msdn.microsoft.com/en-us/wp7trainingcourse_usingpushnotificationslab_topic1
http://create.msdn.com/en-US/education/catalog/sample/push_notifications

Gökhan Manduz
http://gokhanmanduz.blogspot.com/
gmanduz@gmail.com