[Tizen.Applications.Common] Add SendLaunchRequestAsync Method (#545)
authorhjhun <36876573+hjhun@users.noreply.github.com>
Tue, 4 Dec 2018 02:03:51 +0000 (11:03 +0900)
committerHyunho Kang <hhstark.kang@samsung.com>
Tue, 4 Dec 2018 02:03:51 +0000 (11:03 +0900)
* [Tizen.Applications.Common] Add SendLaunchRequestAsync Method

Signed-off-by: Hwankyu Jhun <h.jhun@samsung.com>
* Update doxygen

Added:
 - returns

Signed-off-by: Hwankyu Jhun <h.jhun@samsung.com>
src/Tizen.Applications.Common/Interop/Interop.AppControl.cs
src/Tizen.Applications.Common/Tizen.Applications/AppControl.cs
src/Tizen.Applications.Common/Tizen.Applications/AppControlResult.cs [new file with mode: 0755]

index c379698..a524acd 100755 (executable)
@@ -25,10 +25,18 @@ internal static partial class Interop
     {
         internal const int AppStartedStatus = 1;
 
+        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
         internal delegate bool ExtraDataCallback(IntPtr handle, string key, IntPtr userData);
+
+        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
         internal delegate bool AppMatchedCallback(IntPtr handle, string applicationId, IntPtr userData);
+
+        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
         internal delegate void ReplyCallback(IntPtr request, IntPtr reply, int result, IntPtr userData);
 
+        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+        internal delegate void ResultCallback(IntPtr request, int result, IntPtr userData);
+
         internal enum ErrorCode
         {
             None = Tizen.Internals.Errors.ErrorCode.None,
@@ -134,5 +142,8 @@ internal static partial class Interop
 
         [DllImport(Libraries.AppControl, EntryPoint = "app_control_enable_app_started_result_event")]
         internal static extern ErrorCode EnableAppStartedResultEvent(SafeAppControlHandle handle);
+
+        [DllImport(Libraries.AppControl, EntryPoint = "app_control_send_launch_request_async")]
+        internal static extern ErrorCode SendLaunchRequestAsync(SafeAppControlHandle handle, ResultCallback resultCallback, ReplyCallback replyCallback, IntPtr userData);
     }
 }
index 836928f..3d05d1f 100755 (executable)
@@ -18,6 +18,7 @@ using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Runtime.InteropServices;
+using System.Threading.Tasks;
 
 namespace Tizen.Applications
 {
@@ -45,8 +46,9 @@ namespace Tizen.Applications
     {
         private const string LogTag = "Tizen.Applications";
 
-        private static Dictionary<int, Interop.AppControl.ReplyCallback> s_replyNativeCallbackMaps = new Dictionary<int, Interop.AppControl.ReplyCallback>();
-        private static int s_replyNativeCallbackId = 0;
+        private static Dictionary<int, Interop.AppControl.ResultCallback> s_resultNativeCallbackMaps = new Dictionary<int, Interop.AppControl.ResultCallback>();
+        private static Dictionary<int, AppControlReplyCallback> s_replyCallbackMaps = new Dictionary<int, AppControlReplyCallback>();
+        private static int s_reaustId = 0;
 
         private readonly SafeAppControlHandle _handle;
 
@@ -123,7 +125,26 @@ namespace Tizen.Applications
             }
         }
 
-        #region Public Properties
+        private static Interop.AppControl.ReplyCallback s_replyNativeCallback = (launchHandle, replyHandle, result, userData) =>
+        {
+            int requestId = (int)userData;
+            lock (s_replyCallbackMaps)
+            {
+                if (s_replyCallbackMaps.ContainsKey(requestId))
+                {
+                    s_replyCallbackMaps[requestId](new AppControl(launchHandle), new AppControl(replyHandle), (AppControlReplyResult)result);
+                    if (result != Interop.AppControl.AppStartedStatus)
+                    {
+                        lock (s_replyCallbackMaps)
+                        {
+                            s_replyCallbackMaps.Remove(requestId);
+                        }
+                    }
+                }
+            }
+        };
+
+#region Public Properties
 
         /// <summary>
         /// Gets the SafeAppControlHandle instance.
@@ -423,7 +444,7 @@ namespace Tizen.Applications
             }
         }
 
-        #endregion // Public Properties
+#endregion // Public Properties
 
         /// <summary>
         /// Retrieves all applications that can be launched to handle the given app_control request.
@@ -544,26 +565,12 @@ namespace Tizen.Applications
             if (replyAfterLaunching != null)
             {
                 int id = 0;
-                lock (s_replyNativeCallbackMaps)
+                lock (s_replyCallbackMaps)
                 {
-                    id = s_replyNativeCallbackId++;
-                    s_replyNativeCallbackMaps[id] = (launchRequestHandle, replyRequestHandle, result, userData) =>
-                    {
-                        if (replyAfterLaunching != null)
-                        {
-                            Log.Debug(LogTag, "Reply Callback is launched");
-                            replyAfterLaunching(new AppControl(launchRequestHandle), new AppControl(replyRequestHandle), (AppControlReplyResult)result);
-                            if (result != Interop.AppControl.AppStartedStatus)
-                            {
-                                lock (s_replyNativeCallbackMaps)
-                                {
-                                    s_replyNativeCallbackMaps.Remove(id);
-                                }
-                            }
-                        }
-                    };
+                    id = s_reaustId++;
+                    s_replyCallbackMaps[id] = replyAfterLaunching;
                 }
-                err = Interop.AppControl.SendLaunchRequest(launchRequest._handle, s_replyNativeCallbackMaps[id], IntPtr.Zero);
+                err = Interop.AppControl.SendLaunchRequest(launchRequest._handle, s_replyNativeCallback, (IntPtr)id);
             }
             else
             {
@@ -641,6 +648,83 @@ namespace Tizen.Applications
         }
 
         /// <summary>
