From 1b60f4869a647a5a421253a28d667f15af798b77 Mon Sep 17 00:00:00 2001 From: hiranya911 Date: Thu, 13 Dec 2018 14:56:21 -0800 Subject: [PATCH 1/2] Implemented AndroidNotification API --- .../Messaging/MessageTest.cs | 85 +++++++- .../FirebaseAdmin/Messaging/AndroidConfig.cs | 9 + .../Messaging/AndroidNotification.cs | 184 ++++++++++++++++++ 3 files changed, 277 insertions(+), 1 deletion(-) create mode 100644 FirebaseAdmin/FirebaseAdmin/Messaging/AndroidNotification.cs diff --git a/FirebaseAdmin/FirebaseAdmin.Tests/Messaging/MessageTest.cs b/FirebaseAdmin/FirebaseAdmin.Tests/Messaging/MessageTest.cs index 0ecc1cab..c7e2dbd4 100644 --- a/FirebaseAdmin/FirebaseAdmin.Tests/Messaging/MessageTest.cs +++ b/FirebaseAdmin/FirebaseAdmin.Tests/Messaging/MessageTest.cs @@ -158,6 +158,21 @@ public void AndroidConfig() { "k1", "v1" }, { "k2", "v2" }, }, + Notification = new AndroidNotification() + { + Title = "title", + Body = "body", + Icon = "icon", + Color = "#112233", + Sound = "sound", + Tag = "tag", + ClickAction = "click-action", + TitleLocKey = "title-loc-key", + TitleLocArgs = new List(){ "arg1", "arg2" }, + BodyLocKey = "body-loc-key", + BodyLocArgs = new List(){ "arg3", "arg4" }, + ChannelId = "channel-id", + }, }, }; var expected = new JObject() @@ -170,7 +185,24 @@ public void AndroidConfig() { "priority", "high" }, { "ttl", "0.010000000s" }, { "restricted_package_name", "test-pkg-name" }, - {"data", new JObject(){{"k1", "v1"}, {"k2", "v2"}}}, + { "data", new JObject(){{"k1", "v1"}, {"k2", "v2"}} }, + { + "notification", new JObject() + { + { "title", "title" }, + { "body", "body" }, + { "icon", "icon" }, + { "color", "#112233" }, + { "sound", "sound" }, + { "tag", "tag" }, + { "click_action", "click-action" }, + { "title_loc_key", "title-loc-key" }, + { "title_loc_args", new JArray(){"arg1", "arg2"} }, + { "body_loc_key", "body-loc-key" }, + { "body_loc_args", new JArray(){"arg3", "arg4"} }, + { "channel_id", "channel-id" }, + } + }, } }, }; @@ -225,6 +257,57 @@ public void AndroidConfigInvalidTTL() Assert.Throws(() => message.Validate()); } + [Fact] + public void AndroidNotificationInvalidColor() + { + var message = new Message() + { + Topic = "test-topic", + AndroidConfig = new AndroidConfig() + { + Notification = new AndroidNotification() + { + Color = "not-a-color" + }, + }, + }; + Assert.Throws(() => message.Validate()); + } + + [Fact] + public void AndroidNotificationInvalidTitleLocArgs() + { + var message = new Message() + { + Topic = "test-topic", + AndroidConfig = new AndroidConfig() + { + Notification = new AndroidNotification() + { + TitleLocArgs = new List(){"arg"}, + }, + }, + }; + Assert.Throws(() => message.Validate()); + } + + [Fact] + public void AndroidNotificationInvalidBodyLocArgs() + { + var message = new Message() + { + Topic = "test-topic", + AndroidConfig = new AndroidConfig() + { + Notification = new AndroidNotification() + { + BodyLocArgs = new List(){"arg"}, + }, + }, + }; + Assert.Throws(() => message.Validate()); + } + private void AssertJsonEquals(JObject expected, Message actual) { var json = NewtonsoftJsonSerializer.Instance.Serialize(actual.Validate()); diff --git a/FirebaseAdmin/FirebaseAdmin/Messaging/AndroidConfig.cs b/FirebaseAdmin/FirebaseAdmin/Messaging/AndroidConfig.cs index ca5be3a1..0e21a9c0 100644 --- a/FirebaseAdmin/FirebaseAdmin/Messaging/AndroidConfig.cs +++ b/FirebaseAdmin/FirebaseAdmin/Messaging/AndroidConfig.cs @@ -53,6 +53,11 @@ public sealed class AndroidConfig /// public IReadOnlyDictionary Data { get; set; } + /// + /// The Android notification to be included in the message. + /// + public AndroidNotification Notification { get; set; } + internal ValidatedAndroidConfig Validate() { return new ValidatedAndroidConfig() @@ -62,6 +67,7 @@ internal ValidatedAndroidConfig Validate() TimeToLive = this.TtlString, RestrictedPackageName = this.RestrictedPackageName, Data = this.Data, + Notification = this.Notification?.Validate(), }; } @@ -125,6 +131,9 @@ internal sealed class ValidatedAndroidConfig [JsonProperty("data")] internal IReadOnlyDictionary Data { get; set; } + + [JsonProperty("notification")] + internal ValidatedAndroidNotification Notification { get; set; } } /// diff --git a/FirebaseAdmin/FirebaseAdmin/Messaging/AndroidNotification.cs b/FirebaseAdmin/FirebaseAdmin/Messaging/AndroidNotification.cs new file mode 100644 index 00000000..868a2f2b --- /dev/null +++ b/FirebaseAdmin/FirebaseAdmin/Messaging/AndroidNotification.cs @@ -0,0 +1,184 @@ +// Copyright 2018, Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; +using Newtonsoft.Json; + +namespace FirebaseAdmin.Messaging +{ + /// + /// Represents the Android-specific notification options that can be included in a + /// . + /// + public sealed class AndroidNotification + { + + /// + /// The title of the Android notification. When provided, overrides the title set + /// via . + /// + public string Title { get; set; } + + /// + /// The title of the Android notification. When provided, overrides the title set + /// via . + /// + public string Body { get; set; } + + /// + /// The icon of the Android notification. + /// + public string Icon { get; set; } + + /// + /// The notification icon color. Must be of the form #RRGGBB. + /// + public string Color { get; set; } + + /// + /// The sound to be played when the device receives the notification. + /// + public string Sound { get; set; } + + /// + /// The notification tag. This is an identifier used to replace existing notifications in + /// the notification drawer. If not specified, each request creates a new notification. + /// + public string Tag { get; set; } + + /// + /// The action associated with a user click on the notification. If specified, an activity + /// with a matching Intent Filter is launched when a user clicks on the notification. + /// + public string ClickAction { get; set; } + + /// + /// Sets the key of the title string in the app's string resources to use to localize the + /// title text. + /// . + /// + public string TitleLocKey { get; set; } + + /// + /// The collection of resource key strings that will be used in place of the format + /// specifiers in . + /// + public IEnumerable TitleLocArgs { get; set; } + + /// + /// Sets the key of the body string in the app's string resources to use to localize the + /// body text. + /// . + /// + public string BodyLocKey { get; set; } + + /// + /// The collection of resource key strings that will be used in place of the format + /// specifiers in . + /// + public IEnumerable BodyLocArgs { get; set; } + + /// + /// Sets the Android notification channel ID (new in Android O). The app must create a + /// channel with this channel ID before any notification with this channel ID is received. + /// If you don't send this channel ID in the request, or if the channel ID provided has + /// not yet been created by the app, FCM uses the channel ID specified in the app manifest. + /// + public string ChannelId { get; set; } + + /// + /// Validates the content and structure of this notification, and converts it into the + /// type. This return type can be safely + /// serialized into a JSON string that is acceptable to the FCM backend service. + /// + internal ValidatedAndroidNotification Validate() + { + if (Color != null) { + if (!Regex.Match(Color, "^#[0-9a-fA-F]{6}$").Success) + { + throw new ArgumentException("Color must be in the form #RRGGBB"); + } + } + if (TitleLocArgs != null && TitleLocArgs.Any()) { + if (string.IsNullOrEmpty(TitleLocKey)) + { + throw new ArgumentException("TitleLocKey is required when specifying TitleLocArgs"); + } + } + if (BodyLocArgs != null && BodyLocArgs.Any()) { + if (string.IsNullOrEmpty(BodyLocKey)) + { + throw new ArgumentException("BodyLocKey is required when specifying BodyLocArgs"); + } + } + return new ValidatedAndroidNotification() + { + Title = this.Title, + Body = this.Body, + Icon = this.Icon, + Color = this.Color, + Sound = this.Sound, + Tag = this.Tag, + ClickAction = this.ClickAction, + TitleLocKey = this.TitleLocKey, + TitleLocArgs = this.TitleLocArgs, + BodyLocKey = this.BodyLocKey, + BodyLocArgs = this.BodyLocArgs, + ChannelId = this.ChannelId, + }; + } + } + + internal sealed class ValidatedAndroidNotification + { + [JsonProperty("title")] + internal string Title { get; set; } + + [JsonProperty("body")] + internal string Body { get; set; } + + [JsonProperty("icon")] + internal string Icon { get; set; } + + [JsonProperty("color")] + internal string Color { get; set; } + + [JsonProperty("sound")] + internal string Sound { get; set; } + + [JsonProperty("tag")] + internal string Tag { get; set; } + + [JsonProperty("click_action")] + internal string ClickAction { get; set; } + + [JsonProperty("title_loc_key")] + internal string TitleLocKey { get; set; } + + [JsonProperty("title_loc_args")] + internal IEnumerable TitleLocArgs { get; set; } + + [JsonProperty("body_loc_key")] + internal string BodyLocKey { get; set; } + + [JsonProperty("body_loc_args")] + internal IEnumerable BodyLocArgs { get; set; } + + [JsonProperty("channel_id")] + internal string ChannelId { get; set; } + } +} From 9f9b0a00a46846bb1f998d6e34f64faeab11bc60 Mon Sep 17 00:00:00 2001 From: hiranya911 Date: Fri, 14 Dec 2018 13:40:07 -0800 Subject: [PATCH 2/2] Added documentation to internal types --- FirebaseAdmin/FirebaseAdmin/Messaging/AndroidNotification.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/FirebaseAdmin/FirebaseAdmin/Messaging/AndroidNotification.cs b/FirebaseAdmin/FirebaseAdmin/Messaging/AndroidNotification.cs index 868a2f2b..63e0fddf 100644 --- a/FirebaseAdmin/FirebaseAdmin/Messaging/AndroidNotification.cs +++ b/FirebaseAdmin/FirebaseAdmin/Messaging/AndroidNotification.cs @@ -143,6 +143,10 @@ internal ValidatedAndroidNotification Validate() } } + /// + /// Represents a validated Android notification that can be serialized into the JSON format + /// accepted by the FCM backend service. + /// internal sealed class ValidatedAndroidNotification { [JsonProperty("title")]