PushContext.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360
  1. using System;
  2. using System.Net;
  3. using System.Windows;
  4. using System.Windows.Controls;
  5. using System.Windows.Documents;
  6. using System.Windows.Ink;
  7. using System.Windows.Input;
  8. using System.Windows.Media;
  9. using System.Windows.Media.Animation;
  10. using System.Windows.Shapes;
  11. using Microsoft.Phone.Notification;
  12. using System.Windows.Threading;
  13. using System.Collections.Generic;
  14. using System.Collections.ObjectModel;
  15. using System.IO.IsolatedStorage;
  16. using System.ComponentModel;
  17. namespace WindowsPhone.Recipes.Push.Client
  18. {
  19. public sealed class PushContext : INotifyPropertyChanged
  20. {
  21. #region Fields
  22. private readonly IsolatedStorageSettings Settings = IsolatedStorageSettings.ApplicationSettings;
  23. private static PushContext _current;
  24. private bool _isConnected;
  25. #endregion
  26. #region Properties
  27. private Dispatcher Dispatcher { get; set; }
  28. public string ChannelName { get; private set; }
  29. public string ServiceName { get; private set; }
  30. public IList<Uri> AllowedDomains { get; private set; }
  31. public HttpNotificationChannel NotificationChannel { get; private set; }
  32. public static PushContext Current
  33. {
  34. get { return _current; }
  35. }
  36. public bool IsConnected
  37. {
  38. get { return _isConnected; }
  39. set
  40. {
  41. if (_isConnected != value)
  42. {
  43. _isConnected = value;
  44. NotifyPropertyChanged("IsConnected");
  45. }
  46. }
  47. }
  48. public bool IsPushEnabled
  49. {
  50. get { return GetOrCreate<bool>("PushContext.IsPushEnabled", false); }
  51. set
  52. {
  53. SetOrCreate("PushContext.IsPushEnabled", value);
  54. UpdateNotificationBindings();
  55. NotifyPropertyChanged("IsPushEnabled");
  56. }
  57. }
  58. public bool IsTileEnabled
  59. {
  60. get { return GetOrCreate<bool>("PushContext.IsTileEnabled", true); }
  61. set
  62. {
  63. SetOrCreate("PushContext.IsTileEnabled", value);
  64. UpdateNotificationBindings();
  65. NotifyPropertyChanged("IsTileEnabled");
  66. }
  67. }
  68. public bool IsToastEnabled
  69. {
  70. get { return GetOrCreate<bool>("PushContext.IsToastEnabled", true); }
  71. set
  72. {
  73. SetOrCreate("PushContext.IsToastEnabled", value);
  74. UpdateNotificationBindings();
  75. NotifyPropertyChanged("IsToastEnabled");
  76. }
  77. }
  78. public bool IsRawEnabled
  79. {
  80. get { return GetOrCreate<bool>("PushContext.IsRawEnabled", true); }
  81. set
  82. {
  83. SetOrCreate("PushContext.IsRawEnabled", value);
  84. NotifyPropertyChanged("IsRawEnabled");
  85. }
  86. }
  87. #endregion
  88. #region Events
  89. public event EventHandler<PushContextErrorEventArgs> Error;
  90. public event EventHandler<PushContextEventArgs> ChannelPrepared;
  91. public event EventHandler<HttpNotificationEventArgs> RawNotification;
  92. public event PropertyChangedEventHandler PropertyChanged = delegate { };
  93. #endregion
  94. #region Ctor
  95. public PushContext(string channelName, string serviceName, IList<Uri> allowedDomains, Dispatcher dispatcher)
  96. {
  97. if (_current != null)
  98. {
  99. throw new InvalidOperationException("There should be no more than one push context.");
  100. }
  101. ChannelName = channelName;
  102. ServiceName = serviceName;
  103. AllowedDomains = allowedDomains;
  104. Dispatcher = dispatcher;
  105. _current = this;
  106. }
  107. #endregion
  108. #region Public Methods
  109. public void Connect(Action<HttpNotificationChannel> prepared)
  110. {
  111. if (IsConnected)
  112. {
  113. prepared(NotificationChannel);
  114. return;
  115. }
  116. try
  117. {
  118. // First, try to pick up an existing channel.
  119. NotificationChannel = HttpNotificationChannel.Find(ChannelName);
  120. if (NotificationChannel == null)
  121. {
  122. // Create new channel and subscribe events.
  123. CreateChannel(prepared);
  124. }
  125. else
  126. {
  127. // Channel exists, no need to create a new one.
  128. SubscribeToNotificationEvents();
  129. PrepareChannel(prepared);
  130. }
  131. IsConnected = true;
  132. }
  133. catch (Exception ex)
  134. {
  135. OnError(ex);
  136. }
  137. }
  138. public void Disconnect()
  139. {
  140. if (!IsConnected)
  141. {
  142. return;
  143. }
  144. try
  145. {
  146. if (NotificationChannel != null)
  147. {
  148. UnbindFromTileNotifications();
  149. UnbindFromToastNotifications();
  150. NotificationChannel.Close();
  151. }
  152. }
  153. catch (Exception ex)
  154. {
  155. OnError(ex);
  156. }
  157. finally
  158. {
  159. NotificationChannel = null;
  160. IsConnected = false;
  161. }
  162. }
  163. #endregion
  164. #region Privates
  165. /// <summary>
  166. /// Create channel, subscribe to channel events and open the channel.
  167. /// </summary>
  168. private void CreateChannel(Action<HttpNotificationChannel> prepared)
  169. {
  170. // Create a new channel.
  171. NotificationChannel = new HttpNotificationChannel(ChannelName, ServiceName);
  172. // Register to UriUpdated event. This occurs when channel successfully opens.
  173. NotificationChannel.ChannelUriUpdated += (s, e) => Dispatcher.BeginInvoke(() => PrepareChannel(prepared));
  174. SubscribeToNotificationEvents();
  175. // Trying to Open the channel.
  176. NotificationChannel.Open();
  177. }
  178. private void SubscribeToNotificationEvents()
  179. {
  180. // Register to raw notifications.
  181. NotificationChannel.HttpNotificationReceived += (s, e) =>
  182. {
  183. if (IsPushEnabled & IsRawEnabled)
  184. {
  185. Dispatcher.BeginInvoke(() => OnRawNotification(e));
  186. }
  187. };
  188. }
  189. private void OnRawNotification(HttpNotificationEventArgs e)
  190. {
  191. if (RawNotification != null)
  192. {
  193. RawNotification(this, e);
  194. }
  195. }
  196. private void PrepareChannel(Action<HttpNotificationChannel> prepared)
  197. {
  198. try
  199. {
  200. // OnChannelPrepared(new PushContextEventArgs(NotificationChannel));
  201. prepared(NotificationChannel);
  202. UpdateNotificationBindings();
  203. }
  204. catch (Exception ex)
  205. {
  206. OnError(ex);
  207. }
  208. }
  209. private void OnError(Exception exception)
  210. {
  211. if (Error != null)
  212. {
  213. Error(this, new PushContextErrorEventArgs(exception));
  214. }
  215. }
  216. private void OnChannelPrepared(PushContextEventArgs args)
  217. {
  218. if (ChannelPrepared != null)
  219. {
  220. ChannelPrepared(this, args);
  221. }
  222. }
  223. private void BindToTileNotifications()
  224. {
  225. try
  226. {
  227. if (NotificationChannel != null && !NotificationChannel.IsShellTileBound)
  228. {
  229. var listOfAllowedDomains = new Collection<Uri>(AllowedDomains);
  230. NotificationChannel.BindToShellTile(listOfAllowedDomains);
  231. }
  232. }
  233. catch (Exception ex)
  234. {
  235. OnError(ex);
  236. }
  237. }
  238. private void BindToToastNotifications()
  239. {
  240. try
  241. {
  242. if (NotificationChannel != null && !NotificationChannel.IsShellToastBound)
  243. {
  244. NotificationChannel.BindToShellToast();
  245. }
  246. }
  247. catch (Exception ex)
  248. {
  249. OnError(ex);
  250. }
  251. }
  252. private void UnbindFromTileNotifications()
  253. {
  254. try
  255. {
  256. if (NotificationChannel.IsShellTileBound)
  257. {
  258. NotificationChannel.UnbindToShellTile();
  259. }
  260. }
  261. catch (Exception ex)
  262. {
  263. OnError(ex);
  264. }
  265. }
  266. private void UnbindFromToastNotifications()
  267. {
  268. try
  269. {
  270. if (NotificationChannel.IsShellToastBound)
  271. {
  272. NotificationChannel.UnbindToShellToast();
  273. }
  274. }
  275. catch (Exception ex)
  276. {
  277. OnError(ex);
  278. }
  279. }
  280. private void UpdateNotificationBindings()
  281. {
  282. if (IsPushEnabled && IsTileEnabled)
  283. {
  284. BindToTileNotifications();
  285. }
  286. else
  287. {
  288. UnbindFromTileNotifications();
  289. }
  290. if (IsPushEnabled && IsToastEnabled)
  291. {
  292. BindToToastNotifications();
  293. }
  294. else
  295. {
  296. UnbindFromToastNotifications();
  297. }
  298. }
  299. private T GetOrCreate<T>(string key, T defaultValue = default(T))
  300. {
  301. T value;
  302. if (Settings.TryGetValue(key, out value))
  303. {
  304. return value;
  305. }
  306. return defaultValue;
  307. }
  308. private void SetOrCreate<T>(string key, T value)
  309. {
  310. Settings[key] = value;
  311. }
  312. private void NotifyPropertyChanged(string propertyName)
  313. {
  314. PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
  315. }
  316. #endregion
  317. }
  318. }