PageRenderTime 52ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/BackgroundSensors/cs/Scenario1_DeviceUse.xaml.cs

https://gitlab.com/Rockyspade/Windows-universal-samples
C# | 341 lines | 203 code | 42 blank | 96 comment | 20 complexity | e0675470ed38c45b3c94c4c53420aa93 MD5 | raw file
  1. //*********************************************************
  2. //
  3. // Copyright (c) Microsoft. All rights reserved.
  4. // This code is licensed under the MIT License (MIT).
  5. // THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
  6. // ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
  7. // IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
  8. // PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
  9. //
  10. //*********************************************************
  11. using System;
  12. using System.Threading.Tasks;
  13. using Windows.ApplicationModel.Background;
  14. using Windows.Devices.Sensors;
  15. using Windows.Foundation;
  16. using Windows.Storage;
  17. using Windows.UI.Core;
  18. using Windows.UI.Xaml;
  19. using Windows.UI.Xaml.Controls;
  20. using Windows.UI.Xaml.Navigation;
  21. namespace SDKTemplate
  22. {
  23. /// <summary>
  24. /// An empty page that can be used on its own or navigated to within a Frame.
  25. /// </summary>
  26. public sealed partial class Scenario1_DeviceUse : Page
  27. {
  28. // A pointer back to the main page. This is needed if you want to call methods in MainPage such
  29. // as NotifyUser()
  30. private MainPage rootPage = MainPage.Current;
  31. private Accelerometer Accelerometer;
  32. private DeviceUseTrigger _deviceUseTrigger;
  33. // Used to register the device use background task
  34. private BackgroundTaskRegistration _deviceUseBackgroundTaskRegistration;
  35. // Used for refreshing the number of samples received when the app is visible
  36. private DispatcherTimer _refreshTimer;
  37. public Scenario1_DeviceUse()
  38. {
  39. this.InitializeComponent();
  40. Accelerometer = Accelerometer.GetDefault();
  41. if (null != Accelerometer)
  42. {
  43. // Save trigger so that we may start the background task later.
  44. // Only one instance of the trigger can exist at a time. Since the trigger does not implement
  45. // IDisposable, it may still be in memory when a new trigger is created.
  46. _deviceUseTrigger = new DeviceUseTrigger();
  47. // Setup a timer to periodically refresh results when the app is visible.
  48. _refreshTimer = new DispatcherTimer()
  49. {
  50. Interval = new TimeSpan(0, 0, 1) // Refresh once every second
  51. };
  52. _refreshTimer.Tick += RefreshTimer_Tick;
  53. }
  54. else
  55. {
  56. rootPage.NotifyUser("No accelerometer found", NotifyType.StatusMessage);
  57. }
  58. }
  59. /// <summary>
  60. /// Invoked when this page is about to be displayed in a Frame.
  61. /// </summary>
  62. /// <param name="e">Event data that describes how this page was reached. The Parameter
  63. /// property is typically used to configure the page.</param>
  64. protected override void OnNavigatedTo(NavigationEventArgs e)
  65. {
  66. // If the background task is active, start the refresh timer and activate the "Disable" button.
  67. // The "IsBackgroundTaskActive" state is set by the background task.
  68. bool isBackgroundTaskActive =
  69. ApplicationData.Current.LocalSettings.Values.ContainsKey("IsBackgroundTaskActive") &&
  70. (bool)ApplicationData.Current.LocalSettings.Values["IsBackgroundTaskActive"];
  71. ScenarioEnableButton.IsEnabled = !isBackgroundTaskActive;
  72. ScenarioDisableButton.IsEnabled = isBackgroundTaskActive;
  73. if (isBackgroundTaskActive)
  74. {
  75. _refreshTimer.Start();
  76. }
  77. // Store a setting for the background task to read
  78. ApplicationData.Current.LocalSettings.Values["IsAppVisible"] = true;
  79. }
  80. /// <summary>
  81. /// Invoked immediately before the Page is unloaded and is no longer the current source of a parent Frame.
  82. /// </summary>
  83. /// <param name="e">
  84. /// Event data that can be examined by overriding code. The event data is representative
  85. /// of the navigation that will unload the current Page unless canceled. The
  86. /// navigation can potentially be canceled by setting Cancel.
  87. /// </param>
  88. protected override void OnNavigatingFrom(NavigatingCancelEventArgs e)
  89. {
  90. Window.Current.VisibilityChanged -= new WindowVisibilityChangedEventHandler(VisibilityChanged);
  91. ApplicationData.Current.LocalSettings.Values["IsAppVisible"] = false;
  92. // The default behavior here is to let the background task continue to run when
  93. // this scenario exits. The background task can be canceled by clicking on the "Disable"
  94. // button the next time the app is navigated to.
  95. // To cancel the background task on scenario exit, uncomment this code.
  96. // if (null != _deviceUseBackgroundTaskRegistration)
  97. // {
  98. // _deviceUseBackgroundTaskRegistration.Unregister(true);
  99. // _deviceUseBackgroundTaskRegistration = null;
  100. // }
  101. base.OnNavigatingFrom(e);
  102. }
  103. /// <summary>
  104. /// This is the event handler for VisibilityChanged events.
  105. /// </summary>
  106. /// <param name="sender"></param>
  107. /// <param name="e">
  108. /// Event data that can be examined for the current visibility state.
  109. /// </param>
  110. private void VisibilityChanged(object sender, VisibilityChangedEventArgs e)
  111. {
  112. if (ScenarioDisableButton.IsEnabled)
  113. {
  114. ApplicationData.Current.LocalSettings.Values["IsAppVisible"] = e.Visible;
  115. if (e.Visible)
  116. {
  117. _refreshTimer.Start();
  118. }
  119. else
  120. {
  121. _refreshTimer.Stop();
  122. }
  123. }
  124. }
  125. /// <summary>
  126. /// This is the click handler for the 'Enable' button.
  127. /// </summary>
  128. /// <param name="sender"></param>
  129. /// <param name="e"></param>
  130. private async void ScenarioEnable(object sender, RoutedEventArgs e)
  131. {
  132. Window.Current.VisibilityChanged += new WindowVisibilityChangedEventHandler(VisibilityChanged);
  133. if (null != Accelerometer)
  134. {
  135. // Make sure this app is allowed to run background tasks.
  136. // RequestAccessAsync must be called on the UI thread.
  137. BackgroundAccessStatus accessStatus = await BackgroundExecutionManager.RequestAccessAsync();
  138. if ((BackgroundAccessStatus.AllowedWithAlwaysOnRealTimeConnectivity == accessStatus) ||
  139. (BackgroundAccessStatus.AllowedMayUseActiveRealTimeConnectivity == accessStatus))
  140. {
  141. if (await StartSensorBackgroundTaskAsync(Accelerometer.DeviceId))
  142. {
  143. _refreshTimer.Start();
  144. ScenarioEnableButton.IsEnabled = false;
  145. ScenarioDisableButton.IsEnabled = true;
  146. }
  147. }
  148. else
  149. {
  150. rootPage.NotifyUser("Background tasks may be disabled for this app", NotifyType.ErrorMessage);
  151. }
  152. }
  153. else
  154. {
  155. rootPage.NotifyUser("No accelerometer found", NotifyType.StatusMessage);
  156. }
  157. }
  158. /// <summary>
  159. /// This is the click handler for the 'Disable' button.
  160. /// </summary>
  161. /// <param name="sender"></param>
  162. /// <param name="e"></param>
  163. private void ScenarioDisable(object sender, RoutedEventArgs e)
  164. {
  165. Window.Current.VisibilityChanged -= new WindowVisibilityChangedEventHandler(VisibilityChanged);
  166. ScenarioEnableButton.IsEnabled = true;
  167. ScenarioDisableButton.IsEnabled = false;
  168. _refreshTimer.Stop();
  169. if (null != _deviceUseBackgroundTaskRegistration)
  170. {
  171. // Cancel and unregister the background task from the current app session.
  172. _deviceUseBackgroundTaskRegistration.Unregister(true);
  173. _deviceUseBackgroundTaskRegistration = null;
  174. }
  175. else
  176. {
  177. // Cancel and unregister the background task from the previous app session.
  178. FindAndCancelExistingBackgroundTask();
  179. }
  180. rootPage.NotifyUser("Background task was canceled", NotifyType.StatusMessage);
  181. }
  182. /// <summary>
  183. /// Starts the sensor background task.
  184. /// </summary>
  185. /// <param name="deviceId">Device Id for the sensor to be used by the task.</param>
  186. /// <param name="e"></param>
  187. /// <returns>True if the task is started successfully.</returns>
  188. private async Task<bool> StartSensorBackgroundTaskAsync(String deviceId)
  189. {
  190. bool started = false;
  191. // Make sure only 1 task is running.
  192. FindAndCancelExistingBackgroundTask();
  193. // Register the background task.
  194. var backgroundTaskBuilder = new BackgroundTaskBuilder()
  195. {
  196. Name = SampleConstants.Scenario1_DeviceUse_TaskName,
  197. TaskEntryPoint = SampleConstants.Scenario1_DeviceUse_TaskEntryPoint
  198. };
  199. backgroundTaskBuilder.SetTrigger(_deviceUseTrigger);
  200. _deviceUseBackgroundTaskRegistration = backgroundTaskBuilder.Register();
  201. // Make sure we're notified when the task completes or if there is an update.
  202. _deviceUseBackgroundTaskRegistration.Completed += new BackgroundTaskCompletedEventHandler(OnBackgroundTaskCompleted);
  203. try
  204. {
  205. // Request a DeviceUse task to use the accelerometer.
  206. DeviceTriggerResult deviceTriggerResult = await _deviceUseTrigger.RequestAsync(deviceId);
  207. switch (deviceTriggerResult)
  208. {
  209. case DeviceTriggerResult.Allowed:
  210. rootPage.NotifyUser("Background task started", NotifyType.StatusMessage);
  211. started = true;
  212. break;
  213. case DeviceTriggerResult.LowBattery:
  214. rootPage.NotifyUser("Insufficient battery to run the background task", NotifyType.ErrorMessage);
  215. break;
  216. case DeviceTriggerResult.DeniedBySystem:
  217. // The system can deny a task request if the system-wide DeviceUse task limit is reached.
  218. rootPage.NotifyUser("The system has denied the background task request", NotifyType.ErrorMessage);
  219. break;
  220. default:
  221. rootPage.NotifyUser("Could not start the background task: " + deviceTriggerResult, NotifyType.ErrorMessage);
  222. break;
  223. }
  224. }
  225. catch (InvalidOperationException)
  226. {
  227. // If toggling quickly between 'Disable' and 'Enable', the previous task
  228. // could still be in the process of cleaning up.
  229. rootPage.NotifyUser("A previous background task is still running, please wait for it to exit", NotifyType.ErrorMessage);
  230. FindAndCancelExistingBackgroundTask();
  231. }
  232. return started;
  233. }
  234. /// <summary>
  235. /// Finds a previously registered background task for this scenario and cancels it (if present)
  236. /// </summary>
  237. private void FindAndCancelExistingBackgroundTask()
  238. {
  239. foreach (var backgroundTask in BackgroundTaskRegistration.AllTasks.Values)
  240. {
  241. if (SampleConstants.Scenario1_DeviceUse_TaskName == backgroundTask.Name)
  242. {
  243. ((BackgroundTaskRegistration)backgroundTask).Unregister(true);
  244. break;
  245. }
  246. }
  247. }
  248. /// <summary>
  249. /// This is the tick handler for the Refresh timer.
  250. /// </summary>
  251. /// <param name="sender"></param>
  252. /// <param name="e"></param>
  253. private void RefreshTimer_Tick(object sender, object e)
  254. {
  255. if (ApplicationData.Current.LocalSettings.Values.ContainsKey("SampleCount"))
  256. {
  257. ulong sampleCount = (ulong)ApplicationData.Current.LocalSettings.Values["SampleCount"];
  258. ScenarioOutputSampleCount.Text = sampleCount.ToString(System.Globalization.CultureInfo.CurrentCulture);
  259. }
  260. else
  261. {
  262. ScenarioOutputSampleCount.Text = "No data";
  263. }
  264. }
  265. /// <summary>
  266. /// This is the background task completion handler.
  267. /// </summary>
  268. /// <param name="sender"></param>
  269. /// <param name="e"></param>
  270. private async void OnBackgroundTaskCompleted(BackgroundTaskRegistration sender, BackgroundTaskCompletedEventArgs e)
  271. {
  272. // Dispatch to the UI thread to display the output.
  273. await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
  274. {
  275. // An exception may be thrown if an error occurs in the background task.
  276. try
  277. {
  278. e.CheckResult();
  279. if (ApplicationData.Current.LocalSettings.Values.ContainsKey("TaskCancelationReason"))
  280. {
  281. string cancelationReason = (string)ApplicationData.Current.LocalSettings.Values["TaskCancelationReason"];
  282. rootPage.NotifyUser("Background task was stopped, reason: " + cancelationReason, NotifyType.StatusMessage);
  283. }
  284. }
  285. catch (Exception ex)
  286. {
  287. rootPage.NotifyUser("Exception in background task: " + ex.Message, NotifyType.ErrorMessage);
  288. }
  289. _refreshTimer.Stop();
  290. });
  291. // Unregister the background task and let the remaining task finish until completion.
  292. if (null != _deviceUseBackgroundTaskRegistration)
  293. {
  294. _deviceUseBackgroundTaskRegistration.Unregister(false);
  295. _deviceUseBackgroundTaskRegistration = null;
  296. }
  297. }
  298. }
  299. }