2 * Copyright(c) 2021 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 using System.Collections.Generic;
19 using System.ComponentModel;
20 using Tizen.NUI.BaseComponents;
22 namespace Tizen.NUI.Components
25 /// Notification helps to raise a notification window with a content View.
27 /// <since_tizen> 8 </since_tizen>
28 public class Notification : Disposable
31 /// Toast will appear at the top of the screen.
33 [EditorBrowsable(EditorBrowsableState.Never)]
34 public static readonly Position ToastTop = ParentOrigin.TopCenter;
37 /// Toast will appear at the center of the screen.
39 [EditorBrowsable(EditorBrowsableState.Never)]
40 public static readonly Position ToastCenter = ParentOrigin.Center;
43 /// Toast will appear at the bottom of the screen.
45 [EditorBrowsable(EditorBrowsableState.Never)]
46 public static readonly Position ToastBottom = ParentOrigin.BottomCenter;
49 /// Show the notification for a short period of time.
51 [EditorBrowsable(EditorBrowsableState.Never)]
52 public static readonly uint ToastShort = 2000;
55 /// Show the notification for a long period of time.
57 [EditorBrowsable(EditorBrowsableState.Never)]
58 public static readonly uint ToastLong = ToastShort * 2;
60 private static HashSet<Notification> instanceSet;
62 private Window notificationWindow;
66 private NotificationLevel level = NotificationLevel.Base;
68 private Rectangle positionSize;
70 private bool dismissOnTouch = false;
72 private Animation onPostAnimation;
74 private Animation onDismissAnimation;
76 private NotificationState state = NotificationState.Ready;
80 ThemeManager.AddPackageTheme(DefaultThemeCreator.Instance);
84 /// Create a notification with a content View.
86 /// <param name="contentView">The content view instance to display in the notification window.</param>
87 /// <exception cref="NotSupportedException">Thrown when the device does not support a notification feature.</exception>
88 /// <exception cref="ArgumentNullException">Thrown when a given contentView is null.</exception>
89 /// <remark>Since the notification creates a new window, the system should support a multi-window feature. Otherwise it will throw a NotSupportedException.</remark>
90 /// <since_tizen> 8 </since_tizen>
91 public Notification(View contentView) : base()
93 if (!Window.IsSupportedMultiWindow())
95 throw new NotSupportedException("This device does not support multi-window. Notification can not be created. ");
97 ContentView = contentView ?? throw new ArgumentNullException(nameof(contentView));
100 private enum NotificationState
108 /// The content view received in a constructor.
110 /// <since_tizen> 8 </since_tizen>
111 public View ContentView { get; private set; }
113 private Window NotificationWindow
117 if (notificationWindow == null)
119 notificationWindow = new Window(null, true)
121 Type = WindowType.Notification,
122 BackgroundColor = Color.Transparent,
124 notificationWindow.Show();
127 return notificationWindow;
129 set => notificationWindow = value;
138 timer = new Timer(0);
139 timer.Tick += OnTimeOut;
149 timer.Tick -= OnTimeOut;
162 /// Create a simple text notification called toast.
164 /// <param name="text">The string content.</param>
165 /// <param name="gravity">The location at which the toast should appear. It's one of the notification constants: ToastTop, ToastCenter and ToastBottom.</param>
166 /// <returns>The created Notification instance.</returns>
167 /// <exception cref="NotSupportedException">Thrown when the device does not support a notification feature.</exception>
168 /// <exception cref="ArgumentNullException">Thrown when the given text or gravity is null.</exception>
169 /// <remark>Application need to set http://tizen.org/privilege/window.priority.set to post a notification.</remark>
170 /// <remark>Since the notification creates a new window, the system should support a multi-window feature. Otherwise it will throw a NotSupportedException.</remark>
172 /// The following example demonstrates how to make a toast at the bottom and show it for a short period time.
174 /// Notification.MakeToast("Hello World!", Notification.ToastBottom).Post(Notification.ToastShort);
177 /// <since_tizen> 9 </since_tizen>
178 public static Notification MakeToast(string text, Position gravity)
180 if (!Window.IsSupportedMultiWindow())
182 throw new NotSupportedException("This device does not support multi-window. Notification can not be created. ");
185 var textLabel = new TextLabel(text ?? throw new ArgumentNullException(nameof(text)))
190 if (gravity == null) throw new ArgumentNullException(nameof(gravity));
192 var style = ThemeManager.GetInitialStyleWithoutClone("NotificationToast");
195 textLabel.ApplyStyle(style);
198 textLabel.ParentOrigin = gravity;
199 textLabel.PivotPoint = gravity;
201 if (gravity == ToastCenter)
203 textLabel.PositionY = 0;
205 else if (gravity == ToastBottom)
207 textLabel.PositionY = -textLabel.PositionY;
210 var postAnimation = new Animation(700);
211 postAnimation.AnimateTo(textLabel, "Opacity", 1.0f);
213 var dismissAnimation = new Animation(500);
214 dismissAnimation.AnimateTo(textLabel, "Opacity", 0.0f);
216 return new Notification(textLabel).SetAnimationOnPost(postAnimation).SetAnimationOnDismiss(dismissAnimation);
220 /// Post a notification window with the content view.
222 /// <param name="duration">Dismiss the notification window after given time in millisecond. The value 0 won't dismiss the notification.</param>
223 /// <returns>The current Notification instance.</returns>
224 /// <privilege>http://tizen.org/privilege/window.priority.set</privilege>
225 /// <exception cref="UnauthorizedAccessException">Thrown when the application does not have proper privilege.</exception>
226 /// <since_tizen> 8 </since_tizen>
227 public void Post(uint duration = 0)
229 if (state != NotificationState.Ready)
234 var applyLevelResult = ApplyLevel(level);
236 if (applyLevelResult == Window.OperationResult.PermissionDenied)
238 throw new UnauthorizedAccessException("Cannot post a Notification: Permission Denied. The privilege http://tizen.org/privilege/window.priority.set is needed.");
241 if (applyLevelResult != Window.OperationResult.Succeed)
243 Tizen.Log.Info("NUI", "The notification window may not have proper notification level.");
246 ApplyPositionSize(positionSize);
248 ApplyDismissOnTouch(dismissOnTouch);
250 NotificationWindow.Add(ContentView);
254 Timer.Interval = duration;
257 state = NotificationState.Post;
259 onPostAnimation?.Play();
261 RegisterInstance(this);
265 /// Sets a priority level for the specified notification window.
266 /// The default level is NotificationLevel.Base.
268 /// <param name="level">The notification window level.</param>
269 /// <returns>The current Notification instance.</returns>
270 /// <privilege>http://tizen.org/privilege/window.priority.set</privilege>
271 /// <exception cref="UnauthorizedAccessException">Thrown when the application does not have proper privilege.</exception>
272 /// <since_tizen> 8 </since_tizen>
273 public Notification SetLevel(NotificationLevel level)
277 if (state == NotificationState.Post)
279 var result = ApplyLevel(level);
281 if (result == Window.OperationResult.PermissionDenied)
283 throw new UnauthorizedAccessException("Cannot set notification level: Permission Denied. The privilege http://tizen.org/privilege/window.priority.set is needed.");
286 if (result != Window.OperationResult.Succeed)
288 Tizen.Log.Info("NUI", "Cannot set notification level: Unknown reason. The notification window may not have proper notification level.");
296 /// Sets position and size of the notification window.
298 /// <param name="positionSize">The position and size information in rectangle.</param>
299 /// <returns>The current Notification instance.</returns>
300 /// <exception cref="ArgumentException">Thrown when a given positionSize is invalid.</exception>
301 /// <since_tizen> 8 </since_tizen>
302 public Notification SetPositionSize(Rectangle positionSize)
304 this.positionSize = positionSize ?? throw (new ArgumentException("Input positionSize should not be null."));
306 if (state == NotificationState.Post || state == NotificationState.Dismiss)
308 ApplyPositionSize(positionSize);
315 /// Sets whether listen to touch event to dismiss notification window.
317 /// <param name="dismissOnTouch">Dismiss notification window on touch if the value is true.</param>
318 /// <returns>The current Notification instance.</returns>
319 [EditorBrowsable(EditorBrowsableState.Never)]
320 public Notification SetDismissOnTouch(bool dismissOnTouch)
322 if (this.dismissOnTouch == dismissOnTouch)
327 this.dismissOnTouch = dismissOnTouch;
329 if (state == NotificationState.Post)
331 ApplyDismissOnTouch(dismissOnTouch);
338 /// Sets a user-defined animation to play when posting the notification.
339 /// The Notification will play the given animation right after the notification window pops up.
341 /// <param name="animation">The animation to play.</param>
342 /// <since_tizen> 8 </since_tizen>
343 public Notification SetAnimationOnPost(Animation animation)
345 this.onPostAnimation = animation;
351 /// Sets a user-defined animation to play when dismiss the notification.
352 /// On dismiss, the given animation is played, and after the playback is completed the notification window is undisplayed.
354 /// <param name="animation">The animation to play.</param>
355 /// <since_tizen> 8 </since_tizen>
356 public Notification SetAnimationOnDismiss(Animation animation)
358 this.onDismissAnimation = animation;
364 /// Dismiss the notification window.
366 /// <since_tizen> 8 </since_tizen>
367 public void Dismiss()
369 if (state != NotificationState.Post)
374 state = NotificationState.Dismiss;
376 if (onDismissAnimation != null)
378 onDismissAnimation.Finished += OnAnimationEnd;
380 onDismissAnimation.Play();
384 ApplyDismissOnTouch(false);
393 /// Dismiss the notification window directly without waiting the onDismissAnimation finished.
395 /// <since_tizen> 8 </since_tizen>
396 public void ForceQuit()
398 if (state != NotificationState.Post && state != NotificationState.Dismiss)
407 /// <since_tizen> 8 </since_tizen>
408 protected override void Dispose(DisposeTypes type)
415 if (type == DisposeTypes.Explicit)
419 positionSize?.Dispose();
420 onPostAnimation?.Dispose();
421 onDismissAnimation?.Dispose();
427 private static void RegisterInstance(Notification instance)
429 if (instanceSet == null)
431 instanceSet = new HashSet<Notification>();
434 instanceSet.Add(instance);
437 private static void UnregisterInstance(Notification instance)
439 if (instanceSet == null)
444 instanceSet.Remove(instance);
446 if (instanceSet.Count == 0)
452 private void DestroyNotificationWindow()
454 notificationWindow.Hide();
456 notificationWindow.Dispose();
458 notificationWindow = null;
461 private Window.OperationResult ApplyLevel(NotificationLevel level)
463 var ret = (Window.OperationResult)Interop.Window.SetNotificationLevel(NotificationWindow.SwigCPtr, (int)level);
464 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
468 private void ApplyPositionSize(Rectangle positionSize)
470 if (positionSize != null)
472 NotificationWindow.SetPositionSize(positionSize);
476 private void ApplyDismissOnTouch(bool dismissOnTouch)
480 NotificationWindow.TouchEvent += OnWindowTouch;
484 NotificationWindow.TouchEvent -= OnWindowTouch;
488 private void ClearAll()
490 if (onDismissAnimation != null)
492 onDismissAnimation.Finished -= OnAnimationEnd;
494 onDismissAnimation.Stop();
497 notificationWindow.Remove(ContentView);
499 notificationWindow.TouchEvent -= OnWindowTouch;
503 DestroyNotificationWindow();
505 state = NotificationState.Ready;
507 UnregisterInstance(this);
510 private void OnWindowTouch(object target, Window.TouchEventArgs args)
515 private bool OnTimeOut(object target, Timer.TickEventArgs args)
522 private void OnAnimationEnd(object target, EventArgs args)