Azure 行動推播中樞
想要能夠讓您開發的 Xamarin.Forms 應用程式,接收到來自遠端的訊息推播,這需要您:
- 申請與啟用Google Cloud Messaging (GCM)(對於 Google 平台行動裝置而言) /Apple Push Notification Service (APNS)(對於 iOS 行動平台裝置而言) /Windows Push Notification Services (WNS)(對於 Windows 行動平台裝置而言)
- 申請與建立 Azure 行動應用的推播設定
- 修改 Azure 行動應用的後端
- 修改 Xamarin.Forms 應用程式
在這裡,將以 Android 平台作為實作說明,首先,需要申請 
oogle Cloud Messaging (GCM), 接著,嘗試修正 附件 Azure Mobile App 後端服務建立說明 文章中的 Azure Mobile App 後端服務專案 與 Azure Mobile App 前端專案 程式碼。申請 Google Cloud Messaging (GCM)
- 首先,打開網頁,進入到Google Cloud Consolehttps://console.developers.google.com/iam-admin/projects
- 點選+ 建立專案連結
- 當建立專案對話窗出現之後,請在專案名稱欄位內填入DoggyMobileGCM,您可以看到您的專案 ID 顯示在對話窗內,而後點選建立按鈕 
- 當專案建立完成之後並且出現在所有專案清單內,請點選這個剛剛產生的專案DoggyMobileGCM 
- 當IAM與管理員頁面出現後,點選設定,您會看到專案編號欄位,請將此編號 (213736323010) 複製下來,等下會用到 
- 接著,請點畫面中右上方的Google APIs圖示連結,進入到 Google APIs 申請頁面,當然,也可以直接輸入這個網址 : https://console.developers.google.com/apis/library?project=doggymobilegcm 
- 進入到API管理員頁面,請找到行動服務類 API標題,接著點選Google Cloud Messaging 
- 總覽頁面看到之後,請點選啟用按鈕,當啟用完成之後,接著點選左方的憑證進入到憑證設定頁面 下圖圍啟用完成 下圖圍啟用完成 
- 此時,網頁中間會出現API 憑證這個對話窗 ,請點選建立憑證按鈕,接著點選API金鑰選項。 
- 而後,會出現建立新的金鑰對話窗,請點選「伺服器」金鑰按鈕 
- 當回到憑證頁面,如下圖,請在名稱欄位內輸入伺服器金鑰DoggyMobile做為日後識別之用,接著點選建立按鈕。 
- 最後,您會看到您申請的 API 金鑰 已經產生出來了AIzaSyCp2pENhEkrSD1tErz1AQyb2FN8zuz7TpQ請複製這個金鑰,等下會用到。您可以點選確定按鈕,以關閉這個對話窗。  
Azure 行動應用之推送設定
- 請回到 Azure 儀表板,接著點選doggymobilebe 行動 App圖示,進入到doggymobilebe 行動 App設定刀鋒頁面。
- 在設定刀鋒頁面,點選推送
- 在Create Notificati...刀鋒頁面,點選Notification Hub
- 在Notification Hub刀鋒頁面,點選+ Notification Hub 
- 在剛剛出現新的Notification Hub刀鋒頁面- 請在Notification Hub欄位內,輸入DoggyMobileNotificationHub
- 請點選Namespace 設定必要設定下面一點點的Or create new這個連結。(在此假設您需要為這個 Notification Hub產生一個新的Namespace`, 若您的 Azure 上已經有新的 Namespace,可以直接選取)。
- 請在Create a new namespace欄位內,輸入DoggyMobileNotiHubNamespace
- 請選擇釘選到儀表板檢查盒,使其在勾選狀態下
- 最後請點選確定按鈕
  
- 此時,多餘的刀鋒視窗會自動關閉,剩下Create Notificati...刀鋒視窗,請在該刀鋒視窗下方,點選建立按鈕
- 此時會回到 Azure 儀表板,當剛剛建立的 Notification Hub & Namespace 建立完成之後(您也可以從右上方的提示圖示中看到處理進度與是否完成),點選doggymobilebe 行動 App圖示,進入到doggymobilebe 行動 App設定刀鋒頁面。
- 此時在設定刀鋒頁面內,您會看到一個推送 (DoggyMobileNotificationHu...項目,請點選這個項目
- 在Push notification...刀鋒頁面,點選Google (GCM)
- 在Google (GC...)刀鋒頁面,請在API Key欄位中,輸入剛剛在 Google Cloud Messaging (GCM) 內申請得到的 API Key (AIzaSyCp2pENhEkrSD1tErz1AQyb2FN8zuz7TpQ),而後,點選Save按鈕。當儲存成功的訊息 (Success Notification Hub updated successfully!) 顯示在螢幕上,請點選確定按鈕  
修改 Azure 行動應用的後端
- 請根據 Azure Mobile App 後端服務建立說明 文章中,最後下載的Azure Mobile App 後端服務專案, 使用 Visual Studio 2015,開啟 Azure Mobile App 前端專案DoggyMobileBE.sln
- 請先安裝這個Microsoft.Azure.NotificationHubsNuGet 套件到專案內。
- 在方案總管內,展開專案DoggyMobileBEService>Controllers,接著打開TodoItemController.cs檔案。- 將底下 using 程式碼加入到這個檔案內
 
using System.Collections.Generic;
using Microsoft.Azure.NotificationHubs;
using Microsoft.Azure.Mobile.Server.Config;
- 在 PostTodoItem 方法內,請將底下程式碼加入到呼叫 InsertAsync 方法之後
// Get the settings for the server project.
HttpConfiguration config = this.Configuration;
MobileAppSettingsDictionary settings = 
    this.Configuration.GetMobileAppSettingsProvider().GetMobileAppSettings();
// Get the Notification Hubs credentials for the Mobile App.
string notificationHubName = settings.NotificationHubName;
string notificationHubConnection = settings
    .Connections[MobileAppSettingsKeys.NotificationHubConnectionString].ConnectionString;
// Create a new Notification Hub client.
NotificationHubClient hub = NotificationHubClient
.CreateClientFromConnectionString(notificationHubConnection, notificationHubName);
// Sending the message so that all template registrations that contain "messageParam"
// will receive the notifications. This includes APNS, GCM, WNS, and MPNS template registrations.
Dictionary<string,string> templateParams = new Dictionary<string,string>();
templateParams["messageParam"] = item.Text + " was added to the list.";
try
{
    // Send the push notification and log the results.
    var result = await hub.SendTemplateNotificationAsync(templateParams);
    // Write the success result to the logs.
    config.Services.GetTraceWriter().Info(result.State.ToString());
}
catch (System.Exception ex)
{
    // Write the failure result to the logs.
    config.Services.GetTraceWriter()
        .Error(ex.Message, null, "Push.SendAsync Error");
}
- 請重新 發行這個專案
修改 DoggyMobileBE.Droid 專案
- 請根據 Azure Mobile App 後端服務建立說明 文章中,最後下載的Azure Mobile App 前端專案, 使用 Visual Studio 2015,開啟 Azure Mobile App 前端專案DoggyMobileBE.sln
- 請展開專案DoggyMobileBE.Droid,使用滑鼠右擊Components節點,選擇Get More Components...- 在All Components對話窗內,請搜尋Google Cloud Messaging Client這個元件,找到後,點選這個找到項目 
- 當出現Xamarin Component Store ▶ Google Cloud Messaging Client對話窗時候,請點選右方的Add to App按鈕 
 
- 打開MainActivity.cs檔案- 加入底下 using 敘述到這個檔案內
 
using Gcm.Client;
- 在 OnCreate 方法內,在 LoadApplication 之後,加入底下程式碼
try
{
    // Check to ensure everything's setup right
    GcmClient.CheckDevice(this);
    GcmClient.CheckManifest(this);
    // Register for push notifications
    System.Diagnostics.Debug.WriteLine("Registering...");
    GcmClient.Register(this, PushHandlerBroadcastReceiver.SENDER_IDS);
}
catch (Java.Net.MalformedURLException)
{
    CreateAndShowDialog("There was an error creating the client. Verify the URL.", "Error");
}
catch (Exception e)
{
    CreateAndShowDialog(e.Message, "Error");
}
- 將底下程式碼的 CreateAndShowDialog 輔助方法加入到這個檔案 MainActivity 類別
private void CreateAndShowDialog(String message, String title)
{
    AlertDialog.Builder builder = new AlertDialog.Builder(this);
    builder.SetMessage (message);
    builder.SetTitle (title);
    builder.Create().Show ();
}
- 在 MainActivity 類別內,加入底下屬性宣告程式碼
// Create a new instance field for this activity.
static MainActivity instance = null;
// Return the current activity instance.
public static MainActivity CurrentActivity
{
    get
    {
        return instance;
    }
}
- 在 OnCreate 方法內的最前面,加入底下初始化 instance 的程式碼
// Set the current instance of MainActivity.
instance = this;
- 滑鼠右擊專案 DoggyMobileBE.Droid 節點,選擇加入>類別- 請點選Visual C#>Class
- 在底下名稱欄位內輸入GcmService
- 點選新增按鈕
 
- 在 GcmService.cs 檔案內- 加入底下 using 敘述
 
using Android.App;
using Android.Content;
using Android.Media;
using Android.Support.V4.App;
using Android.Util;
using Gcm.Client;
using Microsoft.WindowsAzure.MobileServices;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
- 在 using 敘述之後與 namespace 之前,加入底下程式碼
[assembly: Permission(Name = "@PACKAGE_NAME@.permission.C2D_MESSAGE")]
[assembly: UsesPermission(Name = "@PACKAGE_NAME@.permission.C2D_MESSAGE")]
[assembly: UsesPermission(Name = "com.google.android.c2dm.permission.RECEIVE")]
[assembly: UsesPermission(Name = "android.permission.INTERNET")]
[assembly: UsesPermission(Name = "android.permission.WAKE_LOCK")]
//GET_ACCOUNTS is only needed for android versions 4.0.3 and below
[assembly: UsesPermission(Name = "android.permission.GET_ACCOUNTS")]
- 在 namespace 之後,加入底下新增的類別定義程式碼- 請將之前在 Google Cloud Messaging (GCM)取得的專案編號(也就是 213736323010 ) 替換掉底下文字<PROJECT_NUMBER>
 
- 請將之前在 
[BroadcastReceiver(Permission = Gcm.Client.Constants.PERMISSION_GCM_INTENTS)]
[IntentFilter(new string[] { Gcm.Client.Constants.INTENT_FROM_GCM_MESSAGE }, Categories = new string[] { "@PACKAGE_NAME@" })]
[IntentFilter(new string[] { Gcm.Client.Constants.INTENT_FROM_GCM_REGISTRATION_CALLBACK }, Categories = new string[] { "@PACKAGE_NAME@" })]
[IntentFilter(new string[] { Gcm.Client.Constants.INTENT_FROM_GCM_LIBRARY_RETRY }, Categories = new string[] { "@PACKAGE_NAME@" })]
public class PushHandlerBroadcastReceiver : GcmBroadcastReceiverBase<GcmService>
{
    public static string[] SENDER_IDS = new string[] { "213736323010" };
}
- 使用底下程式碼,將 GcmService 類別定義替換掉
 [Service]
 public class GcmService : GcmServiceBase
 {
     public static string RegistrationID { get; private set; }
     public GcmService()
         : base(PushHandlerBroadcastReceiver.SENDER_IDS){}
 }
- 使用底下程式碼,加入到 GcmService 類別內```cs protected override void OnRegistered(Context context, string registrationId) { Log.Verbose("PushHandlerBroadcastReceiver", "GCM Registered: " + registrationId); RegistrationID = registrationId;var push = TodoItemManager.DefaultManager.CurrentClient.GetPush();MainActivity.CurrentActivity.RunOnUiThread(() => Register(push, null)); }
public async void Register(Microsoft.WindowsAzure.MobileServices.Push push, IEnumerable tags) { try { const string templateBodyGCM = "{\"data\":{\"message\":\"$(messageParam)\"}}"; 
    JObject templates = new JObject();
    templates["genericMessage"] = new JObject
    {
        {"body", templateBodyGCM}
    };
    await push.RegisterAsync(RegistrationID, templates);
    Log.Info("Push Installation Id", push.InstallationId.ToString());
}
catch (Exception ex)
{
    System.Diagnostics.Debug.WriteLine(ex.Message);
    Debugger.Break();
}
}
10.  使用底下程式碼,加入到 GcmService 類別內
```cs
protected override void OnMessage(Context context, Intent intent)
{
    Log.Info("PushHandlerBroadcastReceiver", "GCM Message Received!");
    var msg = new StringBuilder();
    if (intent != null && intent.Extras != null)
    {
        foreach (var key in intent.Extras.KeySet())
            msg.AppendLine(key + "=" + intent.Extras.Get(key).ToString());
    }
    //Store the message
    var prefs = GetSharedPreferences(context.PackageName, FileCreationMode.Private);
    var edit = prefs.Edit();
    edit.PutString("last_msg", msg.ToString());
    edit.Commit();
    string message = intent.Extras.GetString("message");
    if (!string.IsNullOrEmpty(message))
    {
        createNotification("New todo item!", "Todo item: " + message);
        return;
    }
    string msg2 = intent.Extras.GetString("msg");
    if (!string.IsNullOrEmpty(msg2))
    {
        createNotification("New hub message!", msg2);
        return;
    }
    createNotification("Unknown message details", msg.ToString());
}
void createNotification(string title, string desc)
{
    //Create notification
    var notificationManager = GetSystemService(Context.NotificationService) as NotificationManager;
    //Create an intent to show ui
    var uiIntent = new Intent(this, typeof(MainActivity));
    //Use Notification Builder
    NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
    //Create the notification
    //we use the pending intent, passing our ui intent over which will get called
    //when the notification is tapped.
    var notification = builder.SetContentIntent(PendingIntent.GetActivity(this, 0, uiIntent, 0))
            .SetSmallIcon(Android.Resource.Drawable.SymActionEmail)
            .SetTicker(title)
            .SetContentTitle(title)
            .SetContentText(desc)
            //Set the notification sound
            .SetSound(RingtoneManager.GetDefaultUri(RingtoneType.Notification))
            //Auto cancel will remove the notification once the user touches it
            .SetAutoCancel(true).Build();
    //Show the notification
    notificationManager.Notify(1, notification);
}
- 使用底下程式碼,加入到 GcmService 類別內
protected override void OnUnRegistered(Context context, string registrationId)
{
    Log.Error("PushHandlerBroadcastReceiver", "Unregistered RegisterationId : " + registrationId);
}
protected override void OnError(Context context, string errorId)
{
    Log.Error("PushHandlerBroadcastReceiver", "GCM Error: " + errorId);
}
- 滑鼠右擊DoggyMobileBE.Droid,選擇 選擇設定為起始專案,並且按下 F5 開始執行    
 
 
