[Packagemanager] Throw an exception when failed to add event listener (#5886)
[platform/core/csapi/tizenfx.git] / src / Tizen.Applications.Preference / Tizen.Applications / Preference.cs
1 /*
2  * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
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 Tizen.Internals.Errors;
20
21 namespace Tizen.Applications
22 {
23     /// <summary>
24     /// The preference class provides APIs to store and retrieve an application specific data/preference. A preference is saved in the form of a key-value pair.
25     /// Keys are always text strings and the value can be any one of the four types: integer, double, string, and boolean.
26     /// </summary>
27     /// <since_tizen> 3 </since_tizen>
28     public static class Preference
29     {
30         private const string LogTag = "Tizen.Applications";
31         private static Interop.Preference.ChangedCallback s_preferenceChangedCallback;
32         private static IDictionary<string, EventContext> s_eventMap = new Dictionary<string, EventContext>();
33
34         static Preference()
35         {
36             s_preferenceChangedCallback = (string key, IntPtr userData) =>
37             {
38                 try
39                 {
40                     s_eventMap[key]?.FireEvent();
41                 }
42                 catch (Exception e)
43                 {
44                     Log.Warn(LogTag, e.Message);
45                 }
46             };
47         }
48
49         /// <summary>
50         /// Retrieves all keys of the application preferences.
51         /// </summary>
52         /// <value>
53         /// The list of keys.
54         /// </value>
55         /// <example>
56         /// <code>
57         ///     Preference.Set("Option_enabled", true);
58         ///     Preference.Set("active_user", "Joe");
59         ///     Preference.Set("default_volume", 10);
60         ///     Preference.Set("brightness", "0.6");
61         ///     foreach(string key in Preference.Keys)
62         ///     {
63         ///         Console.WriteLine("key {0}", key);
64         ///     }
65         /// </code>
66         /// </example>
67         /// <since_tizen> 3 </since_tizen>
68         public static IEnumerable<string> Keys
69         {
70             get
71             {
72                 var collection = new List<string>();
73                 Interop.Preference.ItemCallback itemsCallback = (string key, IntPtr userData) =>
74                 {
75                     collection.Add(key);
76                     return true;
77                 };
78                 Interop.Preference.ForeachItem(itemsCallback, IntPtr.Zero);
79                 return collection;
80             }
81         }
82
83         /// <summary>
84         /// Gets the event context for the given key.
85         /// </summary>
86         /// <seealso cref="EventContext"/>
87         /// <param name="key">The preference key.</param>
88         /// <returns>The event context of respective key.</returns>
89         /// <exception cref="KeyNotFoundException">Thrown if the key is not found.</exception>
90         /// <exception cref="ArgumentException">Thrown if the key is invalid parameter.</exception>
91         /// <example>
92         /// <code>
93         ///     private static void Preference_PreferenceChanged(object sender, PreferenceChangedEventArgs e)
94         ///     {
95         ///         Console.WriteLine("key {0}", e.Key);
96         ///     }
97         ///
98         ///     Preference.EventContext context = null;
99         ///     Preference.GetEventContext("active_user").TryGetTarget(out context);
100         ///     if(context != null)
101         ///     {
102         ///         context.Changed += Preference_PreferenceChanged;
103         ///     }
104         ///
105         ///     Preference.Set("active_user", "Poe");
106         ///
107         ///     Preference.GetEventContext("active_user").TryGetTarget(out context);
108         ///     if (context != null)
109         ///     {
110         ///         context.Changed -= Preference_PreferenceChanged;
111         ///     }
112         /// </code>
113         /// </example>
114         /// <since_tizen> 3 </since_tizen>
115         public static WeakReference<EventContext> GetEventContext(string key)
116         {
117             if (!s_eventMap.ContainsKey(key))
118             {
119                 if (Contains(key))
120                 {
121                     s_eventMap[key] = new EventContext(key);
122                 }
123                 else
124                 {
125                     throw PreferenceErrorFactory.GetException((int)PreferenceErrorFactory.PreferenceError.KeyNotAvailable);
126                 }
127             }
128
129             return new WeakReference<EventContext>(s_eventMap[key]);
130         }
131
132         /// <summary>
133         /// Checks whether the given key exists in the preference.
134         /// </summary>
135         /// <param name="key">The name of the key to check.</param>
136         /// <returns>True if the key exists in the preference, otherwise false.</returns>
137         /// <exception cref="ArgumentException">Thrown if the key is an invalid parameter.</exception>
138         /// <exception cref="System.IO.IOException">Thrown when the method failed due to an internal I/O error.</exception>
139         /// <example>
140         /// <code>
141         ///     Preference.Set("active_user", "Joe");
142         ///     bool exists = Preference.Contains("active_user");
143         ///     if (exists)
144         ///     {
145         ///         string value = Preference.Get&lt;istring&gt;("active_user");
146         ///         Console.WriteLine("user {0}", value);
147         ///     }
148         /// </code>
149         /// </example>
150         /// <since_tizen> 3 </since_tizen>
151         public static bool Contains(string key)
152         {
153             bool contains;
154             int ret = Interop.Preference.IsExisting(key, out contains);
155             if (ret != (int)PreferenceErrorFactory.PreferenceError.None)
156             {
157                 Log.Error(LogTag, "Failed to find key");
158                 throw PreferenceErrorFactory.GetException(ret);
159             }
160
161             return contains;
162         }
163
164         /// <summary>
165         /// Sets a key-value pair representing the preference.
166         /// </summary>
167         /// <remarks>
168         /// If the key already exists in the preference, the old value will be overwritten with a new value.
169         /// Data types for supported values are: integer, double, string, and bool.
170         /// </remarks>
171         /// <param name="key">The name of the key to create/modify.</param>
172         /// <param name="value">The value corresponding to the key.</param>
173         /// <exception cref="ArgumentException">Thrown if the key is an invalid parameter.</exception>
174         /// <exception cref="System.IO.IOException">Thrown when the method failed due to an internal I/O error.</exception>
175         /// <example>
176         /// <code>
177         ///     Preference.Set("Option_enabled", true);
178         ///     Preference.Set("active_user", "Joe");
179         ///     Preference.Set("default_volume", 10);
180         ///     Preference.Set("brightness", "0.6");
181         /// </code>
182         /// </example>
183         /// <since_tizen> 3 </since_tizen>
184         public static void Set(string key, object value)
185         {
186             int ret = 0;
187             if (value is int)
188             {
189                 ret = Interop.Preference.SetInt(key, (int)value);
190                 if (ret != (int)PreferenceErrorFactory.PreferenceError.None)
191                 {
192                     Log.Error(LogTag, "Failed to find key");
193                     throw PreferenceErrorFactory.GetException(ret);
194                 }
195             }
196             else if (value is double)
197             {
198                 ret = Interop.Preference.SetDouble(key, (double)value);
199                 if (ret != (int)PreferenceErrorFactory.PreferenceError.None)
200                 {
201                     Log.Error(LogTag, "Failed to find key");
202                     throw PreferenceErrorFactory.GetException(ret);
203                 }
204             }
205             else if (value is string)
206             {
207                 ret = Interop.Preference.SetString(key, (string)value);
208                 if (ret != (int)PreferenceErrorFactory.PreferenceError.None)
209                 {
210                     Log.Error(LogTag, "Failed to find key");
211                     throw PreferenceErrorFactory.GetException(ret);
212                 }
213             }
214             else if (value is bool)
215             {
216                 ret = Interop.Preference.SetBoolean(key, (bool)value);
217                 if (ret != (int)PreferenceErrorFactory.PreferenceError.None)
218                 {
219                     Log.Error(LogTag, "Failed to find key");
220                     throw PreferenceErrorFactory.GetException(ret);
221                 }
222             }
223             else
224             {
225                 Log.Error(LogTag, "Failed to Set");
226                 throw new ArgumentException("Invalid parameter");
227             }
228         }
229
230         /// <summary>
231         /// Gets the value of a preference item with the specified key.
232         /// Note that this is a generic method.
233         /// </summary>
234         /// <typeparam name="T">The generic type to return.</typeparam>
235         /// <param name="key">The key of the preference.</param>
236         /// <returns>The value of the preference item if it is of the specified generic type.</returns>
237         /// <exception cref="KeyNotFoundException">Thrown if the key is not found.</exception>
238         /// <exception cref="ArgumentException">Thrown if the key is an invalid parameter.</exception>
239         /// <exception cref="System.IO.IOException">Thrown when the method failed due to an internal I/O error.</exception>
240         /// <example>
241         /// <code>
242         ///     bool exists = Preference.Contains("active_user");
243         ///     if (exists)
244         ///     {
245         ///         string value = Preference.Get&lt;string&gt;("active_user");
246         ///         Console.WriteLine("user {0}", value);
247         ///     }
248         /// </code>
249         /// </example>
250         /// <since_tizen> 3 </since_tizen>
251         public static T Get<T>(string key)
252         {
253             object result = null;
254             int ret = (int)PreferenceErrorFactory.PreferenceError.None;
255             if (typeof(T) == typeof(bool))
256             {
257                 bool val;
258                 ret = Interop.Preference.GetBoolean(key, out val);
259                 result = val;
260             }
261             else if (typeof(T) == typeof(int))
262             {
263                 int val;
264                 ret = Interop.Preference.GetInt(key, out val);
265                 result = val;
266             }
267             else if (typeof(T) == typeof(string))
268             {
269                 string val;
270                 ret = Interop.Preference.GetString(key, out val);
271                 result = val;
272             }
273             else if (typeof(T) == typeof(double))
274             {
275                 double val;
276                 ret = Interop.Preference.GetDouble(key, out val);
277                 result = val;
278             }
279             else
280             {
281                 Log.Error(LogTag, "Failed to remove key");
282                 throw new ArgumentException("Invalid parameter");
283             }
284
285             if (ret != (int)PreferenceErrorFactory.PreferenceError.None)
286             {
287                 Log.Error(LogTag, "Failed to remove key");
288                 throw PreferenceErrorFactory.GetException(ret);
289             }
290
291             return (result != null) ? (T)result : default(T);
292         }
293
294         /// <summary>
295         /// Removes any preference value with the given key.
296         /// </summary>
297         /// <param name="key">The key to remove.</param>
298         /// <exception cref="KeyNotFoundException">Thrown if the key is not found.</exception>
299         /// <exception cref="System.IO.IOException">Thrown when the method failed due to an internal I/O error.</exception>
300         /// <example>
301         /// <code>
302         ///     bool exists = Preference.Contains("active_user");
303         ///     if (exists)
304         ///     {
305         ///         string value = Preference.Remove("active_user");
306         ///     }
307         /// </code>
308         /// </example>
309         /// <since_tizen> 3 </since_tizen>
310         public static void Remove(string key)
311         {
312             int ret = Interop.Preference.Remove(key);
313             if (ret != (int)PreferenceErrorFactory.PreferenceError.None)
314             {
315                 Log.Error(LogTag, "Failed to remove key");
316                 throw PreferenceErrorFactory.GetException(ret);
317             }
318         }
319
320         /// <summary>
321         /// Removes all the key-value pairs from the preference.
322         /// </summary>
323         /// <exception cref="System.IO.IOException">Thrown when the method failed due to an internal I/O error.</exception>
324         /// <example>
325         /// <code>
326         ///     Preference.Set("Option_enabled", true);
327         ///     Preference.Set("active_user", "Joe");
328         ///     Preference.Set("default_volume", 10);
329         ///     Preference.Set("brightness", "0.6");
330         ///     Preference.RemoveAll();
331         /// </code>
332         /// </example>
333         /// <since_tizen> 3 </since_tizen>
334         public static void RemoveAll()
335         {
336             int ret = Interop.Preference.RemoveAll();
337             if (ret != (int)PreferenceErrorFactory.PreferenceError.None)
338             {
339                 Log.Error(LogTag, "Failed to remove all keys");
340                 throw PreferenceErrorFactory.GetException(ret);
341             }
342         }
343
344         private static void AllowChangeNotifications(string key)
345         {
346             int ret = Interop.Preference.SetChangedCb(key, s_preferenceChangedCallback, IntPtr.Zero);
347             if (ret != (int)PreferenceErrorFactory.PreferenceError.None)
348             {
349                 Log.Error(LogTag, "Failed to set key notification");
350                 throw PreferenceErrorFactory.GetException(ret);
351             }
352         }
353
354         private static void DisallowChangeNotifications(string key)
355         {
356             int ret = Interop.Preference.UnsetChangedCb(key);
357             if (ret != (int)PreferenceErrorFactory.PreferenceError.None)
358             {
359                 Log.Error(LogTag, "Failed to remove key notification");
360                 throw PreferenceErrorFactory.GetException(ret);
361             }
362         }
363
364         /// <summary>
365         /// The class manages event handlers of the preference keys. The class enables having event handlers for individual preference keys.
366         /// </summary>
367         /// <since_tizen> 3 </since_tizen>
368         public class EventContext
369         {
370             private string _key;
371
372             internal EventContext(string key)
373             {
374                 _key = key;
375             }
376
377             /// <summary>
378             /// Occurs whenever there is a change in the value of a preference key.
379             /// </summary>
380             /// <exception cref="System.ArgumentException">Thrown when the key does not exist or when there is an invalid parameter.</exception>
381             /// <exception cref="System.InvalidOperationException">Thrown when the bundle instance has been disposed.</exception>
382             /// <example>
383             /// <code>
384             ///     private static void Preference_PreferenceChanged(object sender, PreferenceChangedEventArgs e)
385             ///     {
386             ///         Console.WriteLine("key {0}", e.Key);
387             ///     }
388             ///     Preference.EventContext context = null;
389             ///     Preference.GetEventContext("active_user").TryGetTarget(out context);
390             ///     if(context != null)
391             ///     {
392             ///         context.Changed += Preference_PreferenceChanged;
393             ///     }
394             ///
395             ///     Preference.Set("active_user", "Poe");
396             ///
397             ///     Preference.GetEventContext("active_user").TryGetTarget(out context);
398             ///     if (context != null)
399             ///     {
400             ///         context.Changed -= Preference_PreferenceChanged;
401             ///     }
402             /// </code>
403             /// </example>
404             /// <since_tizen> 3 </since_tizen>
405             public event EventHandler<PreferenceChangedEventArgs> Changed
406             {
407                 add
408                 {
409                     if (_changed == null)
410                     {
411                         AllowChangeNotifications(_key);
412                     }
413
414                     _changed += value;
415                 }
416
417                 remove
418                 {
419                     _changed -= value;
420                     if (_changed == null)
421                     {
422                         DisallowChangeNotifications(_key);
423                         s_eventMap.Remove(_key);
424                     }
425                 }
426             }
427
428             private event EventHandler<PreferenceChangedEventArgs> _changed;
429
430             internal void FireEvent()
431             {
432                 _changed?.Invoke(null, new PreferenceChangedEventArgs() { Key = _key });
433             }
434         }
435
436     }
437
438     internal static class PreferenceErrorFactory
439     {
440         internal enum PreferenceError
441         {
442             None = ErrorCode.None,
443             OutOfMemory = ErrorCode.OutOfMemory,
444             InvalidParameter = ErrorCode.InvalidParameter,
445             KeyNotAvailable = -0x01100000 | 0x30,
446             IoError = ErrorCode.IoError
447         }
448
449         static internal Exception GetException(int error)
450         {
451             if ((PreferenceError)error == PreferenceError.OutOfMemory)
452             {
453                 return new OutOfMemoryException("Out of memory");
454             }
455             else if ((PreferenceError)error == PreferenceError.InvalidParameter)
456             {
457                 return new ArgumentException("Invalid parameter");
458             }
459             else if ((PreferenceError)error == PreferenceError.KeyNotAvailable)
460             {
461                 return new KeyNotFoundException("Key does not exist in the bundle");
462             }
463             else if ((PreferenceError)error == PreferenceError.IoError)
464             {
465                 return new System.IO.IOException("I/O Error");
466             }
467             else
468             {
469                 return new ArgumentException("Unknown error");
470             }
471         }
472     }
473 }