[NUI] Make Notification APIs public and fix CA warnings. (#1747)
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI.Components / Controls / Notification.cs
1 /*
2  * Copyright(c) 2020 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  *
16  */
17 using System;
18 using System.Collections.Generic;
19 using System.ComponentModel;
20 using Tizen.NUI.BaseComponents;
21
22 namespace Tizen.NUI.Components
23 {
24     /// <summary>
25     /// Notification helps to raise a notification window with a content View.
26     /// </summary>
27     /// <since_tizen> 8 </since_tizen>
28     public class Notification : Disposable
29     {
30         private static HashSet<Notification> instanceSet;
31
32         private Window notificationWindow;
33
34         private Timer timer;
35
36         private NotificationLevel level = NotificationLevel.Base;
37
38         private Rectangle positionSize;
39
40         private bool dismissOnTouch = false;
41
42         private Animation onPostAnimation;
43
44         private Animation onDismissAnimation;
45
46         private NotificationState state = NotificationState.Ready;
47
48         /// <summary>
49         /// Create a notification with a content View.
50         /// </summary>
51         /// <param name="contentView">The content view instance to display in the notification window.</param>
52         /// <exception cref="ArgumentException">Thrown when a given contentView is invalid.</exception>
53         /// <since_tizen> 8 </since_tizen>
54         public Notification(View contentView) : base()
55         {
56             ContentView = contentView ?? throw new ArgumentException("Input contentView should not be null.");
57         }
58
59         private enum NotificationState
60         {
61             Ready,
62             Post,
63             Dismiss,
64         }
65
66         /// <summary>
67         /// The content view received in a constructor.
68         /// </summary>
69         /// <since_tizen> 8 </since_tizen>
70         public View ContentView { get; private set; }
71
72         private Window NotificationWindow
73         {
74             get
75             {
76                 if (notificationWindow == null)
77                 {
78                     notificationWindow = new Window(null, true)
79                     {
80                         Type = WindowType.Notification,
81                     };
82                     notificationWindow.Show();
83                 }
84
85                 return notificationWindow;
86             }
87             set => notificationWindow = value;
88         }
89
90         private Timer Timer
91         {
92             get
93             {
94                 if (timer == null)
95                 {
96                     timer = new Timer(0);
97                     timer.Tick += OnTimeOut;
98                 }
99
100                 return timer;
101             }
102             set
103             {
104                 if (timer != null)
105                 {
106                     timer.Stop();
107                     timer.Tick -= OnTimeOut;
108
109                     if (value == null)
110                     {
111                         timer.Dispose();
112                     }
113                 }
114
115                 timer = value;
116             }
117         }
118
119         /// <summary>
120         /// Post a notification window with the content view.
121         /// </summary>
122         /// <param name="duration">Dismiss the notification window after given time. The value 0 won't dismiss the notification.</param>
123         /// <returns>The current Notification instance.</returns>
124         /// <privilege>http://tizen.org/privilege/window.priority.set</privilege>
125         /// <exception cref="UnauthorizedAccessException">Thrown when the application does not have proper privilege.</exception>
126         /// <since_tizen> 8 </since_tizen>
127         public void Post(uint duration = 0)
128         {
129             if (state != NotificationState.Ready)
130             {
131                 return;
132             }
133
134             if (!ApplyLevel(level))
135             {
136                 throw new UnauthorizedAccessException("Cannot post a Notification: Permission Denied");
137             }
138
139             ApplyPositionSize(positionSize);
140
141             ApplyDismissOnTouch(dismissOnTouch);
142
143             NotificationWindow.Add(ContentView);
144
145             if (duration > 0)
146             {
147                 Timer.Interval = duration;
148             }
149
150             state = NotificationState.Post;
151
152             onPostAnimation?.Play();
153
154             RegisterInstance(this);
155         }
156
157         /// <summary>
158         /// Sets a priority level for the specified notification window.
159         /// The default level is NotificationLevel.Base.
160         /// </summary>
161         /// <param name="level">The notification window level.</param>
162         /// <returns>The current Notification instance.</returns>
163         /// <privilege>http://tizen.org/privilege/window.priority.set</privilege>
164         /// <exception cref="UnauthorizedAccessException">Thrown when the application does not have proper privilege.</exception>
165         /// <since_tizen> 8 </since_tizen>
166         public Notification SetLevel(NotificationLevel level)
167         {
168             this.level = level;
169
170             if (state == NotificationState.Post && !ApplyLevel(level))
171             {
172                 throw new UnauthorizedAccessException("Cannot set notification level: Permission Denied");
173             }
174
175             return this;
176         }
177
178         /// <summary>
179         /// Sets position and size of the notification window.
180         /// </summary>
181         /// <param name="positionSize">The position and size information in rectangle.</param>
182         /// <returns>The current Notification instance.</returns>
183         /// <exception cref="ArgumentException">Thrown when a given positionSize is invalid.</exception>
184         /// <since_tizen> 8 </since_tizen>
185         public Notification SetPositionSize(Rectangle positionSize)
186         {
187             this.positionSize = positionSize ?? throw (new ArgumentException("Input positionSize should not be null."));
188
189             if (state == NotificationState.Post || state == NotificationState.Dismiss)
190             {
191                 ApplyPositionSize(positionSize);
192             }
193
194             return this;
195         }
196
197         /// <summary>
198         /// Sets whether listen to touch event to dismiss notification window.
199         /// </summary>
200         /// <param name="dismissOnTouch">Dismiss notification window on touch if the value is true.</param>
201         /// <returns>The current Notification instance.</returns>
202         [EditorBrowsable(EditorBrowsableState.Never)]
203         public Notification SetDismissOnTouch(bool dismissOnTouch)
204         {
205             if (this.dismissOnTouch == dismissOnTouch)
206             {
207                 return this;
208             }
209
210             this.dismissOnTouch = dismissOnTouch;
211
212             if (state == NotificationState.Post)
213             {
214                 ApplyDismissOnTouch(dismissOnTouch);
215             }
216
217             return this;
218         }
219
220         /// <summary>
221         /// Sets a user-defined animation to play when posting the notification.
222         /// The Notification will play the given animation right after the notification window pops up.
223         /// </summary>
224         /// <param name="animation">The animation to play.</param>
225         /// <since_tizen> 8 </since_tizen>
226         public Notification SetAnimationOnPost(Animation animation)
227         {
228             this.onPostAnimation = animation;
229
230             return this;
231         }
232
233         /// <summary>
234         /// Sets a user-defined animation to play when dismiss the notification.
235         /// On dismiss, the given animation is played, and after the playback is completed the notification window is undisplayed.
236         /// </summary>
237         /// <param name="animation">The animation to play.</param>
238         /// <since_tizen> 8 </since_tizen>
239         public Notification SetAnimationOnDismiss(Animation animation)
240         {
241             this.onDismissAnimation = animation;
242
243             return this;
244         }
245
246         /// <summary>
247         /// Dismiss the notification window.
248         /// </summary>
249         /// <since_tizen> 8 </since_tizen>
250         public void Dismiss()
251         {
252             if (state != NotificationState.Post)
253             {
254                 return;
255             }
256
257             state = NotificationState.Dismiss;
258
259             if (onDismissAnimation != null)
260             {
261                 onDismissAnimation.Finished += OnAnimationEnd;
262
263                 onDismissAnimation.Play();
264
265                 Timer = null;
266
267                 ApplyDismissOnTouch(false);
268
269                 return;
270             }
271
272             ClearAll();
273         }
274
275         /// <summary>
276         /// Dismiss the notification window directly without waiting the onDismissAnimation finished.
277         /// </summary>
278         /// <since_tizen> 8 </since_tizen>
279         public void ForceQuit()
280         {
281             if (state != NotificationState.Post && state != NotificationState.Dismiss)
282             {
283                 return;
284             }
285
286             ClearAll();
287         }
288
289         /// <inheritdoc/>
290         /// <since_tizen> 8 </since_tizen>
291         protected override void Dispose(DisposeTypes type)
292         {
293             if (disposed)
294             {
295                 return;
296             }
297
298             if (type == DisposeTypes.Explicit)
299             {
300                 ClearAll();
301
302                 positionSize?.Dispose();
303                 onPostAnimation?.Dispose();
304                 onDismissAnimation?.Dispose();
305             }
306
307             base.Dispose(type);
308         }
309
310         private static void RegisterInstance(Notification instance)
311         {
312             if (instanceSet == null)
313             {
314                 instanceSet = new HashSet<Notification>();
315             }
316
317             instanceSet.Add(instance);
318         }
319
320         private static void UnregisterInstance(Notification instance)
321         {
322             if (instanceSet == null)
323             {
324                 return;
325             }
326
327             instanceSet.Remove(instance);
328
329             if (instanceSet.Count == 0)
330             {
331                 instanceSet = null;
332             }
333         }
334
335         private void DestroyNotificationWindow()
336         {
337             notificationWindow.Hide();
338
339             notificationWindow.Dispose();
340
341             notificationWindow = null;
342         }
343
344         private bool ApplyLevel(NotificationLevel level)
345         {
346             return NotificationWindow.SetNotificationLevel(level);
347         }
348
349         private void ApplyPositionSize(Rectangle positionSize)
350         {
351             if (positionSize != null)
352             {
353                 NotificationWindow.SetPositionSize(positionSize);
354             }
355         }
356
357         private void ApplyDismissOnTouch(bool dismissOnTouch)
358         {
359             if (dismissOnTouch)
360             {
361                 NotificationWindow.TouchEvent += OnWindowTouch;                    
362             }
363             else
364             {
365                 NotificationWindow.TouchEvent -= OnWindowTouch;
366             }
367         }
368
369         private void ClearAll()
370         {
371             if (onDismissAnimation != null)
372             {
373                 onDismissAnimation.Finished -= OnAnimationEnd;
374
375                 onDismissAnimation.Stop();
376             }
377
378             notificationWindow.Remove(ContentView);
379
380             notificationWindow.TouchEvent -= OnWindowTouch;
381
382             Timer = null;
383
384             DestroyNotificationWindow();
385
386             state = NotificationState.Ready;
387
388             UnregisterInstance(this);
389         }
390
391         private void OnWindowTouch(object target, Window.TouchEventArgs args)
392         {
393             Dismiss();
394         }
395
396         private bool OnTimeOut(object target, Timer.TickEventArgs args)
397         {
398             Dismiss();
399
400             return false;
401         }
402
403         private void OnAnimationEnd(object target, EventArgs args)
404         {
405             ClearAll();
406         }
407     }
408 }