+        /// Sends the launch request asynchronously.
+        /// </summary>
+        /// <remarks>
+        /// The operation is mandatory information for the launch request.
+        /// If the operation is not specified, AppControlOperations.Default is used by default.
+        /// If the operation is AppControlOperations.Default, the application ID is mandatory to explicitly launch the application.<br/>
+        /// Since Tizen 2.4, the launch request of the service application over out of packages is restricted by the platform.
+        /// Also, implicit launch requests are NOT delivered to service applications since 2.4.
+        /// To launch a service application, an explicit launch request with the application ID given by property ApplicationId MUST be sent.
+        /// </remarks>
+        /// <param name="launchRequest">The AppControl.</param>
+        /// <param name="replyAfterLaunching">The callback function to be called when the reply is delivered.</param>
+        /// <returns>A task with the result of the launch request.</returns>
+        /// <exception cref="ArgumentException">Thrown when failed because of the argument is invalid.</exception>
+        /// <exception cref="Exceptions.AppNotFoundException">Thrown when the application to run is not found.</exception>
+        /// <exception cref="Exceptions.LaunchRejectedException">Thrown when the launch request is rejected.</exception>
+        /// <privilege>http://tizen.org/privilege/appmanager.launch</privilege>
+        /// <since_tizen> 6 </since_tizen>
+        public static Task<AppControlResult> SendLaunchRequestAsync(AppControl launchRequest, AppControlReplyCallback replyAfterLaunching)
+        {
+            if (launchRequest == null)
+            {
+                throw new ArgumentNullException(nameof(launchRequest));
+            }
+
+            var task = new TaskCompletionSource<AppControlResult>();
+            Interop.AppControl.ErrorCode err;
+            int requestId = 0;
+
+            lock (s_resultNativeCallbackMaps)
+            {
+                requestId = s_reaustId++;
+                s_resultNativeCallbackMaps[requestId] = (handle, result, userData) =>
+                {
+                    task.SetResult((AppControlResult)result);
+                    lock (s_resultNativeCallbackMaps)
+                    {
+                        s_resultNativeCallbackMaps.Remove((int)userData);
+                    }
+                };
+            }
+
+            if (replyAfterLaunching != null)
+            {
+                lock (s_replyCallbackMaps)
+                {
+                    s_replyCallbackMaps[requestId] = replyAfterLaunching;
+                }
+                err = Interop.AppControl.SendLaunchRequestAsync(launchRequest.SafeAppControlHandle, s_resultNativeCallbackMaps[requestId], s_replyNativeCallback, (IntPtr)requestId);
+            }
+            else
+            {
+                err = Interop.AppControl.SendLaunchRequestAsync(launchRequest.SafeAppControlHandle, s_resultNativeCallbackMaps[requestId], null, (IntPtr)requestId);
+            }
+
+            if (err != Interop.AppControl.ErrorCode.None)
+            {
+                switch (err)
+                {
+                    case Interop.AppControl.ErrorCode.InvalidParameter:
+                        throw new ArgumentException("Invalid Arguments");
+                    case Interop.AppControl.ErrorCode.AppNotFound:
+                        throw new Exceptions.AppNotFoundException("App not found");
+                    case Interop.AppControl.ErrorCode.LaunchRejected:
+                        throw new Exceptions.LaunchRejectedException("Launch rejected");
+                    case Interop.AppControl.ErrorCode.PermissionDenied:
+                        throw new Exceptions.PermissionDeniedException("Permission denied");
+
+                    default:
+                        throw new Exceptions.LaunchRejectedException("Launch rejected");
+                }
+            }
+
+            return task.Task;
+        }
+
+        /// <summary>
         /// Class for extra data.
         /// </summary>
         /// <since_tizen> 3 </since_tizen>
diff --git a/src/Tizen.Applications.Common/Tizen.Applications/AppControlResult.cs b/src/Tizen.Applications.Common/Tizen.Applications/AppControlResult.cs
new file mode 100755 (executable)
index 0000000..f9fa1ef
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+namespace Tizen.Applications
+{
+    /// <summary>
+    /// Enumeration for the application control result.
+    /// </summary>
+    /// <since_tizen> 6 </since_tizen>
+    public enum AppControlResult
+    {
+        /// <summary>
+        /// Successful.
+        /// </summary>
+        None = Interop.AppControl.ErrorCode.None,
+
+        /// <summary>
+        /// Invalid parameter.
+        /// </summary>
+        InvalidParameter = Interop.AppControl.ErrorCode.InvalidParameter,
+
+        /// <summary>
+        /// The application to run the given launch request is not found.
+        /// </summary>
+        AppNotFound = Interop.AppControl.ErrorCode.AppNotFound,
+
+        /// <summary>
+        /// The application cannot be launched in current context.
+        /// </summary>
+        LaunchRejected = Interop.AppControl.ErrorCode.LaunchRejected,
+
+        /// <summary>
+        /// Permission denied.
+        /// </summary>
+        PermissionDenied = Interop.AppControl.ErrorCode.PermissionDenied,
+    }
+}