private static async Task UpdatePrimaryTileNotificationsBlocking(AccountDataItem account, AccountDataStore data) { try { Debug.WriteLine("Updating Primary Tile"); TileUpdater tileUpdater = TileUpdateManager.CreateTileUpdaterForApplication(); if (account == null || account.MainTileSettings.IsDisabled()) { ClearScheduledNotifications(tileUpdater); tileUpdater.Clear(); return; } DateTime todayInLocal = DateTime.Today; var allUpcoming = await getAllUpcomingBlocking(account, data, DateTime.SpecifyKind(todayInLocal, DateTimeKind.Utc), account.MainTileSettings); UpdateUpcomingTile(tileUpdater, allUpcoming, todayInLocal, UpcomingTileType.PrimaryTile, account.MainTileSettings); } catch (Exception ex) { if (!UWPExceptionHelper.TrackIfNotificationsIssue(ex, "Tiles")) { throw ex; } } }
private static async Task UpdateTileAsync(SecondaryTile tile, AccountDataItem account, AccountDataStore data, Guid classId) { try { DateTime todayInLocal = DateTime.Today; // Get the class tile settings ClassTileSettings settings = await account.GetClassTileSettings(classId); ClassData classData = await LoadDataAsync(data, classId, DateTime.SpecifyKind(todayInLocal, DateTimeKind.Utc), settings); // If classData was null, that means the class wasn't found, so we should delete the tile if (classData == null) { await tile.RequestDeleteAsync(); return; } bool changed = false; string desiredName = GetTrimmedClassName(classData.Class.Name); Color desiredColor; if (settings.CustomColor != null) desiredColor = ColorTools.GetColor(settings.CustomColor); else desiredColor = ColorTools.GetColor(classData.Class.Color); if (!tile.DisplayName.Equals(desiredName)) { changed = true; tile.DisplayName = desiredName; } if (!tile.VisualElements.BackgroundColor.Equals(desiredColor)) { changed = true; tile.VisualElements.BackgroundColor = desiredColor; } if (changed) await tile.UpdateAsync(); var updater = TileUpdateManager.CreateTileUpdaterForSecondaryTile(tile.TileId); UpdateUpcomingTile(updater, classData.AllUpcoming, todayInLocal, UpcomingTileType.ClassTile, settings); } catch (Exception ex) { if (!UWPExceptionHelper.TrackIfNotificationsIssue(ex, "Tiles")) { throw ex; } } }
private static async Task UpdateScheduleTile(string tileId, AccountDataItem account, AccountDataStore data) { try { if (!SecondaryTile.Exists(tileId)) { return; } Debug.WriteLine("Updating Secondary Schedule Tile"); TileUpdater updater; try { updater = TileUpdateManager.CreateTileUpdaterForSecondaryTile(tileId); } catch { return; } TileHelper.ClearScheduledNotifications(updater); bool sentNotification = false; var notifications = await GenerateTileNotificationContentsAsync(account); foreach (var n in notifications) { Schedule(updater, n.Content, n.DeliveryTime ?? DateTime.Now, n.ExpirationTime); sentNotification = true; } if (!sentNotification) { updater.Clear(); } } catch (Exception ex) { if (!UWPExceptionHelper.TrackIfNotificationsIssue(ex, "Tiles") && !UWPExceptionHelper.TrackIfElementNotFound(ex, "Tiles")) { throw ex; } } }
/// <summary> /// Invoked when application execution is being suspended. Application state is saved /// without knowing whether the application will be terminated or resumed with the contents /// of memory still intact. /// </summary> /// <param name="sender">The source of the suspend request.</param> /// <param name="e">Details about the suspend request.</param> private void OnSuspending(object sender, SuspendingEventArgs e) { var deferral = e.SuspendingOperation.GetDeferral(); //NotificationsExtensions.Toasts.ToastContent c = new NotificationsExtensions.Toasts.ToastContent() //{ // Visual = new NotificationsExtensions.Toasts.ToastVisual() // { // TitleText = new NotificationsExtensions.Toasts.ToastText() // { // Text = "Suspended" // } // } //}; //Windows.UI.Notifications.ToastNotificationManager.CreateToastNotifier().Show(new Windows.UI.Notifications.ToastNotification(c.GetXml())); //TODO: Save application state and stop any background activity // TODO: Cancel any current syncs // Register the tasks try { // Make sure none are registered (they should have already been unregistered) UnregisterAllBackgroundTasks(); RegisterInfrequentBackgroundTask(); RegisterRawPushBackgroundTask(); } catch (Exception ex) { if (UWPExceptionHelper.TrackIfRpcServerUnavailable(ex, "RegisterBgTasks")) { } else if (UWPExceptionHelper.TrackIfPathInvalid(ex, "RegisterBgTasks")) { } else { TelemetryExtension.Current?.TrackException(ex); } } deferral.Complete(); }
protected override Task ActuallyClearReminders(Guid localAccountId) { try { ClearReminders(localAccountId, CancellationToken.None); } catch (Exception ex) { if (UWPExceptionHelper.TrackIfNotificationsIssue(ex, nameof(ActuallyClearReminders))) { } else { TelemetryExtension.Current?.TrackException(ex); Debug.WriteLine("ClearReminders failed - " + ex.ToString()); } } return(Task.FromResult(true)); }
private static void RegisterRawPushBackgroundTask() { try { var builder = CreateBackgroundTaskBuilder("RawPushBackgroundTask"); // Trigger on raw push received builder.SetTrigger(new PushNotificationTrigger()); // Make sure internet available when triggered builder.AddCondition(new SystemCondition(SystemConditionType.InternetAvailable)); builder.Register(); } catch (Exception ex) { if (!UWPExceptionHelper.TrackIfNotificationsIssue(ex, nameof(RegisterRawPushBackgroundTask))) { TelemetryExtension.Current?.TrackException(ex); } } }
/// <summary> /// Runs on a new thread /// </summary> /// <param name="account"></param> /// <param name="data"></param> protected override async Task ActuallyResetReminders(AccountDataItem account, AccountDataStore data) { try { await System.Threading.Tasks.Task.Run(async delegate { await ResetReminders(account, CancellationToken.None); }); } catch (OperationCanceledException) { } catch (Exception ex) { if (UWPExceptionHelper.TrackIfNotificationsIssue(ex, "ResetReminders")) { } else { TelemetryExtension.Current?.TrackException(ex); Debug.WriteLine("ResetReminders failed - " + ex.ToString()); } } }
/// <summary> /// /// </summary> /// <param name="updater"></param> /// <param name="allUpcoming">This list will be manipulated. Should already be sorted.</param> /// <param name="todayInLocal"></param> /// <param name="type"></param> internal static void UpdateUpcomingTile(TileUpdater updater, List <BaseViewItemHomeworkExam> allUpcoming, DateTime todayInLocal, UpcomingTileType type, BaseUpcomingTileSettings tileSettings) { try { // Clear all scheduled notifications ClearScheduledNotifications(updater); List <ItemsOnDay> groupedByDay = GroupByDay(allUpcoming); bool veryFirst = true; DateTime today = todayInLocal; const int daysInAdvance = 5; for (int i = 0; i < daysInAdvance; i++, today = today.AddDays(1)) { DateTime todayAsUtc = DateTime.SpecifyKind(today, DateTimeKind.Utc); // Remove any exams that are past "today" for (int x = 0; x < groupedByDay.Count; x++) { groupedByDay[x].Items.RemoveAll(item => item is ViewItemExam && item.Date < today); if (groupedByDay[x].Items.Count == 0) { groupedByDay.RemoveAt(x); x--; } } DateTime dateToStartFrom = tileSettings.GetDateToStartDisplayingOn(todayAsUtc); // Remove any day groups that are older than the user's chosen max date range to display while (groupedByDay.Count > 0 && groupedByDay[0].DateInUtc < dateToStartFrom) { groupedByDay.RemoveAt(0); } // If there's a today group Dictionary <Guid, DateTime> identifiersAndEndTimes = new Dictionary <Guid, DateTime>(); var todayGroup = groupedByDay.FirstOrDefault(g => g.DateInUtc == todayAsUtc); if (todayGroup != null) { // That means we'll need to check if there's exams/events, and if so, we need to update the tile // after the event is over, rather than waiting till the day is over // First we need to create a mapping of the item end times and their indexes, because we need to make sure that we're removing // in order of the earliest end times (right now items are sorted by their start times, so not necessarily correct order based on end times) // We also need to be aware that an event could potentially span multiple days... foreach (var item in todayGroup.Items) { // We ignore "all day" items which end at 11:59:59 DateTime endTime; if (item is ViewItemExam && item.TryGetEndDateWithTime(out endTime) && endTime.TimeOfDay != new TimeSpan(23, 59, 59)) { identifiersAndEndTimes[item.Identifier] = endTime; } } // Remove any events that have already expired (so that the first update doesn't include them) //if (veryFirst) //{ // while (true) // { // if (identifiersAndEndTimes.Count > 0) // { // DateTime minEndTime = identifiersAndEndTimes.Values.Min(); // // If it's already expired, we remove it // if (minEndTime < DateTime.Now) // { // Guid[] identifiersToRemove = identifiersAndEndTimes.Where(p => p.Value == minEndTime).Select(p => p.Key).ToArray(); // foreach (var id in identifiersToRemove) // { // identifiersAndEndTimes.Remove(id); // } // // Remove those events // todayGroup.Items.RemoveAll(x => identifiersToRemove.Contains(x.Identifier)); // // If we've removed all for that day // if (todayGroup.Items.Count == 0) // { // // Remove the group // groupedByDay.Remove(todayGroup); // } // } // } // break; // } //} } DateTime deliveryTime = today; do { if (deliveryTime.Date != today.Date) { // If an event ended up spanning multiple days causing delivery time to be past today, we just won't add it. // In the future when we add support for multiple day events, we'll have to actually handle this... otherwise when the // event expires, the tile won't update correctly on that future day. break; } // On all items but the last, we use the friendly date XmlDocument tile = GenerateUpcomingTileNotificationContent( groupedByDay: groupedByDay, todayAsUtc: todayAsUtc, useFriendlyDates: i != daysInAdvance - 1, type: type); if (tile == null) { if (veryFirst) { updater.Clear(); } return; } DateTime thisDeliveryTime = deliveryTime; DateTime expirationTime = today.AddDays(1); bool hasAdditionalOnThisDay = false; // Pick off earliest ending time if there are any left if (identifiersAndEndTimes.Count > 0) { DateTime minEndTime = identifiersAndEndTimes.Values.Min(); Guid[] identifiersToRemove = identifiersAndEndTimes.Where(p => p.Value == minEndTime).Select(p => p.Key).ToArray(); foreach (var id in identifiersToRemove) { identifiersAndEndTimes.Remove(id); } // Assign the expiration time and the next delivery time to be this end time if (minEndTime > deliveryTime) { expirationTime = minEndTime; deliveryTime = minEndTime; // Setting this for the NEXT notification } // Remove those events todayGroup.Items.RemoveAll(x => identifiersToRemove.Contains(x.Identifier)); // If we've removed all for that day if (todayGroup.Items.Count == 0) { // Remove the group groupedByDay.Remove(todayGroup); } // Otherwise else { // We have more on this day that we'll have to repeat the loop for hasAdditionalOnThisDay = true; } } // If we don't have additional on this day, we can remove today since we're done with it, // which will ensure we correctly set expiration time if there's nothing else left if (!hasAdditionalOnThisDay && todayGroup != null) { groupedByDay.Remove(todayGroup); } if (veryFirst || thisDeliveryTime < DateTime.Now.AddSeconds(5)) { TileNotification currNotif = new TileNotification(tile); // Only assign expiration time if we have no items left if (groupedByDay.Count == 0) { currNotif.ExpirationTime = expirationTime; } updater.Update(currNotif); veryFirst = false; } else { ScheduledTileNotification s = new ScheduledTileNotification(tile, thisDeliveryTime); // Only assign expiration time if we have no items left if (groupedByDay.Count == 0) { s.ExpirationTime = expirationTime; } updater.AddToSchedule(s); } if (!hasAdditionalOnThisDay) { break; } } while (true); } } catch (Exception ex) { if (!UWPExceptionHelper.TrackIfNotificationsIssue(ex, "Tiles")) { throw ex; } } }
protected override async System.Threading.Tasks.Task OnLaunchedOrActivated(IActivatedEventArgs e) { try { #if DEBUG //if (System.Diagnostics.Debugger.IsAttached) //{ // this.DebugSettings.EnableFrameRateCounter = true; //} #endif // Register background tasks if (!_registeredBackgroundTasks) { try { // Make sure none are registered (they should have already been unregistered) UnregisterAllBackgroundTasks(); RegisterInfrequentBackgroundTask(); RegisterRawPushBackgroundTask(); RegisterToastBackgroundTask(); } catch (Exception ex) { if (UWPExceptionHelper.TrackIfRpcServerUnavailable(ex, "RegisterBgTasks")) { } else if (UWPExceptionHelper.TrackIfPathInvalid(ex, "RegisterBgTasks")) { } else { TelemetryExtension.Current?.TrackException(ex); } } _registeredBackgroundTasks = true; } // Wait for initialization to complete, to ensure we don't accidently add multiple windows // Although right now we don't even do any async tasks, so this will be useless await PowerPlannerApp.InitializeTask; MainAppWindow mainAppWindow; // If no windows, need to register window mainAppWindow = PowerPlannerApp.Current.Windows.OfType <MainAppWindow>().FirstOrDefault(); if (mainAppWindow == null) { // This configures the view models, does NOT call Activate yet var nativeWindow = new NativeUwpAppWindow(); mainAppWindow = new MainAppWindow(); await PowerPlannerApp.Current.RegisterWindowAsync(mainAppWindow, nativeWindow); if (PowerPlannerApp.Current.Windows.Count > 1) { throw new Exception("There are more than 1 windows registered"); } } if (e is LaunchActivatedEventArgs) { var launchEventArgs = e as LaunchActivatedEventArgs; var launchContext = !object.Equals(launchEventArgs.TileId, "App") ? LaunchSurface.SecondaryTile : LaunchSurface.Normal; if (launchContext == LaunchSurface.Normal) { // Track whether was launched from primary tile if (ApiInformation.IsPropertyPresent(typeof(LaunchActivatedEventArgs).FullName, nameof(LaunchActivatedEventArgs.TileActivatedInfo))) { if (launchEventArgs.TileActivatedInfo != null) { launchContext = LaunchSurface.PrimaryTile; } } } await HandleArguments(mainAppWindow, launchEventArgs.Arguments, launchContext); } else if (e is ToastNotificationActivatedEventArgs) { var args = e as ToastNotificationActivatedEventArgs; await HandleArguments(mainAppWindow, args.Argument, LaunchSurface.Toast); } else if (e is ProtocolActivatedEventArgs) { var protocolEventArgs = e as ProtocolActivatedEventArgs; if (!string.IsNullOrWhiteSpace(protocolEventArgs.Uri.PathAndQuery) && protocolEventArgs.Uri.PathAndQuery.StartsWith("?")) { await HandleArguments(mainAppWindow, protocolEventArgs.Uri.PathAndQuery.Substring(1), LaunchSurface.Uri); } } else if (e is AppointmentsProviderShowAppointmentDetailsActivatedEventArgs) { // Note that this code is essentially deprecated and doesn't get hit... Uri launch happens instead var showDetailsArgs = e as AppointmentsProviderShowAppointmentDetailsActivatedEventArgs; try { AppointmentsHelper.RoamingIdData data = AppointmentsHelper.RoamingIdData.FromString(showDetailsArgs.RoamingId); string finalArgs = null; switch (data.ItemType) { case ItemType.Schedule: finalArgs = new ViewScheduleArguments() { LocalAccountId = data.LocalAccountId }.SerializeToString(); break; case ItemType.MegaItem: finalArgs = new ViewTaskArguments() { LocalAccountId = data.LocalAccountId, ItemId = data.Identifier }.SerializeToString(); break; } if (finalArgs != null) { await HandleArguments(mainAppWindow, finalArgs, LaunchSurface.Calendar); } } catch (Exception ex) { TelemetryExtension.Current?.TrackException(ex); } } if (mainAppWindow.GetViewModel().Content == null) { await mainAppWindow.GetViewModel().HandleNormalLaunchActivation(); } Window.Current.Activate(); // Listen to window activation changes Window.Current.Activated += Current_Activated; // Set up the default window properties ConfigureWindowProperties(); // Set up the sharing support ConfigureDataTransferManager(); // Display updates HandleVersionChange(); } catch (Exception ex) { TelemetryExtension.Current?.TrackException(ex); } }