Release 4.0.0-preview1-00235
[platform/core/csapi/tizenfx.git] / src / Tizen.Applications.Common / Tizen.Applications / AppControl.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 System.Linq;
20 using System.Runtime.InteropServices;
21
22 namespace Tizen.Applications
23 {
24     /// <summary>
25     /// Represents the control message to exchange between applications.
26     /// </summary>
27     /// <example>
28     /// <code>
29     /// public class AppControlExample : UIApplication
30     /// {
31     ///     /// ...
32     ///     protected override void OnAppControlReceived(AppControlReceivedEventArgs e)
33     ///     {
34     ///         AppControl appControl = new AppControl();
35     ///         appControl.ApplicationId = "org.tizen.calculator";
36     ///         AppControl.SendLaunchRequest(appControl, (launchRequest, replyRequest, result) => {
37     ///             // ...
38     ///         });
39     ///     }
40     /// }
41     /// </code>
42     /// </example>
43     public class AppControl
44     {
45         private const string LogTag = "Tizen.Applications";
46
47         private static Dictionary<int, Interop.AppControl.ReplyCallback> s_replyNativeCallbackMaps = new Dictionary<int, Interop.AppControl.ReplyCallback>();
48         private static int s_replyNativeCallbackId = 0;
49
50         private readonly SafeAppControlHandle _handle;
51
52         private string _operation = null;
53         private string _mime = null;
54         private string _uri = null;
55         private string _category = null;
56         private string _applicationId = null;
57         private ExtraDataCollection _extraData = null;
58
59         /// <summary>
60         /// Initializes the instance of the AppControl class.
61         /// </summary>
62         /// <exception cref="InvalidOperationException">Thrown when failed to create the AppControl handle.</exception>
63         public AppControl()
64         {
65             Interop.AppControl.ErrorCode err = Interop.AppControl.Create(out _handle);
66             if (err != Interop.AppControl.ErrorCode.None)
67             {
68                 throw new InvalidOperationException("Failed to create the appcontrol handle. Err = " + err);
69             }
70         }
71
72         /// <summary>
73         /// Initializes the instance of the AppControl class with a parameter.
74         /// </summary>
75         /// <param name="enableAppStartedResultEvent">The flag value to receive an additional launch result event on the launch request.</param>
76         /// <exception cref="InvalidOperationException">Thrown when failed to create the AppControl handle.</exception>
77         public AppControl(bool enableAppStartedResultEvent)
78         {
79             Interop.AppControl.ErrorCode err = Interop.AppControl.Create(out _handle);
80             if (err != Interop.AppControl.ErrorCode.None)
81             {
82                 throw new InvalidOperationException("Failed to create the appcontrol handle. Err = " + err);
83             }
84
85             if (enableAppStartedResultEvent)
86             {
87                 err = Interop.AppControl.EnableAppStartedResultEvent(_handle);
88                 if (err != Interop.AppControl.ErrorCode.None)
89                 {
90                     throw new InvalidOperationException("Failed to set EnableAppStartedResultEvent");
91                 }
92             }
93         }
94
95         /// <summary>
96         /// Initializes the instance of the AppControl class with the SafeAppControlHandle.
97         /// </summary>
98         /// <param name="handle"></param>
99         public AppControl(SafeAppControlHandle handle)
100         {
101             if (handle == null)
102             {
103                 throw new ArgumentNullException("handle");
104             }
105
106             Interop.AppControl.ErrorCode err = Interop.AppControl.DangerousClone(out _handle, handle.DangerousGetHandle());
107             if (err != Interop.AppControl.ErrorCode.None)
108             {
109                 throw new InvalidOperationException("Failed to clone the appcontrol handle. Err = " + err);
110             }
111         }
112
113         private AppControl(IntPtr handle)
114         {
115             Interop.AppControl.ErrorCode err = Interop.AppControl.DangerousClone(out _handle, handle);
116             if (err != Interop.AppControl.ErrorCode.None)
117             {
118                 throw new InvalidOperationException("Failed to clone the appcontrol handle. Err = " + err);
119             }
120         }
121
122         #region Public Properties
123
124         /// <summary>
125         /// Gets the SafeAppControlHandle instance.
126         /// </summary>
127         public SafeAppControlHandle SafeAppControlHandle
128         {
129             get
130             {
131                 return _handle;
132             }
133         }
134
135         /// <summary>
136         /// Gets and sets the operation to be performed.
137         /// </summary>
138         /// <value>
139         /// The operation is the mandatory information for the launch request. If the operation is not specified,
140         /// AppControlOperations.Default is used for the launch request. If the operation is AppControlOperations.Default,
141         /// the package information is mandatory to explicitly launch the application.
142         /// (if the operation is null for setter, it clears the previous value.)
143         /// </value>
144         /// <example>
145         /// <code>
146         /// AppControl appControl = new AppControl();
147         /// appControl.Operation = AppControlOperations.Default;
148         /// Log.Debug(LogTag, "Operation: " + appControl.Operation);
149         /// </code>
150         /// </example>
151         public string Operation
152         {
153             get
154             {
155                 if (String.IsNullOrEmpty(_operation))
156                 {
157                     Interop.AppControl.ErrorCode err = Interop.AppControl.GetOperation(_handle, out _operation);
158                     if (err != Interop.AppControl.ErrorCode.None)
159                     {
160                         Log.Warn(LogTag, "Failed to get the operation from the appcontrol. Err = " + err);
161                     }
162                 }
163                 return _operation;
164             }
165             set
166             {
167                 Interop.AppControl.ErrorCode err = Interop.AppControl.SetOperation(_handle, value);
168                 if (err == Interop.AppControl.ErrorCode.None)
169                 {
170                     _operation = value;
171                 }
172                 else
173                 {
174                     Log.Warn(LogTag, "Failed to set the operation to the appcontrol. Err = " + err);
175                 }
176             }
177         }
178
179         /// <summary>
180         /// Gets and sets the explicit MIME type of the data.
181         /// </summary>
182         /// <value>
183         /// (if the mime is null for setter, it clears the previous value.)
184         /// </value>
185         /// <example>
186         /// <code>
187         /// AppControl appControl = new AppControl();
188         /// appControl.Mime = "image/jpg";
189         /// Log.Debug(LogTag, "Mime: " + appControl.Mime);
190         /// </code>
191         /// </example>
192         public string Mime
193         {
194             get
195             {
196                 if (String.IsNullOrEmpty(_mime))
197                 {
198                     Interop.AppControl.ErrorCode err = Interop.AppControl.GetMime(_handle, out _mime);
199                     if (err != Interop.AppControl.ErrorCode.None)
200                     {
201                         Log.Warn(LogTag, "Failed to get the mime from the appcontrol. Err = " + err);
202                     }
203                 }
204                 return _mime;
205             }
206             set
207             {
208                 Interop.AppControl.ErrorCode err = Interop.AppControl.SetMime(_handle, value);
209                 if (err == Interop.AppControl.ErrorCode.None)
210                 {
211                     _mime = value;
212                 }
213                 else
214                 {
215                     Log.Warn(LogTag, "Failed to set the mime to the appcontrol. Err = " + err);
216                 }
217             }
218         }
219
220         /// <summary>
221         /// Gets and sets the URI of the data.
222         /// </summary>
223         /// <value>
224         /// Since Tizen 2.4, if the parameter 'uri' is started with 'file://' and
225         /// it is a regular file in this application's data path, which can be obtained
226         /// by property DataPath in ApplicationInfo class,
227         /// it will be shared to the callee application.
228         /// Framework will grant a temporary permission to the callee application for this file and
229         /// revoke it when the callee application is terminated.
230         /// The callee application can just read it.
231         /// (if the uri is null for setter, it clears the previous value.)
232         /// </value>
233         /// <example>
234         /// <code>
235         /// public class AppControlExample : UIApplication
236         /// {
237         ///     ...
238         ///     protected override void OnAppControlReceived(AppControlReceivedEventArgs e)
239         ///     {
240         ///         ...
241         ///         AppControl appControl = new AppControl();
242         ///         appContrl.Uri = this.ApplicationInfo.DataPath + "image.jpg";
243         ///         Log.Debug(LogTag, "Set Uri: " + appControl.Uri);
244         ///     }
245         /// }
246         /// </code>
247         /// </example>
248         public string Uri
249         {
250             get
251             {
252                 if (String.IsNullOrEmpty(_uri))
253                 {
254                     Interop.AppControl.ErrorCode err = Interop.AppControl.GetUri(_handle, out _uri);
255                     if (err != Interop.AppControl.ErrorCode.None)
256                     {
257                         Log.Warn(LogTag, "Failed to get the uri from the appcontrol. Err = " + err);
258                     }
259                 }
260                 return _uri;
261             }
262             set
263             {
264                 Interop.AppControl.ErrorCode err = Interop.AppControl.SetUri(_handle, value);
265                 if (err == Interop.AppControl.ErrorCode.None)
266                 {
267                     _uri = value;
268                 }
269                 else
270                 {
271                     Log.Warn(LogTag, "Failed to set the uri to the appcontrol. Err = " + err);
272                 }
273             }
274         }
275
276         /// <summary>
277         /// Gets and sets the explicit category.
278         /// </summary>
279         /// <value>
280         /// (if the category is null for setter, it clears the previous value.)
281         /// </value>
282         public string Category
283         {
284             get
285             {
286                 if (String.IsNullOrEmpty(_category))
287                 {
288                     Interop.AppControl.ErrorCode err = Interop.AppControl.GetCategory(_handle, out _category);
289                     if (err != Interop.AppControl.ErrorCode.None)
290                     {
291                         Log.Warn(LogTag, "Failed to get the category from the appcontrol. Err = " + err);
292                     }
293                 }
294                 return _category;
295             }
296             set
297             {
298                 Interop.AppControl.ErrorCode err = Interop.AppControl.SetCategory(_handle, value);
299                 if (err == Interop.AppControl.ErrorCode.None)
300                 {
301                     _category = value;
302                 }
303                 else
304                 {
305                     Log.Warn(LogTag, "Failed to set the category to the appcontrol. Err = " + err);
306                 }
307             }
308         }
309
310         /// <summary>
311         /// Gets and sets the application ID to explicitly launch.
312         /// </summary>
313         /// <value>
314         /// (if the application ID is null for setter, it clears the previous value.)
315         /// </value>
316         /// <example>
317         /// <code>
318         /// AppControl appControl = new AppControl();
319         /// appControl.ApplicationId = "org.tizen.calculator";
320         /// Log.Debug(LogTag, "ApplicationId: " + appControl.ApplicationId);
321         /// </code>
322         /// </example>
323         public string ApplicationId
324         {
325             get
326             {
327                 if (String.IsNullOrEmpty(_applicationId))
328                 {
329                     Interop.AppControl.ErrorCode err = Interop.AppControl.GetAppId(_handle, out _applicationId);
330                     if (err != Interop.AppControl.ErrorCode.None)
331                     {
332                         Log.Warn(LogTag, "Failed to get the application id from the AppControl. Err = " + err);
333                     }
334                 }
335                 return _applicationId;
336             }
337             set
338             {
339                 Interop.AppControl.ErrorCode err = Interop.AppControl.SetAppId(_handle, value);
340                 if (err == Interop.AppControl.ErrorCode.None)
341                 {
342                     _applicationId = value;
343                 }
344                 else
345                 {
346                     Log.Warn(LogTag, "Failed to set the application id to the AppControl. Err = " + err);
347                 }
348             }
349         }
350
351         /// <summary>
352         /// Gets and sets the launch mode of the application.
353         /// </summary>
354         /// <value>
355         /// Although, LaunchMode were set as AppControlLaunchMode.Group, the
356         /// callee application would be launched as a single mode
357         /// if the manifest file of callee application defined the launch mode as "single".
358         /// This property can just set the preference of the caller application to launch an application.
359         /// Sub-applications, which were launched as a group mode always have own process.
360         /// Since Tizen 3.0, if launch mode is not set in the caller application control,
361         /// this property returns the AppControlLaunchMode.Single launch mode.
362         /// </value>
363         /// <example>
364         /// <code>
365         /// AppControl appControl = new AppControl();
366         /// appControl.LaunchMode = AppControlLaunchMode.Group;
367         /// </code>
368         /// </example>
369         public AppControlLaunchMode LaunchMode
370         {
371             get
372             {
373                 int value = 0;
374                 Interop.AppControl.ErrorCode err = Interop.AppControl.GetLaunchMode(_handle, out value);
375                 if (err != Interop.AppControl.ErrorCode.None)
376                 {
377                     Log.Warn(LogTag, "Failed to get the LaunchMode from the AppControl. Err = " + err);
378                 }
379                 return (AppControlLaunchMode)value;
380             }
381             set
382             {
383                 Interop.AppControl.ErrorCode err = Interop.AppControl.SetLaunchMode(_handle, (int)value);
384                 if (err != Interop.AppControl.ErrorCode.None)
385                 {
386                     Log.Warn(LogTag, "Failed to set the LaunchMode to the AppControl. Err = " + err);
387                 }
388             }
389         }
390
391         /// <summary>
392         /// Gets the collection of the extra data.
393         /// </summary>
394         /// <value>
395         /// Extra data for communication between AppControls.
396         /// </value>
397         /// <example>
398         /// <code>
399         /// AppControl appControl = new AppControl();
400         /// appControl.ExtraData.Add("key", "value");
401         /// ...
402         /// </code>
403         /// </example>
404         public ExtraDataCollection ExtraData
405         {
406             get
407             {
408                 if (_extraData == null)
409                     _extraData = new ExtraDataCollection(_handle);
410                 return _extraData;
411             }
412         }
413
414         #endregion // Public Properties
415
416         /// <summary>
417         /// Retrieves all applications that can be launched to handle the given app_control request.
418         /// </summary>
419         /// <param name="control">The AppControl.</param>
420         /// <returns>ApplicationIds.</returns>
421         /// <exception cref="InvalidOperationException">Thrown when failed because of an invalid parameter.</exception>
422         /// <example>
423         /// <code>
424         /// IEnumerable<string> applicationIds = AppControl.GetMatchedApplicationIds(control);
425         /// if (applicationIds != null)
426         /// {
427         ///     foreach (string id in applicationIds)
428         ///     {
429         ///         // ...
430         ///     }
431         /// }
432         /// </code>
433         /// </example>
434         public static IEnumerable<string> GetMatchedApplicationIds(AppControl control)
435         {
436             if (control == null)
437             {
438                 throw new ArgumentNullException("control");
439             }
440
441             List<string> ids = new List<string>();
442             Interop.AppControl.AppMatchedCallback callback = (handle, applicationId, userData) =>
443             {
444                 if (applicationId == null)
445                 {
446                         return false;
447                 }
448
449                 ids.Add(applicationId);
450                 return true;
451             };
452
453             Interop.AppControl.ErrorCode err = Interop.AppControl.ForeachAppMatched(control._handle, callback, IntPtr.Zero);
454             if (err != Interop.AppControl.ErrorCode.None)
455             {
456                     throw new InvalidOperationException("Failed to get matched application ids. err = " + err);
457             }
458
459             return ids;
460         }
461
462         /// <summary>
463         /// Sends the launch request.
464         /// </summary>
465         /// <remarks>
466         /// The operation is mandatory information for the launch request.
467         /// If the operation is not specified, AppControlOperations.Default is used by default.
468         /// If the operation is AppControlOperations.Default, the application ID is mandatory to explicitly launch the application. \n
469         /// Since Tizen 2.4, the launch request of the service application over out of packages is restricted by the platform.
470         /// Also, implicit launch requests are NOT delivered to service applications since 2.4.
471         /// To launch a service application, an explicit launch request with the application ID given by property ApplicationId MUST be sent.
472         /// </remarks>
473         /// <param name="launchRequest">The AppControl.</param>
474         /// <exception cref="ArgumentNullException">Thrown when failed because of a null argument.</exception>
475         /// <exception cref="InvalidOperationException">Thrown when failed because of an invalid operation.</exception>
476         /// <exception cref="TimeoutException">Thrown when failed because of timeout.</exception>
477         /// <privilege>http://tizen.org/privilege/appmanager.launch</privilege>
478         /// <example>
479         /// <code>
480         /// AppControl appControl = new AppControl();
481         /// appControl.ApplicationId = "org.tizen.calculator";
482         /// AppControl.SendLaunchRequest(appControl);
483         /// </code>
484         /// </example>
485         public static void SendLaunchRequest(AppControl launchRequest)
486         {
487             SendLaunchRequest(launchRequest, null);
488         }
489
490         /// <summary>
491         /// Sends the launch request.
492         /// </summary>
493         /// <remarks>
494         /// The operation is mandatory information for the launch request.
495         /// If the operation is not specified, AppControlOperations.Default is used by default.
496         /// If the operation is AppControlOperations.Default, the application ID is mandatory to explicitly launch the application. \n
497         /// Since Tizen 2.4, the launch request of the service application over out of packages is restricted by the platform.
498         /// Also, implicit launch requests are NOT delivered to service applications since 2.4.
499         /// To launch a service application, an explicit launch request with the application ID given by property ApplicationId MUST be sent.
500         /// </remarks>
501         /// <param name="launchRequest">The AppControl.</param>
502         /// <param name="replyAfterLaunching">The callback function to be called when the reply is delivered.</param>
503         /// <exception cref="ArgumentException">Thrown when failed because of the argument is invalid.</exception>
504         /// <exception cref="Exceptions.AppNotFoundException">Thrown when the application to run is not found.</exception>
505         /// <exception cref="Exceptions.LaunchFailedException">Thrown when the request failed to launch the application.</exception>
506         /// <exception cref="Exceptions.LaunchRejectedException">Thrown when the launch request is rejected.</exception>
507         /// <exception cref="Exceptions.OutOfMemoryException">Thrown when the memory is insufficient.</exception>
508         /// <exception cref="Exceptions.PermissionDeniedException">Thrown when the permission is denied.</exception>
509         /// <exception cref="TimeoutException">Thrown when failed because of timeout.</exception>
510         /// <privilege>http://tizen.org/privilege/appmanager.launch</privilege>
511         /// <example>
512         /// <code>
513         /// AppControl appControl = new AppControl();
514         /// appControl.ApplicationId = "org.tizen.calculator";
515         /// AppControl.SendLaunchRequest(appControl, (launchRequest, replyRequest, result) => {
516         ///     // ...
517         /// });
518         /// </code>
519         /// </example>
520         public static void SendLaunchRequest(AppControl launchRequest, AppControlReplyCallback replyAfterLaunching)
521         {
522             if (launchRequest == null)
523             {
524                 throw new ArgumentNullException("launchRequest");
525             }
526
527             Interop.AppControl.ErrorCode err;
528
529             if (replyAfterLaunching != null)
530             {
531                 int id = 0;
532                 lock (s_replyNativeCallbackMaps)
533                 {
534                     id = s_replyNativeCallbackId++;
535                     s_replyNativeCallbackMaps[id] = (launchRequestHandle, replyRequestHandle, result, userData) =>
536                     {
537                         if (replyAfterLaunching != null)
538                         {
539                             Log.Debug(LogTag, "Reply Callback is launched");
540                             replyAfterLaunching(new AppControl(launchRequestHandle), new AppControl(replyRequestHandle), (AppControlReplyResult)result);
541                             if (result != Interop.AppControl.AppStartedStatus)
542                             {
543                                 lock (s_replyNativeCallbackMaps)
544                                 {
545                                     s_replyNativeCallbackMaps.Remove(id);
546                                 }
547                             }
548                         }
549                     };
550                 }
551                 err = Interop.AppControl.SendLaunchRequest(launchRequest._handle, s_replyNativeCallbackMaps[id], IntPtr.Zero);
552             }
553             else
554             {
555                 err = Interop.AppControl.SendLaunchRequest(launchRequest._handle, null, IntPtr.Zero);
556             }
557
558             if (err != Interop.AppControl.ErrorCode.None)
559             {
560                 switch (err)
561                 {
562                     case Interop.AppControl.ErrorCode.InvalidParameter:
563                         throw new ArgumentException("Invalid Arguments");
564                     case Interop.AppControl.ErrorCode.TimedOut:
565                         throw new TimeoutException("Timed out");
566                     case Interop.AppControl.ErrorCode.OutOfMemory:
567                         throw new Exceptions.OutOfMemoryException("Out-of-memory");
568                     case Interop.AppControl.ErrorCode.AppNotFound:
569                         throw new Exceptions.AppNotFoundException("App not found");
570                     case Interop.AppControl.ErrorCode.LaunchRejected:
571                         throw new Exceptions.LaunchRejectedException("Launch rejected");
572                     case Interop.AppControl.ErrorCode.LaunchFailed:
573                         throw new Exceptions.LaunchFailedException("Launch failed");
574                     case Interop.AppControl.ErrorCode.PermissionDenied:
575                         throw new Exceptions.PermissionDeniedException("Permission denied");
576
577                     default:
578                         throw new Exceptions.LaunchRejectedException("Launch rejected");
579                 }
580             }
581         }
582
583         /// <summary>
584         /// Sends the terminate request to the application that is launched by AppControl.
585         /// </summary>
586         /// <remarks>
587         /// You are not allowed to terminate other general applications using this API.
588         /// This API can be used to terminate sub-applications, which were launched as a group mode by the caller application.
589         /// Once the callee application is being terminated by this API,
590         /// other applications, which were launched by the callee application as a group mode will be terminated as well.
591         /// </remarks>
592         /// <param name="terminateRequest">The AppControl.</param>
593         /// <exception cref="ArgumentException">Thrown when failed because of the argument is invalid.</exception>
594         /// <exception cref="InvalidOperationException">Thrown when failed because of an invalid operation.</exception>
595         /// <exception cref="TimeoutException">Thrown when failed because of timeout.</exception>
596         /// <example>
597         /// <code>
598         /// AppControl terminateRequest = new AppControl();
599         /// terminateRequest.ApplicationId = "org.tizen.calculator";
600         /// AppControl.SendTerminateRequest(terminateRequest);
601         /// </code>
602         /// </example>
603         public static void SendTerminateRequest(AppControl terminateRequest)
604         {
605             if (terminateRequest == null)
606             {
607                 throw new ArgumentNullException("terminateRequest");
608             }
609             Interop.AppControl.ErrorCode err;
610
611             err = Interop.AppControl.SendTerminateRequest(terminateRequest._handle);
612
613             if (err != Interop.AppControl.ErrorCode.None)
614             {
615                 switch (err)
616                 {
617                     case Interop.AppControl.ErrorCode.InvalidParameter:
618                         throw new ArgumentException("Invalid Arguments");
619                     case Interop.AppControl.ErrorCode.TimedOut:
620                         throw new TimeoutException("Timed out");
621                     default:
622                         throw new InvalidOperationException("Error = " + err);
623                 }
624             }
625         }
626
627         /// <summary>
628         /// Class for extra data.
629         /// </summary>
630         public class ExtraDataCollection
631         {
632             private readonly SafeAppControlHandle _handle;
633
634             internal ExtraDataCollection(SafeAppControlHandle handle)
635             {
636                 _handle = handle;
637             }
638
639             /// <summary>
640             /// Adds extra data.
641             /// </summary>
642             /// <remarks>
643             /// The function replaces any existing value for the given key.
644             /// </remarks>
645             /// <param name="key">The name of the extra data.</param>
646             /// <param name="value">The value associated with the given key.</param>
647             /// <exception cref="ArgumentNullException">Thrown when a key or a value is a zero-length string.</exception>
648             /// <exception cref="ArgumentException">Thrown when the application tries to use the same key with the system-defined key.</exception>
649             /// <example>
650             /// <code>
651             /// AppControl appControl = new AppControl();
652             /// appControl.ExtraData.Add("myKey", "myValue");
653             /// </code>
654             /// </example>
655             public void Add(string key, string value)
656             {
657                 if (string.IsNullOrEmpty(key))
658                 {
659                     throw new ArgumentNullException("key");
660                 }
661                 if (string.IsNullOrEmpty(value))
662                 {
663                     throw new ArgumentNullException("value");
664                 }
665                 Interop.AppControl.ErrorCode err = Interop.AppControl.AddExtraData(_handle, key, value);
666                 if (err != Interop.AppControl.ErrorCode.None)
667                 {
668                     switch (err)
669                     {
670                         case Interop.AppControl.ErrorCode.InvalidParameter:
671                             throw new ArgumentException("Invalid parameter: key or value is a zero-length string");
672                         case Interop.AppControl.ErrorCode.KeyRejected:
673                             throw new ArgumentException("Key is rejected: the key is system-defined key.");
674                         default:
675                             throw new InvalidOperationException("Error = " + err);
676                     }
677                 }
678             }
679
680             /// <summary>
681             /// Adds extra data.
682             /// </summary>
683             /// <remarks>
684             /// The function replaces any existing value for the given key.
685             /// </remarks>
686             /// <param name="key">The name of the extra data.</param>
687             /// <param name="value">The value associated with the given key.</param>
688             /// <exception cref="ArgumentNullException">Thrown when key or value is a zero-length string.</exception>
689             /// <exception cref="ArgumentException">Thrown when the application tries to use the same key with the system-defined key.</exception>
690             /// <example>
691             /// <code>
692             /// AppControl appControl = new AppControl();
693             /// string[] myValues = new string[] { "first", "second", "third" };
694             /// appControl.ExtraData.Add("myKey", myValues);
695             /// </code>
696             /// </example>
697             public void Add(string key, IEnumerable<string> value)
698             {
699                 if (string.IsNullOrEmpty(key))
700                 {
701                     throw new ArgumentNullException("key");
702                 }
703                 if (value == null)
704                 {
705                     throw new ArgumentNullException("value");
706                 }
707                 string[] valueArray = value.ToArray();
708                 Interop.AppControl.ErrorCode err = Interop.AppControl.AddExtraDataArray(_handle, key, valueArray, valueArray.Length);
709                 if (err != Interop.AppControl.ErrorCode.None)
710                 {
711                     switch (err)
712                     {
713                         case Interop.AppControl.ErrorCode.InvalidParameter:
714                             throw new ArgumentException("Invalid parameter: key or value is a zero-length string");
715                         case Interop.AppControl.ErrorCode.KeyRejected:
716                             throw new ArgumentException("Key is rejected: the key is system-defined key.");
717                         default:
718                             throw new InvalidOperationException("Error = " + err);
719                     }
720                 }
721             }
722
723             /// <summary>
724             /// Gets the extra data.
725             /// </summary>
726             /// <typeparam name="T">Only string and IEnumerable&lt;string&gt;</typeparam>
727             /// <param name="key">The name of extra data.</param>
728             /// <returns>The value associated with the given key.</returns>
729             /// <exception cref="ArgumentNullException">Thrown when the key is an invalid parameter.</exception>
730             /// <exception cref="KeyNotFoundException">Thrown when the key is not found.</exception>
731             /// <exception cref="ArgumentException">Thrown when the key is rejected.</exception>
732             /// <example>
733             /// <code>
734             /// AppControl appControl = new AppControl();
735             /// string myValue = appControl.ExtraData.Get<string>("myKey");
736             /// </code>
737             /// </example>
738             public T Get<T>(string key)
739             {
740                 object ret = Get(key);
741                 return (T)ret;
742             }
743
744             /// <summary>
745             /// Gets the extra data.
746             /// </summary>
747             /// <param name="key">The name of extra data.</param>
748             /// <returns>The value associated with the given key.</returns>
749             /// <exception cref="ArgumentNullException">Thrown when the key is an invalid parameter.</exception>
750             /// <exception cref="KeyNotFoundException">Thrown when the key is not found.</exception>
751             /// <exception cref="ArgumentException">Thrown when the key is rejected.</exception>
752             /// <example>
753             /// <code>
754             /// AppControl appControl = new AppControl();
755             /// string myValue = appControl.ExtraData.Get("myKey") as string;
756             /// if (myValue != null)
757             /// {
758             ///     // ...
759             /// }
760             /// </code>
761             /// </example>
762             public object Get(string key)
763             {
764                 if (IsCollection(key))
765                 {
766                     return GetDataCollection(key);
767                 }
768                 else
769                 {
770                     return GetData(key);
771                 }
772             }
773
774             /// <summary>
775             /// Gets all keys in extra data.
776             /// </summary>
777             /// <returns>The keys in the AppControl.</returns>
778             /// <exception cref="InvalidOperationException">Thrown when the key is an invalid parameter.</exception>
779             /// <example>
780             /// <code>
781             /// AppControl appControl = new AppControl();
782             /// IEnumerable<string> keys = appControl.GetKeys();
783             /// if (keys != null)
784             /// {
785             ///     foreach (string key in keys)
786             ///     {
787             ///         // ...
788             ///     }
789             /// }
790             /// </code>
791             /// </example>
792             public IEnumerable<string> GetKeys()
793             {
794                 List<string> keys = new List<string>();
795                 Interop.AppControl.ExtraDataCallback callback = (handle, key, userData) =>
796                 {
797                     if (key == null)
798                     {
799                         return false;
800                     }
801
802                     keys.Add(key);
803                     return true;
804                 };
805
806                 Interop.AppControl.ErrorCode err = Interop.AppControl.ForeachExtraData(_handle, callback, IntPtr.Zero);
807                 if (err != Interop.AppControl.ErrorCode.None)
808                 {
809                     throw new InvalidOperationException("Failed to get keys. err = " + err);
810                 }
811
812                 return keys;
813             }
814
815             /// <summary>
816             /// Tries getting the extra data.
817             /// </summary>
818             /// <param name="key">The name of extra data.</param>
819             /// <param name="value">The value associated with the given key.</param>
820             /// <returns>The result whether getting the value is done.</returns>
821             /// <exception cref="ArgumentNullException">Thrown when the key is an invalid parameter.</exception>
822             /// <exception cref="KeyNotFoundException">Thrown when the key is not found.</exception>
823             /// <exception cref="ArgumentException">Thrown when the key is rejected.</exception>
824             /// <example>
825             /// <code>
826             /// AppControl appControl = new AppControl();
827             /// string myValue = string.Empty;
828             /// bool result = appControl.ExtraData.TryGet("myKey", out myValue);
829             /// if (result != null)
830             /// {
831             ///     // ...
832             /// }
833             /// </code>
834             /// </example>
835             public bool TryGet(string key, out string value)
836             {
837                 if (string.IsNullOrEmpty(key))
838                 {
839                     throw new ArgumentNullException("key");
840                 }
841                 Interop.AppControl.GetExtraData(_handle, key, out value);
842                 if (value != null)
843                 {
844                     return true;
845                 }
846                 else
847                 {
848                     value = default(string);
849                     return false;
850                 }
851             }
852
853             /// <summary>
854             /// Tries getting the extra data.
855             /// </summary>
856             /// <param name="key">The name of extra data.</param>
857             /// <param name="value">The value associated with the given key.</param>
858             /// <returns>The result whether getting the value is done.</returns>
859             /// <exception cref="ArgumentNullException">Thrown when the key is an invalid parameter.</exception>
860             /// <exception cref="KeyNotFoundException">Thrown when the key is not found.</exception>
861             /// <exception cref="ArgumentException">Thrown when the key is rejected.</exception>
862             /// <example>
863             /// <code>
864             /// AppControl appControl = new AppControl();
865             /// IEnumerable<string> myValue = null;
866             /// bool result = appControl.ExtraData.TryGet("myKey", out myValue);
867             /// if (result)
868             /// {
869             ///     foreach (string value in myValue)
870             ///     {
871             ///         // ...
872             ///     }
873             /// }
874             /// </code>
875             /// </example>
876             public bool TryGet(string key, out IEnumerable<string> value)
877             {
878                 if (string.IsNullOrEmpty(key))
879                 {
880                     throw new ArgumentNullException("key");
881                 }
882                 IntPtr valuePtr = IntPtr.Zero;
883                 int len = -1;
884                 Interop.AppControl.ErrorCode err = Interop.AppControl.GetExtraDataArray(_handle, key, out valuePtr, out len);
885                 if (err == Interop.AppControl.ErrorCode.None && valuePtr != IntPtr.Zero)
886                 {
887                     List<string> stringList = new List<string>();
888                     for (int i = 0; i < len; ++i)
889                     {
890                         IntPtr charArr = Marshal.ReadIntPtr(valuePtr, IntPtr.Size * i);
891                         stringList.Add(Marshal.PtrToStringAnsi(charArr));
892                         Interop.Libc.Free(charArr);
893                     }
894                     Interop.Libc.Free(valuePtr);
895                     value = stringList;
896                     return true;
897                 }
898                 else
899                 {
900                     value = default(IEnumerable<string>);
901                     return false;
902                 }
903             }
904
905             /// <summary>
906             /// Removes the extra data.
907             /// </summary>
908             /// <param name="key">The name of the extra data.</param>
909             /// <exception cref="ArgumentNullException">Thrown when the key is a zero-length string.</exception>
910             /// <exception cref="KeyNotFoundException">Thrown when the key is not found.</exception>
911             /// <exception cref="ArgumentException">Thrown when the key is rejected.</exception>
912             /// <example>
913             /// <code>
914             /// AppControl appControl = new AppControl();
915             /// appControl.ExtraData.Remove("myKey");
916             /// </code>
917             /// </example>
918             public void Remove(string key)
919             {
920                 if (string.IsNullOrEmpty(key))
921                 {
922                     throw new ArgumentNullException("key");
923                 }
924                 Interop.AppControl.ErrorCode err = Interop.AppControl.RemoveExtraData(_handle, key);
925                 if (err != Interop.AppControl.ErrorCode.None)
926                 {
927                     switch (err)
928                     {
929                         case Interop.AppControl.ErrorCode.InvalidParameter:
930                             throw new ArgumentException("Invalid parameter: key is a zero-length string");
931                         case Interop.AppControl.ErrorCode.KeyNotFound:
932                             throw new KeyNotFoundException("Key is not found"); ;
933                         case Interop.AppControl.ErrorCode.KeyRejected:
934                             throw new ArgumentException("Key is rejected: the key is system-defined key.");
935                         default:
936                             throw new InvalidOperationException("Error = " + err);
937                     }
938                 }
939             }
940
941             /// <summary>
942             /// Counts keys in the extra data.
943             /// </summary>
944             /// <returns>The number of counting keys.</returns>
945             /// <exception cref="InvalidOperationException">Thrown when the key is an invalid parameter.</exception>
946             /// <example>
947             /// <code>
948             /// AppControl appControl = new AppControl();
949             /// int numberOfKeys = appControl.ExtraData.Count();
950             /// </code>
951             /// </example>
952             public int Count()
953             {
954                 return GetKeys().Count();
955             }
956
957             /// <summary>
958             /// Checks whether the extra data associated with the given key is of the collection data type.
959             /// </summary>
960             /// <param name="key">The name of the extra data.</param>
961             /// <returns>If true, the extra data is of the array data type, otherwise false.</returns>
962             /// <exception cref="ArgumentNullException">Thrown when the key is a zero-length string.</exception>
963             /// <exception cref="InvalidOperationException">Thrown when failed to check the key.</exception>
964             /// <example>
965             /// <code>
966             /// AppControl appControl = new AppControl();
967             /// bool result = appControl.ExtraData.IsCollection("myKey");
968             /// </code>
969             /// </example>
970             public bool IsCollection(string key)
971             {
972                 if (string.IsNullOrEmpty(key))
973                 {
974                     throw new ArgumentNullException("key");
975                 }
976                 bool isArray = false;
977                 Interop.AppControl.ErrorCode err = Interop.AppControl.IsExtraDataArray(_handle, key, out isArray);
978                 if (err != Interop.AppControl.ErrorCode.None)
979                 {
980                     throw new InvalidOperationException("Error = " + err);
981                 }
982                 return isArray;
983             }
984
985             private string GetData(string key)
986             {
987                 if (string.IsNullOrEmpty(key))
988                 {
989                     throw new ArgumentNullException("key");
990                 }
991                 string value = string.Empty;
992                 Interop.AppControl.ErrorCode err = Interop.AppControl.GetExtraData(_handle, key, out value);
993                 if (err != Interop.AppControl.ErrorCode.None)
994                 {
995                     switch (err)
996                     {
997                         case Interop.AppControl.ErrorCode.InvalidParameter:
998                             throw new ArgumentException("Invalid parameter: key is a zero-length string");
999                         case Interop.AppControl.ErrorCode.KeyNotFound:
1000                             throw new KeyNotFoundException("Key is not found"); ;
1001                         case Interop.AppControl.ErrorCode.InvalidDataType:
1002                             throw new ArgumentException("Invalid data type: value is data collection type");
1003                         case Interop.AppControl.ErrorCode.KeyRejected:
1004                             throw new ArgumentException("Key is rejected: the key is system-defined key.");
1005                         default:
1006                             throw new InvalidOperationException("Error = " + err);
1007                     }
1008                 }
1009                 return value;
1010             }
1011
1012             private IEnumerable<string> GetDataCollection(string key)
1013             {
1014                 if (string.IsNullOrEmpty(key))
1015                 {
1016                     throw new ArgumentNullException("key");
1017                 }
1018                 IntPtr valuePtr = IntPtr.Zero;
1019                 int len = -1;
1020                 Interop.AppControl.ErrorCode err = Interop.AppControl.GetExtraDataArray(_handle, key, out valuePtr, out len);
1021                 if (err != Interop.AppControl.ErrorCode.None)
1022                 {
1023                     switch (err)
1024                     {
1025                         case Interop.AppControl.ErrorCode.InvalidParameter:
1026                             throw new ArgumentException("Invalid parameter: key is a zero-length string");
1027                         case Interop.AppControl.ErrorCode.KeyNotFound:
1028                             throw new KeyNotFoundException("Key is not found"); ;
1029                         case Interop.AppControl.ErrorCode.InvalidDataType:
1030                             throw new ArgumentException("Invalid data type: value is data collection type");
1031                         case Interop.AppControl.ErrorCode.KeyRejected:
1032                             throw new ArgumentException("Key is rejected: the key is system-defined key.");
1033                         default:
1034                             throw new InvalidOperationException("Error = " + err);
1035                     }
1036                 }
1037
1038                 List<string> valueArray = new List<string>();
1039                 if (valuePtr != IntPtr.Zero)
1040                 {
1041                     for (int i = 0; i < len; ++i)
1042                     {
1043                         IntPtr charArr = Marshal.ReadIntPtr(valuePtr, IntPtr.Size * i);
1044                         valueArray.Add(Marshal.PtrToStringAnsi(charArr));
1045                         Interop.Libc.Free(charArr);
1046                     }
1047                     Interop.Libc.Free(valuePtr);
1048                 }
1049                 return valueArray;
1050             }
1051         }
1052     }
1053 }