Commit 83940968 by Sebastián Katzer

Added support for repeating notifications on Windows (WIP)

parent 1552ffc9
......@@ -347,14 +347,18 @@ exports.clone = function (obj) {
* @return [ LocalNotification.Options ]
*/
exports.parseOptions = function (obj) {
var opts = new LocalNotification.Options();
var opts = new LocalNotification.Options(),
ignore = ['actions', 'trigger'];
for (var prop in opts) {
if (prop != 'actions' && obj[prop]) {
if (!ignore.includes(prop) && obj[prop]) {
opts[prop] = obj[prop];
}
}
var trigger = exports.parseTrigger(obj);
opts.trigger = trigger;
var actions = exports.parseActions(obj);
opts.actions = actions;
......@@ -362,6 +366,28 @@ exports.parseOptions = function (obj) {
};
/**
* Parse trigger spec into instance of prefered type.
*
* @param [ Object ] obj The notification options map.
*
* @return [ LocalNotification.Trigger ]
*/
exports.parseTrigger = function (obj) {
var trigger = new LocalNotification.Trigger(),
spec = obj.trigger, val;
if (!spec) return trigger;
for (var prop in trigger) {
val = spec[prop];
if (!val) continue;
trigger[prop] = prop == 'every' ? val.toString() : val;
}
return trigger;
};
/**
* Parse action specs into instances of prefered types.
*
* @param [ Object ] obj The notification options map.
......
......@@ -30,22 +30,27 @@ namespace LocalNotificationProxy.LocalNotification
/// Initializes a new instance of the <see cref="Builder"/> class.
/// </summary>
/// <param name="options">Notification properties to set.</param>
internal Builder(Options options)
public Builder(Options options)
{
this.Content = new Notification(options);
}
/// <summary>
/// Gets content
/// Gets the content.
/// </summary>
public Notification Content { get; private set; }
/// <summary>
/// Gets options
/// Gets the options.
/// </summary>
private Options Options { get => this.Content.Options; }
/// <summary>
/// Gets the trigger.
/// </summary>
private Trigger Trigger { get => this.Options.Trigger; }
/// <summary>
/// Build a toast notification specified by the options.
/// </summary>
/// <returns>A fully configured toast notification instance.</returns>
......@@ -60,6 +65,17 @@ namespace LocalNotificationProxy.LocalNotification
}
/// <summary>
/// If there is at least one more toast variant to build.
/// </summary>
/// <returns>True if there are more toasts to build.</returns>
public bool HasNext() => this.Trigger.Count > this.Trigger.Occurrence;
/// <summary>
/// Moves the flag to the next toast variant.
/// </summary>
public void MoveNext() => this.Trigger.Occurrence += this.HasNext() ? 1 : 0;
/// <summary>
/// Gets the initialize skeleton for a toast notification.
/// </summary>
/// <returns>Basic skeleton with sound, image and text.</returns>
......@@ -137,21 +153,16 @@ namespace LocalNotificationProxy.LocalNotification
{
var xml = toast.GetXml();
var at = this.Content.Date;
ScheduledToastNotification notification;
var notification = new ScheduledToastNotification(xml, at);
if (this.Content.IsRepeating())
{
var interval = this.Content.Interval;
notification = new ScheduledToastNotification(xml, at, interval, 5);
}
else
notification.Id = this.Content.Id;
notification.Tag = this.Options.Id.ToString();
if (this.Trigger.Occurrence > 1)
{
notification = new ScheduledToastNotification(xml, at);
notification.Group = notification.Tag;
}
notification.Id = this.Options.ID.ToString();
notification.Tag = notification.Id;
return notification;
}
}
......
......@@ -46,8 +46,15 @@ namespace LocalNotificationProxy.LocalNotification
{
foreach (Options options in notifications)
{
var toast = new Builder(options).Build();
ToastNotifier.AddToSchedule(toast);
var builder = new Builder(options);
ScheduledToastNotification toast;
do
{
toast = builder.Build();
ToastNotifier.AddToSchedule(toast);
builder.MoveNext();
} while (builder.HasNext());
}
}
......@@ -98,29 +105,29 @@ namespace LocalNotificationProxy.LocalNotification
public List<Options> Cancel(int[] ids)
{
var scheduled = ToastNotifier.GetScheduledToastNotifications();
var tags = new List<int>(ids);
var toRemove = new List<int>(ids);
var toClear = new List<int>(ids);
var toasts = new List<Options>();
foreach (var toast in scheduled)
{
var id = int.Parse(toast.Tag);
if (tags.Contains(id))
if (toRemove.Contains(id))
{
tags.Remove(id);
toasts.Add(new Notification(toast).Options);
toClear.Remove(id);
ToastNotifier.RemoveFromSchedule(toast);
}
if (tags.Count == 0)
{
break;
if (!this.IsPhantom(toast))
{
toasts.Add(new Notification(toast).Options);
}
}
}
if (tags.Count > 0)
if (toClear.Count > 0)
{
toasts.AddRange(this.Clear(tags.ToArray()));
toasts.AddRange(this.Clear(toClear.ToArray()));
}
return toasts;
......@@ -165,7 +172,10 @@ namespace LocalNotificationProxy.LocalNotification
foreach (var toast in toasts)
{
ids.Add(int.Parse(toast.Tag));
if (!this.IsPhantom(toast))
{
ids.Add(int.Parse(toast.Tag));
}
}
}
......@@ -175,7 +185,10 @@ namespace LocalNotificationProxy.LocalNotification
foreach (var toast in toasts)
{
ids.Add(int.Parse(toast.Tag));
if (!this.IsPhantom(toast))
{
ids.Add(int.Parse(toast.Tag));
}
}
}
......@@ -206,7 +219,10 @@ namespace LocalNotificationProxy.LocalNotification
foreach (var toast in toasts)
{
notifications.Add(new Notification(toast));
if (!this.IsPhantom(toast))
{
notifications.Add(new Notification(toast));
}
}
}
......@@ -216,7 +232,10 @@ namespace LocalNotificationProxy.LocalNotification
foreach (var toast in toasts)
{
notifications.Add(new Notification(toast));
if (!this.IsPhantom(toast))
{
notifications.Add(new Notification(toast));
}
}
}
......@@ -358,5 +377,25 @@ namespace LocalNotificationProxy.LocalNotification
return Notification.Type.Unknown;
}
/// <summary>
/// If the specified toast is only for internal reason.
/// </summary>
/// <param name="toast">The toast notification to test for.</param>
/// <returns>True if its an internal ("phantom") one.</returns>
private bool IsPhantom(ToastNotification toast)
{
return toast.Group.Length > 0;
}
/// <summary>
/// If the specified toast is only for internal reason.
/// </summary>
/// <param name="toast">The toast notification to test for.</param>
/// <returns>True if its an internal ("phantom") one.</returns>
private bool IsPhantom(ScheduledToastNotification toast)
{
return toast.Group.Length > 0;
}
}
}
......@@ -49,9 +49,17 @@
All, Scheduled, Triggered, Unknown
}
/// <summary>
/// Gets the wrapped notification options.
/// </summary>
public Options Options { get; private set; }
/// <summary>
/// Gets the unique identifier for the toast.
/// </summary>
public string Id => $"{this.Options.Id}#{this.Options.Trigger.Occurrence}";
/// <summary>
/// Gets a ToastAudio object based on the specified sound uri.
/// </summary>
public ToastAudio Sound
......@@ -260,9 +268,13 @@
{
get
{
var date = DateTimeOffset.FromUnixTimeMilliseconds(this.Options.At * 1000).LocalDateTime;
var trigger = this.Options.Trigger;
var time = trigger.At;
var date = DateTimeOffset.FromUnixTimeMilliseconds(time * 1000).LocalDateTime;
var minDate = DateTime.Now.AddSeconds(0.1);
date = date.AddTicks(this.Interval.Ticks * (trigger.Occurrence - 1));
return (date < minDate) ? minDate : date;
}
}
......@@ -274,7 +286,7 @@
{
get
{
switch (this.Options.Every)
switch (this.Options.Trigger.Every)
{
case "minute":
return new TimeSpan(TimeSpan.TicksPerMinute);
......@@ -296,16 +308,5 @@
{
return this.Options.GetXml();
}
/// <summary>
/// If the notification shall be repeating.
/// </summary>
/// <returns>True if the Every property has some value.</returns>
public bool IsRepeating()
{
var every = this.Options.Every;
return every != null && every.Length > 0 && !every.Equals("0");
}
}
}
......@@ -33,7 +33,7 @@ namespace LocalNotificationProxy.LocalNotification
/// <summary>
/// Gets or sets notification ID.
/// </summary>
public int ID { get; set; }
public int Id { get; set; }
/// <summary>
/// Gets or sets notification title.
......@@ -61,14 +61,9 @@ namespace LocalNotificationProxy.LocalNotification
public string Image { get; set; }
/// <summary>
/// Gets or sets the notification fire date.
/// Gets or sets the notification trigger.
/// </summary>
public long At { get; set; }
/// <summary>
/// Gets or sets the notification repeat interval.
/// </summary>
public string Every { get; set; }
public Trigger Trigger { get; set; }
/// <summary>
/// Gets or sets the notification user data.
......@@ -98,9 +93,8 @@ namespace LocalNotificationProxy.LocalNotification
var options = new Options();
var node = doc.DocumentElement;
options.ID = int.Parse(node.GetAttribute("id"));
options.Id = int.Parse(node.GetAttribute("id"));
options.Badge = int.Parse(node.GetAttribute("badge"));
options.At = int.Parse(node.GetAttribute("at"));
if (node.GetAttributeNode("text") != null)
{
......@@ -122,11 +116,6 @@ namespace LocalNotificationProxy.LocalNotification
options.Image = node.GetAttribute("image");
}
if (node.GetAttributeNode("every") != null)
{
options.Every = node.GetAttribute("every");
}
if (node.GetAttributeNode("data") != null)
{
options.Data = node.GetAttribute("data");
......@@ -158,9 +147,8 @@ namespace LocalNotificationProxy.LocalNotification
{
var node = new XmlDocument().CreateElement("options");
node.SetAttribute("id", this.ID.ToString());
node.SetAttribute("id", this.Id.ToString());
node.SetAttribute("badge", this.Badge.ToString());
node.SetAttribute("at", this.At.ToString());
if (this.Title != null)
{
......@@ -182,11 +170,6 @@ namespace LocalNotificationProxy.LocalNotification
node.SetAttribute("image", this.Image);
}
if (this.Every != null)
{
node.SetAttribute("every", this.Every);
}
if (this.Data != null)
{
node.SetAttribute("data", this.Data);
......
/*
* Apache 2.0 License
*
* Copyright (c) Sebastian Katzer 2017
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apache License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. Please obtain a copy of the License at
* http://opensource.org/licenses/Apache-2.0/ and read it before using this
* file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*/
namespace LocalNotificationProxy.LocalNotification
{
public sealed class Trigger
{
/// <summary>
/// Gets trigger type.
/// </summary>
public string Type { get; } = "calendar";
/// <summary>
/// Gets or sets trigger date.
/// </summary>
public long At { get; set; }
/// <summary>
/// Gets or sets trigger count.
/// </summary>
public int Count { get; set; } = 1;
/// <summary>
/// Gets trigger occurrence.
/// </summary>
public int Occurrence { get; internal set; } = 1;
/// <summary>
/// Gets or sets trigger interval.
/// </summary>
public string Every { get; set; }
}
}
......@@ -118,6 +118,7 @@
<Compile Include="LocalNotification\Input.cs" />
<Compile Include="LocalNotification\Manager.cs" />
<Compile Include="LocalNotification\Options.cs" />
<Compile Include="LocalNotification\Trigger.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
......
{
"version": 2,
"version": 3,
"targets": {
"UAP,Version=v10.0": {
"UAP,Version=v10.0.10240": {
"Microsoft.CSharp/4.0.1": {
"type": "package",
"dependencies": {
......@@ -2173,7 +2173,7 @@
}
}
},
"UAP,Version=v10.0/win10-arm": {
"UAP,Version=v10.0.10240/win10-arm": {
"Microsoft.CSharp/4.0.1": {
"type": "package",
"dependencies": {
......@@ -4428,7 +4428,7 @@
}
}
},
"UAP,Version=v10.0/win10-arm-aot": {
"UAP,Version=v10.0.10240/win10-arm-aot": {
"Microsoft.CSharp/4.0.1": {
"type": "package",
"dependencies": {
......@@ -5006,9 +5006,6 @@
},
"runtime.win10-arm-aot.runtime.native.System.IO.Compression/4.0.1": {
"type": "package",
"compile": {
"runtimes/win10-arm-aot/lib/netcore50/_._": {}
},
"runtime": {
"runtimes/win10-arm-aot/lib/netcore50/clrcompression.dll": {}
}
......@@ -6722,7 +6719,7 @@
}
}
},
"UAP,Version=v10.0/win10-x64": {
"UAP,Version=v10.0.10240/win10-x64": {
"Microsoft.CSharp/4.0.1": {
"type": "package",
"dependencies": {
......@@ -8986,7 +8983,7 @@
}
}
},
"UAP,Version=v10.0/win10-x64-aot": {
"UAP,Version=v10.0.10240/win10-x64-aot": {
"Microsoft.CSharp/4.0.1": {
"type": "package",
"dependencies": {
......@@ -9567,9 +9564,6 @@
},
"runtime.win10-x64-aot.runtime.native.System.IO.Compression/4.0.1": {
"type": "package",
"compile": {
"runtimes/win10-x64-aot/lib/netcore50/_._": {}
},
"runtime": {
"runtimes/win10-x64-aot/lib/netcore50/clrcompression.dll": {}
}
......@@ -11289,7 +11283,7 @@
}
}
},
"UAP,Version=v10.0/win10-x86": {
"UAP,Version=v10.0.10240/win10-x86": {
"Microsoft.CSharp/4.0.1": {
"type": "package",
"dependencies": {
......@@ -13553,7 +13547,7 @@
}
}
},
"UAP,Version=v10.0/win10-x86-aot": {
"UAP,Version=v10.0.10240/win10-x86-aot": {
"Microsoft.CSharp/4.0.1": {
"type": "package",
"dependencies": {
......@@ -14134,9 +14128,6 @@
},
"runtime.win10-x86-aot.runtime.native.System.IO.Compression/4.0.1": {
"type": "package",
"compile": {
"runtimes/win10-x86-aot/lib/netcore50/_._": {}
},
"runtime": {
"runtimes/win10-x86-aot/lib/netcore50/clrcompression.dll": {}
}
......@@ -22044,7 +22035,7 @@
"Microsoft.NETCore.UniversalWindowsPlatform >= 5.2.3",
"StyleCop.Analyzers >= 1.0.0"
],
"UAP,Version=v10.0": []
"UAP,Version=v10.0.10240": []
},
"packageFolders": {
"C:\\Users\\katze\\.nuget\\packages\\": {}
......@@ -22055,14 +22046,24 @@
"projectName": "Microsoft.Toolkit.Uwp.Notifications.UWP",
"projectPath": "Z:\\Documents\\github\\cordova-plugin-local-notifications\\src\\windows\\Microsoft.Toolkit.Uwp.Notifications.UWP\\Microsoft.Toolkit.Uwp.Notifications.UWP.csproj",
"projectJsonPath": "Z:\\Documents\\github\\cordova-plugin-local-notifications\\src\\windows\\Microsoft.Toolkit.Uwp.Notifications.UWP\\project.json",
"projectStyle": "ProjectJson"
"packagesPath": "C:\\Users\\katze\\.nuget\\packages\\",
"outputPath": "Z:\\Documents\\github\\cordova-plugin-local-notifications\\src\\windows\\Microsoft.Toolkit.Uwp.Notifications.UWP\\obj\\",
"projectStyle": "ProjectJson",
"configFilePaths": [
"C:\\Users\\katze\\AppData\\Roaming\\NuGet\\NuGet.Config",
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config"
],
"sources": {
"C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {},
"https://api.nuget.org/v3/index.json": {}
}
},
"dependencies": {
"Microsoft.NETCore.UniversalWindowsPlatform": "5.2.3",
"StyleCop.Analyzers": "1.0.0"
},
"frameworks": {
"uap10.0": {}
"uap10.0.10240": {}
},
"runtimes": {
"win10-arm": {
......
......@@ -213,6 +213,14 @@ exports.convertTrigger = function (options) {
trigger.every = options.every;
}
if (!trigger.count && device.platform == 'windows') {
trigger.count = trigger.every ? 5 : 1;
}
if (trigger.every && device.platform == 'windows') {
trigger.every = trigger.every.toString();
}
if (!isCal) {
trigger.notifyOnEntry = !!trigger.notifyOnEntry;
trigger.notifyOnExit = trigger.notifyOnExit === true;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment