[NUI] Add UI thread feature (#4358)
authordkdk-ryu <dkdk.ryu@samsung.com>
Fri, 8 Jul 2022 04:20:33 +0000 (13:20 +0900)
committerJaehyun Cho <jaehyun0cho@gmail.com>
Tue, 12 Jul 2022 02:41:27 +0000 (11:41 +0900)
* [NUI] Add UI thread feature

UI thread means that the events related to GUI (e.g. touch, key, window events) are emitted on the UI thread.
In the main thread, the LowBattery, LowMemory and etc events are emitted.

* [NUI] Add UIThread example app

src/Tizen.NUI/src/internal/Application/Application.cs
src/Tizen.NUI/src/internal/Application/NUICoreBackend.cs
src/Tizen.NUI/src/internal/Interop/Interop.Application.cs
src/Tizen.NUI/src/internal/Interop/NDalicPINVOKE.cs
src/Tizen.NUI/src/public/Application/NUIApplication.cs
test/Tizen.NUI.UIThread/Tizen.NUI.UIThread.cs [new file with mode: 0644]
test/Tizen.NUI.UIThread/Tizen.NUI.UIThread.csproj [new file with mode: 0644]
test/Tizen.NUI.UIThread/shared/res/Tizen.NUI.UIThread.png [new file with mode: 0644]
test/Tizen.NUI.UIThread/tizen-manifest.xml [new file with mode: 0644]

index effb0d8..5e3ce7e 100755 (executable)
@@ -463,6 +463,56 @@ namespace Tizen.NUI
                 appControlSignal = null;
             }
 
+            //Task
+            if (applicationTaskInitEventCallbackDelegate != null)
+            {
+                taskInitSignal?.Disconnect(applicationTaskInitEventCallbackDelegate);
+                taskInitSignal?.Dispose();
+                taskInitSignal = null;
+            }
+
+            if (applicationTaskTerminateEventCallbackDelegate != null)
+            {
+                taskTerminateSignal?.Disconnect(applicationTaskTerminateEventCallbackDelegate);
+                taskTerminateSignal?.Dispose();
+                taskTerminateSignal = null;
+            }
+
+            if (applicationTaskLanguageChangedEventCallbackDelegate != null)
+            {
+                taskLanguageChangedSignal?.Disconnect(applicationTaskLanguageChangedEventCallbackDelegate);
+                taskLanguageChangedSignal?.Dispose();
+                taskLanguageChangedSignal = null;
+            }
+
+            if (applicationTaskRegionChangedEventCallbackDelegate != null)
+            {
+                taskRegionChangedSignal?.Disconnect(applicationTaskRegionChangedEventCallbackDelegate);
+                taskRegionChangedSignal?.Dispose();
+                taskRegionChangedSignal = null;
+            }
+
+            if (applicationTaskBatteryLowEventCallbackDelegate != null)
+            {
+                taskBatteryLowSignal?.Disconnect(applicationTaskBatteryLowEventCallbackDelegate);
+                taskBatteryLowSignal?.Dispose();
+                taskBatteryLowSignal = null;
+            }
+
+            if (applicationTaskMemoryLowEventCallbackDelegate != null)
+            {
+                taskMemoryLowSignal?.Disconnect(applicationTaskMemoryLowEventCallbackDelegate);
+                taskMemoryLowSignal?.Dispose();
+                taskMemoryLowSignal = null;
+            }
+
+            if (applicationTaskAppControlEventCallbackDelegate != null)
+            {
+                taskAppControlSignal?.Disconnect(applicationTaskAppControlEventCallbackDelegate);
+                taskAppControlSignal?.Dispose();
+                taskAppControlSignal = null;
+            }
+
             window?.Dispose();
             window = null;
 
@@ -548,6 +598,34 @@ namespace Tizen.NUI
         private NUIApplicationAppControlEventCallbackDelegate applicationAppControlEventCallbackDelegate;
         private ApplicationControlSignal appControlSignal;
 
+        private DaliEventHandler<object, NUIApplicationInitEventArgs> applicationTaskInitEventHandler;
+        private NUIApplicationInitEventCallbackDelegate applicationTaskInitEventCallbackDelegate;
+        private ApplicationSignal taskInitSignal;
+
+        private DaliEventHandler<object, NUIApplicationTerminatingEventArgs> applicationTaskTerminateEventHandler;
+        private NUIApplicationTerminateEventCallbackDelegate applicationTaskTerminateEventCallbackDelegate;
+        private ApplicationSignal taskTerminateSignal;
+
+        private DaliEventHandler<object, NUIApplicationLanguageChangedEventArgs> applicationTaskLanguageChangedEventHandler;
+        private NUIApplicationLanguageChangedEventCallbackDelegate applicationTaskLanguageChangedEventCallbackDelegate;
+        private ApplicationSignal taskLanguageChangedSignal;
+
+        private DaliEventHandler<object, NUIApplicationRegionChangedEventArgs> applicationTaskRegionChangedEventHandler;
+        private NUIApplicationRegionChangedEventCallbackDelegate applicationTaskRegionChangedEventCallbackDelegate;
+        private ApplicationSignal taskRegionChangedSignal;
+
+        private DaliEventHandler<object, NUIApplicationBatteryLowEventArgs> applicationTaskBatteryLowEventHandler;
+        private NUIApplicationBatteryLowEventCallbackDelegate applicationTaskBatteryLowEventCallbackDelegate;
+        private LowBatterySignalType taskBatteryLowSignal;
+
+        private DaliEventHandler<object, NUIApplicationMemoryLowEventArgs> applicationTaskMemoryLowEventHandler;
+        private NUIApplicationMemoryLowEventCallbackDelegate applicationTaskMemoryLowEventCallbackDelegate;
+        private LowMemorySignalType taskMemoryLowSignal;
+
+        private DaliEventHandler<object, NUIApplicationAppControlEventArgs> applicationTaskAppControlEventHandler;
+        private NUIApplicationAppControlEventCallbackDelegate applicationTaskAppControlEventCallbackDelegate;
+        private ApplicationControlSignal taskAppControlSignal;
+
         private Window window;
 
         /**
@@ -996,6 +1074,300 @@ namespace Tizen.NUI
             }
         }
 
+        /// <summary>
+        /// @brief Event for Initialized signal which can be used to subscribe/unsubscribe the event handler
+        ///  provided by the user. Initialized signal is emitted when application is initialized
+        /// </summary>
+        public event DaliEventHandler<object, NUIApplicationInitEventArgs> TaskInitialized
+        {
+            add
+            {
+                // Restricted to only one listener
+                if (applicationTaskInitEventHandler == null)
+                {
+                    Tizen.Log.Fatal("NUI", "TaskInitialized Property adding");
+                    applicationTaskInitEventHandler += value;
+                    applicationTaskInitEventCallbackDelegate = new NUIApplicationInitEventCallbackDelegate(OnApplicationTaskInit);
+                    taskInitSignal = this.TaskInitSignal();
+                    taskInitSignal?.Connect(applicationTaskInitEventCallbackDelegate);
+                }
+            }
+
+            remove
+            {
+                if (applicationTaskInitEventHandler != null)
+                {
+                    taskInitSignal?.Disconnect(applicationTaskInitEventCallbackDelegate);
+                    taskInitSignal?.Dispose();
+                    taskInitSignal = null;
+                }
+
+                applicationTaskInitEventHandler -= value;
+            }
+        }
+
+        private void OnApplicationTaskInit(IntPtr data)
+        {
+            if (applicationTaskInitEventHandler != null)
+            {
+                NUIApplicationInitEventArgs e = new NUIApplicationInitEventArgs();
+                e.Application = this;
+                applicationTaskInitEventHandler.Invoke(this, e);
+            }
+
+        }
+
+        /// <summary>
+        /// @brief Event for Terminated signal which can be used to subscribe/unsubscribe the event handler
+        ///  provided by the user. Terminated signal is emitted when application is terminating
+        /// </summary>
+        public event DaliEventHandler<object, NUIApplicationTerminatingEventArgs> TaskTerminating
+        {
+            add
+            {
+                // Restricted to only one listener
+                if (applicationTaskTerminateEventHandler == null)
+                {
+                    applicationTaskTerminateEventHandler += value;
+
+                    applicationTaskTerminateEventCallbackDelegate = new NUIApplicationTerminateEventCallbackDelegate(OnNUIApplicationTaskTerminate);
+                    taskTerminateSignal = this.TaskTerminateSignal();
+                    taskTerminateSignal?.Connect(applicationTaskTerminateEventCallbackDelegate);
+                }
+            }
+
+            remove
+            {
+                if (applicationTaskTerminateEventHandler != null)
+                {
+                    taskTerminateSignal?.Disconnect(applicationTaskTerminateEventCallbackDelegate);
+                    taskTerminateSignal?.Dispose();
+                    taskTerminateSignal = null;
+                }
+
+                applicationTaskTerminateEventHandler -= value;
+            }
+        }
+
+        private void OnNUIApplicationTaskTerminate(IntPtr data)
+        {
+            if (applicationTaskTerminateEventHandler != null)
+            {
+                NUIApplicationTerminatingEventArgs e = new NUIApplicationTerminatingEventArgs();
+                e.Application = this;
+                applicationTaskTerminateEventHandler.Invoke(this, e);
+            }
+        }
+
+        /// <summary>
+        /// @brief Event for TaskLanguageChanged signal which can be used to subscribe/unsubscribe the event handler
+        ///  provided by the user. TaskLanguageChanged signal is emitted when the region of the device is changed.
+        /// </summary>
+        public event DaliEventHandler<object, NUIApplicationLanguageChangedEventArgs> TaskLanguageChanged
+        {
+            add
+            {
+                // Restricted to only one listener
+                if (applicationTaskLanguageChangedEventHandler == null)
+                {
+                    applicationTaskLanguageChangedEventHandler += value;
+
+                    applicationTaskLanguageChangedEventCallbackDelegate = new NUIApplicationLanguageChangedEventCallbackDelegate(OnNUIApplicationTaskLanguageChanged);
+                    taskLanguageChangedSignal = this.TaskLanguageChangedSignal();
+                    taskLanguageChangedSignal?.Connect(applicationTaskLanguageChangedEventCallbackDelegate);
+                }
+            }
+
+            remove
+            {
+                if (applicationTaskLanguageChangedEventHandler != null)
+                {
+                    taskLanguageChangedSignal?.Disconnect(applicationTaskLanguageChangedEventCallbackDelegate);
+                    taskLanguageChangedSignal?.Dispose();
+                    taskLanguageChangedSignal = null;
+                }
+
+                applicationTaskLanguageChangedEventHandler -= value;
+            }
+        }
+
+        private void OnNUIApplicationTaskLanguageChanged(IntPtr data)
+        {
+            if (applicationTaskLanguageChangedEventHandler != null)
+            {
+                NUIApplicationLanguageChangedEventArgs e = new NUIApplicationLanguageChangedEventArgs();
+                e.Application = this;
+                applicationTaskLanguageChangedEventHandler.Invoke(this, e);
+            }
+        }
+
+        /// <summary>
+        /// @brief Event for TaskRegionChanged signal which can be used to subscribe/unsubscribe the event handler
+        ///  provided by the user. TaskRegionChanged signal is emitted when the region of the device is changed.
+        /// </summary>
+        public event DaliEventHandler<object, NUIApplicationRegionChangedEventArgs> TaskRegionChanged
+        {
+            add
+            {
+                // Restricted to only one listener
+                if (applicationTaskRegionChangedEventHandler == null)
+                {
+                    applicationTaskRegionChangedEventHandler += value;
+
+                    applicationTaskRegionChangedEventCallbackDelegate = new NUIApplicationRegionChangedEventCallbackDelegate(OnNUIApplicationTaskRegionChanged);
+                    taskRegionChangedSignal = this.TaskRegionChangedSignal();
+                    taskRegionChangedSignal?.Connect(applicationTaskRegionChangedEventCallbackDelegate);
+                }
+            }
+
+            remove
+            {
+                if (applicationTaskRegionChangedEventHandler != null)
+                {
+                    taskRegionChangedSignal?.Disconnect(applicationTaskRegionChangedEventCallbackDelegate);
+                    taskRegionChangedSignal?.Dispose();
+                    taskRegionChangedSignal = null;
+                }
+
+                applicationTaskRegionChangedEventHandler -= value;
+            }
+        }
+
+        private void OnNUIApplicationTaskRegionChanged(IntPtr data)
+        {
+            if (applicationTaskRegionChangedEventHandler != null)
+            {
+                NUIApplicationRegionChangedEventArgs e = new NUIApplicationRegionChangedEventArgs();
+                e.Application = this;
+                applicationTaskRegionChangedEventHandler.Invoke(this, e);
+            }
+        }
+
+        /// <summary>
+        /// @brief Event for TaskBatteryLow signal which can be used to subscribe/unsubscribe the event handler
+        /// provided by the user. TaskBatteryLow signal is emitted when the battery level of the device is low.
+        /// </summary>
+        public event DaliEventHandler<object, NUIApplicationBatteryLowEventArgs> TaskBatteryLow
+        {
+            add
+            {
+                // Restricted to only one listener
+                if (applicationTaskBatteryLowEventHandler == null)
+                {
+                    applicationTaskBatteryLowEventHandler += value;
+
+                    applicationTaskBatteryLowEventCallbackDelegate = new NUIApplicationBatteryLowEventCallbackDelegate(OnNUIApplicationTaskBatteryLow);
+                    taskBatteryLowSignal = this.TaskBatteryLowSignal();
+                    taskBatteryLowSignal?.Connect(applicationTaskBatteryLowEventCallbackDelegate);
+                }
+            }
+
+            remove
+            {
+                if (applicationTaskBatteryLowEventHandler != null)
+                {
+                    taskBatteryLowSignal?.Disconnect(applicationTaskBatteryLowEventCallbackDelegate);
+                    taskBatteryLowSignal?.Dispose();
+                    taskBatteryLowSignal = null;
+                }
+
+                applicationTaskBatteryLowEventHandler -= value;
+            }
+        }
+
+        private void OnNUIApplicationTaskBatteryLow(BatteryStatus status)
+        {
+            NUIApplicationBatteryLowEventArgs e = new NUIApplicationBatteryLowEventArgs();
+
+            // Populate all members of "e" (NUIApplicationBatteryLowEventArgs) with real data
+            e.BatteryStatus = status;
+            applicationTaskBatteryLowEventHandler?.Invoke(this, e);
+        }
+
+        /// <summary>
+        /// @brief Event for TaskMemoryLow signal which can be used to subscribe/unsubscribe the event handler
+        /// provided by the user. TaskMemoryLow signal is emitted when the memory level of the device is low.
+        /// </summary>
+        public event DaliEventHandler<object, NUIApplicationMemoryLowEventArgs> TaskMemoryLow
+        {
+            add
+            {
+                // Restricted to only one listener
+                if (applicationTaskMemoryLowEventHandler == null)
+                {
+                    applicationTaskMemoryLowEventHandler += value;
+
+                    applicationTaskMemoryLowEventCallbackDelegate = new NUIApplicationMemoryLowEventCallbackDelegate(OnNUIApplicationTaskMemoryLow);
+                    taskMemoryLowSignal = this.TaskMemoryLowSignal();
+                    taskMemoryLowSignal?.Connect(applicationTaskMemoryLowEventCallbackDelegate);
+                }
+            }
+
+            remove
+            {
+                if (applicationTaskMemoryLowEventHandler != null)
+                {
+                    taskMemoryLowSignal?.Disconnect(applicationTaskMemoryLowEventCallbackDelegate);
+                    taskMemoryLowSignal?.Dispose();
+                    taskMemoryLowSignal = null;
+                }
+
+                applicationTaskMemoryLowEventHandler -= value;
+            }
+        }
+
+        private void OnNUIApplicationTaskMemoryLow(MemoryStatus status)
+        {
+            NUIApplicationMemoryLowEventArgs e = new NUIApplicationMemoryLowEventArgs();
+
+            // Populate all members of "e" (NUIApplicationMemoryLowEventArgs) with real data
+            e.MemoryStatus = status;
+            applicationTaskMemoryLowEventHandler?.Invoke(this, e);
+        }
+
+        /// <summary>
+        /// @brief Event for TaskAppControl signal which can be used to subscribe/unsubscribe the event handler
+        /// provided by the user. TaskAppControl signal is emitted when another application sends a launch request to the application.
+        /// </summary>
+        public event DaliEventHandler<object, NUIApplicationAppControlEventArgs> TaskAppControl
+        {
+            add
+            {
+                // Restricted to only one listener
+                if (applicationTaskAppControlEventHandler == null)
+                {
+                    applicationTaskAppControlEventHandler += value;
+
+                    applicationTaskAppControlEventCallbackDelegate = new NUIApplicationAppControlEventCallbackDelegate(OnNUIApplicationTaskAppControl);
+                    taskAppControlSignal = this.TaskAppControlSignal();
+                    taskAppControlSignal?.Connect(applicationTaskAppControlEventCallbackDelegate);
+                }
+            }
+
+            remove
+            {
+                if (applicationTaskAppControlEventHandler != null)
+                {
+                    taskAppControlSignal?.Disconnect(applicationTaskAppControlEventCallbackDelegate);
+                    taskAppControlSignal?.Dispose();
+                    taskAppControlSignal = null;
+                }
+
+                applicationTaskAppControlEventHandler -= value;
+            }
+        }
+
+        private void OnNUIApplicationTaskAppControl(IntPtr application, IntPtr voidp)
+        {
+            if (applicationTaskAppControlEventHandler != null)
+            {
+                NUIApplicationAppControlEventArgs e = new NUIApplicationAppControlEventArgs();
+                e.VoidP = voidp;
+                e.Application = this;
+                applicationTaskAppControlEventHandler.Invoke(this, e);
+            }
+        }
+
         protected static Application instance; // singleton
 
         public static Application Instance
@@ -1100,6 +1472,19 @@ namespace Tizen.NUI
             return instance;
         }
 
+        public static Application NewApplication(string[] args, string stylesheet, NUIApplication.WindowMode windowMode, Rectangle positionSize, bool useUIThread)
+        {
+            if (instance != null)
+            {
+                return instance;
+            }
+            Application ret = New(args, stylesheet, windowMode, positionSize, useUIThread);
+            if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
+
+            instance = ret;
+            return instance;
+        }
+
         /// <summary>
         /// Ensures that the function passed in is called from the main loop when it is idle.
         /// </summary>
@@ -1218,6 +1603,30 @@ namespace Tizen.NUI
             return ret;
         }
 
+        public static Application New(string[] args, string stylesheet, NUIApplication.WindowMode windowMode, Rectangle positionSize, bool useUIThread)
+        {
+            Application ret = null;
+            int argc = 0;
+            string argvStr = "";
+            try
+            {
+                argc = args.Length;
+                argvStr = string.Join(" ", args);
+
+                ret = new Application(Interop.Application.New(argc, stylesheet, (int)windowMode, Rectangle.getCPtr(positionSize), useUIThread), true);
+                if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
+            }
+            catch (Exception exception)
+            {
+                Tizen.Log.Fatal("NUI", "[Error] got exception during Application New(), this should not occur, message : " + exception.Message);
+                Tizen.Log.Fatal("NUI", "[Error] error line number : " + new StackTrace(exception, true).GetFrame(0).GetFileLineNumber());
+                Tizen.Log.Fatal("NUI", "[Error] Stack Trace : " + exception.StackTrace);
+                throw;
+            }
+
+            return ret;
+        }
+
         public Application() : this(Interop.Application.NewApplication(), true)
         {
             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
@@ -1393,5 +1802,55 @@ namespace Tizen.NUI
             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
             return ret;
         }
+
+        //Task
+        internal ApplicationSignal TaskInitSignal()
+        {
+            ApplicationSignal ret = new ApplicationSignal(NDalicPINVOKE.ApplicationTaskInitSignal(SwigCPtr), false);
+            if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
+            return ret;
+        }
+
+        internal ApplicationSignal TaskTerminateSignal()
+        {
+            ApplicationSignal ret = new ApplicationSignal(NDalicPINVOKE.ApplicationTaskTerminateSignal(SwigCPtr), false);
+            if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
+            return ret;
+        }
+
+        internal ApplicationControlSignal TaskAppControlSignal()
+        {
+            ApplicationControlSignal ret = new ApplicationControlSignal(NDalicPINVOKE.ApplicationTaskAppControlSignal(SwigCPtr), false);
+            if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
+            return ret;
+        }
+
+        internal ApplicationSignal TaskLanguageChangedSignal()
+        {
+            ApplicationSignal ret = new ApplicationSignal(NDalicPINVOKE.ApplicationTaskLanguageChangedSignal(SwigCPtr), false);
+            if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
+            return ret;
+        }
+
+        internal ApplicationSignal TaskRegionChangedSignal()
+        {
+            ApplicationSignal ret = new ApplicationSignal(NDalicPINVOKE.ApplicationTaskRegionChangedSignal(SwigCPtr), false);
+            if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
+            return ret;
+        }
+
+        internal LowBatterySignalType TaskBatteryLowSignal()
+        {
+            LowBatterySignalType ret = new LowBatterySignalType(NDalicPINVOKE.ApplicationTaskLowBatterySignal(SwigCPtr), false);
+            if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
+            return ret;
+        }
+
+        internal LowMemorySignalType TaskMemoryLowSignal()
+        {
+            LowMemorySignalType ret = new LowMemorySignalType(NDalicPINVOKE.ApplicationTaskLowMemorySignal(SwigCPtr), false);
+            if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
+            return ret;
+        }
     }
 }
index 4dc9b17..3fa6ce7 100755 (executable)
@@ -23,7 +23,7 @@ using Tizen.Applications;
 
 namespace Tizen.NUI
 {
-    class NUICoreBackend : ICoreBackend
+    class NUICoreBackend : ICoreTaskBackend
     {
         /// <summary>
         /// The Application instance to connect event.
@@ -33,11 +33,13 @@ namespace Tizen.NUI
         private NUIApplication.WindowMode windowMode = NUIApplication.WindowMode.Opaque;
         private Rectangle windowRectangle = null;
         private WindowType defaultWindowType = WindowType.Normal;
+        private ICoreTask coreTask;
 
         /// <summary>
         /// The Dictionary to contain each type of event callback.
         /// </summary>
         protected IDictionary<EventType, object> Handlers = new Dictionary<EventType, object>();
+        protected IDictionary<EventType, object> TaskHandlers = new Dictionary<EventType, object>();
 
         /// <summary>
         /// The default Constructor.
@@ -177,11 +179,29 @@ namespace Tizen.NUI
             {
                 if (windowRectangle != null)
                 {
-                    application = Application.NewApplication(args, stylesheet, windowMode, windowRectangle);
+                    if (coreTask != null)
+                    {
+                        application = Application.NewApplication(args, stylesheet, windowMode, windowRectangle, true);
+                    }
+                    else
+                    {
+                        application = Application.NewApplication(args, stylesheet, windowMode, windowRectangle);
+                    }
                 }
                 else
                 {
-                    application = Application.NewApplication(args, stylesheet, windowMode);
+                    if (coreTask != null)
+                    {
+                        // The Rectangle(0, 0, 0, 0) means that want to use the full screen size window at 0,0.
+                        using (Rectangle rec = new Rectangle(0, 0, 0, 0))
+                        {
+                            application = Application.NewApplication(args, stylesheet, windowMode, rec, true);
+                        }
+                    }
+                    else
+                    {
+                        application = Application.NewApplication(args, stylesheet, windowMode);
+                    }
                 }
             }
             Tizen.Tracer.End();
@@ -201,11 +221,34 @@ namespace Tizen.NUI
 
             Tizen.Tracer.End();
 
+            if (coreTask != null)
+            {
+                application.TaskBatteryLow += OnTaskBatteryLow;
+                application.TaskLanguageChanged += OnTaskLanguageChanged;
+                application.TaskMemoryLow += OnTaskMemoryLow;
+                application.TaskRegionChanged += OnTaskRegionChanged;
+
+                application.TaskInitialized += OnTaskInitialized;
+                application.TaskTerminating += OnTaskTerminated;
+                application.TaskAppControl += OnTaskAppControl;
+                // Note: UIEvent, DeviceOrientationChanged are not implemented.
+            }
+
             application.MainLoop();
             application.Dispose();
         }
 
         /// <summary>
+        /// Sets the core task.
+        /// </summary>
+        /// <param name="task">The core task interface.</param>
+        /// <since_tizen> 10 </since_tizen>
+        public void SetCoreTask(ICoreTask task)
+        {
+            coreTask = task;
+        }
+
+        /// <summary>
         /// The Region changed event callback function.
         /// </summary>
         /// <param name="source">The application instance.</param>
@@ -354,7 +397,7 @@ namespace Tizen.NUI
         {
             Log.Info("NUI", "NUICorebackend OnAppControl Called");
             var handler = Handlers[EventType.AppControlReceived] as Action<AppControlReceivedEventArgs>;
-            SafeAppControlHandle handle = new SafeAppControlHandle(e.VoidP, false);
+            using SafeAppControlHandle handle = new SafeAppControlHandle(e.VoidP, false);
             handler?.Invoke(new AppControlReceivedEventArgs(new ReceivedAppControl(handle)));
         }
 
@@ -370,6 +413,118 @@ namespace Tizen.NUI
             handler?.Invoke();
         }
 
+        /// <summary>
+        /// The Region changed event callback function. The callback is emitted on the main thread.
+        /// </summary>
+        /// <param name="source">The application instance.</param>
+        /// <param name="e">The event argument for RegionChanged.</param>
+        private void OnTaskRegionChanged(object source, NUIApplicationRegionChangedEventArgs e)
+        {
+            Log.Info("NUI", "NUICorebackend OnTaskRegionChanged Called");
+            coreTask.OnRegionFormatChanged(new RegionFormatChangedEventArgs((source as Application)?.GetRegion()));
+        }
+
+        /// <summary>
+        /// The Memory Low event callback function. The callback is emitted on the main thread.
+        /// </summary>
+        /// <param name="source">The application instance.</param>
+        /// <param name="e">The event argument for MemoryLow.</param>
+        private void OnTaskMemoryLow(object source, NUIApplicationMemoryLowEventArgs e)
+        {
+            Log.Info("NUI", "NUICorebackend OnTaskMemoryLow Called");
+            switch (e.MemoryStatus)
+            {
+                case Application.MemoryStatus.Normal:
+                    {
+                        coreTask.OnLowMemory(new LowMemoryEventArgs(LowMemoryStatus.None));
+                        break;
+                    }
+                case Application.MemoryStatus.Low:
+                    {
+                        coreTask.OnLowMemory(new LowMemoryEventArgs(LowMemoryStatus.SoftWarning));
+                        break;
+                    }
+                case Application.MemoryStatus.CriticallyLow:
+                    {
+                        coreTask.OnLowMemory(new LowMemoryEventArgs(LowMemoryStatus.HardWarning));
+                        break;
+                    }
+            }
+        }
+
+        /// <summary>
+        /// The Language changed event callback function. The callback is emitted on the main thread.
+        /// </summary>
+        /// <param name="source">The application instance.</param>
+        /// <param name="e">The event argument for LanguageChanged.</param>
+        private void OnTaskLanguageChanged(object source, NUIApplicationLanguageChangedEventArgs e)
+        {
+            Log.Info("NUI", "NUICorebackend OnTaskLanguageChanged Called");
+            coreTask.OnLocaleChanged(new LocaleChangedEventArgs((source as Application)?.GetLanguage()));
+        }
+
+        /// <summary>
+        /// The Battery Low event callback function. The callback is emitted on the main thread.
+        /// </summary>
+        /// <param name="source">The application instance.</param>
+        /// <param name="e">The event argument for BatteryLow.</param>
+        private void OnTaskBatteryLow(object source, NUIApplicationBatteryLowEventArgs e)
+        {
+            Log.Info("NUI", "NUICorebackend OnTaskBatteryLow Called");
+            switch (e.BatteryStatus)
+            {
+                case Application.BatteryStatus.Normal:
+                    {
+                        coreTask?.OnLowBattery(new LowBatteryEventArgs(LowBatteryStatus.None));
+                        break;
+                    }
+                case Application.BatteryStatus.CriticallyLow:
+                    {
+                        coreTask?.OnLowBattery(new LowBatteryEventArgs(LowBatteryStatus.CriticalLow));
+                        break;
+                    }
+                case Application.BatteryStatus.PowerOff:
+                    {
+                        coreTask?.OnLowBattery(new LowBatteryEventArgs(LowBatteryStatus.PowerOff));
+                        break;
+                    }
+            }
+        }
+
+        /// <summary>
+        /// The Initialized event callback function. The callback is emitted on the main thread.
+        /// </summary>
+        /// <param name="source">The application instance.</param>
+        /// <param name="e">The event argument for Initialized.</param>
+        private void OnTaskInitialized(object source, NUIApplicationInitEventArgs e)
+        {
+            Log.Info("NUI", "NUICorebackend OnTaskInitialized Called");
+            coreTask.OnCreate();
+        }
+
+        /// <summary>
+        /// The Terminated event callback function. The callback is emitted on the main thread.
+        /// </summary>
+        /// <param name="source">The application instance.</param>
+        /// <param name="e">The event argument for Terminated.</param>
+        private void OnTaskTerminated(object source, NUIApplicationTerminatingEventArgs e)
+        {
+            Log.Info("NUI", "NUICorebackend OnTaskTerminated Called");
+            coreTask.OnTerminate();
+        }
+
+        /// <summary>
+        /// The App control event callback function. The callback is emitted on the main thread.
+        /// </summary>
+        /// <param name="source">The application instance.</param>
+        /// <param name="e">The event argument for AppControl.</param>
+        private void OnTaskAppControl(object source, NUIApplicationAppControlEventArgs e)
+        {
+            Log.Info("NUI", "NUICorebackend OnTaskAppControl Called");
+            using SafeAppControlHandle handle = new SafeAppControlHandle(e.VoidP, false);
+            coreTask.OnAppControlReceived(new AppControlReceivedEventArgs(new ReceivedAppControl(handle)));
+        }
+
         internal Application ApplicationHandle
         {
             get
index a4ab450..a258523 100755 (executable)
@@ -91,6 +91,9 @@ namespace Tizen.NUI
 
             [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_Application_New__SWIG_5")]
             public static extern global::System.IntPtr New(int jarg1, string jarg3, int jarg4, global::System.Runtime.InteropServices.HandleRef jarg5, int jarg6);
+
+            [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_Application_New__SWIG_6")]
+            public static extern global::System.IntPtr New(int jarg1, string jarg3, int jarg4, global::System.Runtime.InteropServices.HandleRef jarg5, bool jarg7);
         }
     }
 }
index 62d0b49..d830f53 100755 (executable)
@@ -277,6 +277,28 @@ namespace Tizen.NUI
         [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_Application_LowMemorySignal")]
         public static extern global::System.IntPtr ApplicationLowMemorySignal(global::System.Runtime.InteropServices.HandleRef jarg1);
 
+        //Task
+        [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_Application_TaskInitSignal")]
+        public static extern global::System.IntPtr ApplicationTaskInitSignal(global::System.Runtime.InteropServices.HandleRef jarg1);
+
+        [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_Application_TaskTerminateSignal")]
+        public static extern global::System.IntPtr ApplicationTaskTerminateSignal(global::System.Runtime.InteropServices.HandleRef jarg1);
+
+        [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_Application_TaskAppControlSignal")]
+        public static extern global::System.IntPtr ApplicationTaskAppControlSignal(global::System.Runtime.InteropServices.HandleRef jarg1);
+
+        [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_Application_TaskLanguageChangedSignal")]
+        public static extern global::System.IntPtr ApplicationTaskLanguageChangedSignal(global::System.Runtime.InteropServices.HandleRef jarg1);
+
+        [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_Application_TaskRegionChangedSignal")]
+        public static extern global::System.IntPtr ApplicationTaskRegionChangedSignal(global::System.Runtime.InteropServices.HandleRef jarg1);
+
+        [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_Application_TaskLowBatterySignal")]
+        public static extern global::System.IntPtr ApplicationTaskLowBatterySignal(global::System.Runtime.InteropServices.HandleRef jarg1);
+
+        [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_Application_TaskLowMemorySignal")]
+        public static extern global::System.IntPtr ApplicationTaskLowMemorySignal(global::System.Runtime.InteropServices.HandleRef jarg1);
+
 
         [Obsolete("This has been deprecated in API9 and will be removed in API11. Use NDalicPINVOKE.DeleteBaseHandle(...) instead.")]
         [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_delete_BaseHandle")]
index 1c4b472..8f84534 100755 (executable)
@@ -178,6 +178,7 @@ namespace Tizen.NUI
         /// <param name="windowMode">The windowMode.</param>
         /// <param name="type">The default window type.</param>
         /// <since_tizen> 9 </since_tizen>
+        [SuppressMessage("Microsoft.Design", "CA2000: Dispose objects before losing scope", Justification = "NUICoreBackend is disposed in the base class when the application is terminated")]
         public NUIApplication(string styleSheet, WindowMode windowMode, WindowType type) : base(new NUICoreBackend(styleSheet, windowMode, type))
         {
             ExternalThemeManager.Initialize();
@@ -193,6 +194,7 @@ namespace Tizen.NUI
         /// <param name="windowPosition">The window position.</param>
         /// <param name="borderInterface"><see cref="Tizen.NUI.IBorderInterface"/>If borderInterface is null, defaultBorder is enabled.</param>
         [EditorBrowsable(EditorBrowsableState.Never)]
+        [SuppressMessage("Microsoft.Design", "CA2000: Dispose objects before losing scope", Justification = "NUICoreBackend is disposed in the base class when the application is terminated")]
         public NUIApplication(string styleSheet, Size2D windowSize, Position2D windowPosition, IBorderInterface borderInterface, WindowMode windowMode = WindowMode.Opaque) : base(new NUICoreBackend(styleSheet, windowMode, windowSize, windowPosition))
         {
             borderEnabled = true;
@@ -200,6 +202,42 @@ namespace Tizen.NUI
         }
 
         /// <summary>
+        /// The constructor with a stylesheet, window mode, coretask
+        /// </summary>
+        /// <note>
+        /// There is the UI thread feature.
+        /// UI thread is an additional thread that an Application object creates. The thread is for UI events.
+        /// To enable the UI Thread, you have to pass CoreTask object using this contructor.
+        /// When the UI thread feature is enabled, The methods of CoreTask are emitted on the main thread,
+        /// and the NUIApplication's events are emitted on the UI thread.
+        /// If you want to handle windows or actors in cases like when the memory level of the device is low, you have to use the NUIApplication events, not the CoreTask methods.
+        /// The CoreTask is not for handling GUI.
+        /// Callbacks of the all events in NUIApplication except the CoreTask are emitted on the UI thread.
+        /// </note>
+        /// <param name="styleSheet">The styleSheet URL.</param>
+        /// <param name="windowMode">The windowMode.</param>
+        /// <param name="task">True If app creates a UI Thread</param>
+        [SuppressMessage("Microsoft.Design", "CA2000: Dispose objects before losing scope", Justification = "NUICoreBackend is disposed in the base class when the application is terminated")]
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public NUIApplication(string styleSheet, WindowMode windowMode, CoreTask task) : base(new NUICoreBackend(styleSheet, windowMode), task)
+        {
+        }
+
+        /// <summary>
+        /// The constructor with a stylesheet, window mode, window size, position, coretask
+        /// </summary>
+        /// <param name="styleSheet">The styleSheet URL.</param>
+        /// <param name="windowMode">The windowMode.</param>
+        /// <param name="windowSize">The window size.</param>
+        /// <param name="windowPosition">The window position.</param>
+        /// <param name="task">True If app creates a UI Thread</param>
+        [SuppressMessage("Microsoft.Design", "CA2000: Dispose objects before losing scope", Justification = "NUICoreBackend is disposed in the base class when the application is terminated")]
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public NUIApplication(string styleSheet, WindowMode windowMode, Size2D windowSize, Position2D windowPosition, CoreTask task) : base(new NUICoreBackend(styleSheet, windowMode, windowSize, windowPosition), task)
+        {
+        }
+
+        /// <summary>
         /// Occurs whenever the application is resumed.
         /// </summary>
         /// <since_tizen> 4 </since_tizen>
diff --git a/test/Tizen.NUI.UIThread/Tizen.NUI.UIThread.cs b/test/Tizen.NUI.UIThread/Tizen.NUI.UIThread.cs
new file mode 100644 (file)
index 0000000..8c70f09
--- /dev/null
@@ -0,0 +1,160 @@
+
+using Tizen.Applications;
+using Tizen.NUI;
+using Tizen.NUI.Components;
+using Tizen.NUI.BaseComponents;
+
+namespace UIThreadApp
+{
+    public class AppCoreTask : CoreTask
+    {
+        public override void OnCreate()
+        {
+            Tizen.Log.Info("UIThreadApp", "CoreTask OnCreate");
+        }
+
+        public override void OnTerminate()
+        {
+            Tizen.Log.Info("UIThreadApp", "CoreTask OnTerminate");
+        }
+
+        public override void OnAppControlReceived(AppControlReceivedEventArgs e)
+        {
+            Tizen.Log.Info("UIThreadApp", "CoreTask OnAppControlReceived " + e.ReceivedAppControl.ApplicationId);
+        }
+
+        public override void OnLowMemory(LowMemoryEventArgs e)
+        {
+            Tizen.Log.Info("UIThreadApp", "CoreTask OnLowMemory " + e.LowMemoryStatus);
+        }
+
+        public override void OnLowBattery(LowBatteryEventArgs e)
+        {
+            Tizen.Log.Info("UIThreadApp", "CoreTask OnCreate " + e.LowBatteryStatus);
+        }
+
+        public override void OnLocaleChanged(LocaleChangedEventArgs e)
+        {
+            Tizen.Log.Info("UIThreadApp", "CoreTask OnLocaleChanged " + e.Locale);
+        }
+
+        public override void OnRegionFormatChanged(RegionFormatChangedEventArgs e)
+        {
+            Tizen.Log.Info("UIThreadApp", "CoreTask OnRegionFormatChanged " + e.Region);
+        }
+    }
+
+    class Program : NUIApplication
+    {
+        private View root;
+        private Control control;
+
+        public Program(string styleSheet, WindowMode windowMode, CoreTask task) : base(styleSheet, windowMode, task)
+        {
+        }
+
+        protected override void OnCreate()
+        {
+            Tizen.Log.Info("UIThreadApp", "NUIApplication OnCreate");
+            base.OnCreate();
+            Initialize();
+        }
+
+        protected override void OnLocaleChanged(LocaleChangedEventArgs e)
+        {
+            Tizen.Log.Info("UIThreadApp", "NUIApplication OnLocaleChanged " + e.Locale);
+        }
+
+        protected override void OnLowBattery(LowBatteryEventArgs e)
+        {
+            Tizen.Log.Info("UIThreadApp", "NUIApplication OnLowBattery " + e.LowBatteryStatus);
+        }
+
+        protected override void OnLowMemory(LowMemoryEventArgs e)
+        {
+            Tizen.Log.Info("UIThreadApp", "NUIApplication OnLowMemory " + e.LowMemoryStatus);
+        }
+
+        protected override void OnRegionFormatChanged(RegionFormatChangedEventArgs e)
+        {
+            Tizen.Log.Info("UIThreadApp", "NUIApplication OnRegionFormatChanged " + e.Region);
+        }
+
+        protected override void OnTerminate()
+        {
+            Tizen.Log.Info("UIThreadApp", "NUIApplication OnTerminate");
+        }
+
+        protected override void OnPause()
+        {
+            Tizen.Log.Info("UIThreadApp", "NUIApplication OnPause");
+        }
+
+        protected override void OnResume()
+        {
+            Tizen.Log.Info("UIThreadApp", "NUIApplication OnResume");
+        }
+
+        protected override void OnPreCreate()
+        {
+            Tizen.Log.Info("UIThreadApp", "NUIApplication OnPreCreate");
+        }
+
+        protected override void OnAppControlReceived(AppControlReceivedEventArgs e)
+        {
+            Tizen.Log.Info("UIThreadApp", "NUIApplication OnAppControlReceived " + e.ReceivedAppControl.ApplicationId);
+        }
+
+        void Initialize()
+        {
+
+            Window window = NUIApplication.GetDefaultWindow();
+
+            root = new View()
+            {
+                Size = window.Size,
+                BackgroundColor = new Color(0.8f, 0.8f, 0.8f, 0.6f),
+                ParentOrigin = ParentOrigin.Center,
+                PivotPoint = PivotPoint.Center,
+                PositionUsesPivotPoint = true,
+            };
+            window.Add(root);
+
+            control = new Control()
+            {
+                Size = new Size(100, 100),
+                BackgroundColor = Color.Blue,
+                ParentOrigin = ParentOrigin.Center,
+                PivotPoint = PivotPoint.Center,
+                PositionUsesPivotPoint = true,
+                BoxShadow = new Shadow(0, new Color(0.2f, 0.2f, 0.2f, 0.3f), new Vector2(5, 5)),
+                CornerRadius = 0.5f,
+                CornerRadiusPolicy = VisualTransformPolicyType.Relative,
+            };
+
+            root.Add(control);
+
+            var animation = new Animation(2000);
+            animation.AnimateTo(control, "SizeWidth", 200, 0, 1000);
+            animation.AnimateTo(control, "SizeWidth", 100, 1000, 2000);
+            animation.Looping = true;
+            animation.Play();
+
+            NUIApplication.GetDefaultWindow().KeyEvent += OnKeyEvent;
+        }
+
+        public void OnKeyEvent(object sender, Window.KeyEventArgs e)
+        {
+            if (e.Key.State == Key.StateType.Down && (e.Key.KeyPressedName == "XF86Back" || e.Key.KeyPressedName == "Escape"))
+            {
+                Exit();
+            }
+        }
+
+        static void Main(string[] args)
+        {
+            var app = new Program("", NUIApplication.WindowMode.Opaque, new AppCoreTask());
+            app.Run(args);
+        }
+    }
+}
diff --git a/test/Tizen.NUI.UIThread/Tizen.NUI.UIThread.csproj b/test/Tizen.NUI.UIThread/Tizen.NUI.UIThread.csproj
new file mode 100644 (file)
index 0000000..1badb70
--- /dev/null
@@ -0,0 +1,26 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+    <PropertyGroup>
+        <OutputType>Exe</OutputType>
+        <TargetFramework>netcoreapp3.1</TargetFramework>
+    </PropertyGroup>
+
+    <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+        <DebugType>portable</DebugType>
+    </PropertyGroup>
+    <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+        <DebugType>None</DebugType>
+    </PropertyGroup>
+
+    <ItemGroup>
+        <PackageReference Include="Tizen.NET.Sdk" Version="1.0.9" />
+        <ProjectReference Include="../../src/Tizen/Tizen.csproj" />
+        <ProjectReference Include="../../src/Tizen.NUI.Components/Tizen.NUI.Components.csproj" />
+        <ProjectReference Include="../../src/Tizen.NUI/Tizen.NUI.csproj" />
+    </ItemGroup>
+
+    <PropertyGroup>
+        <NeedInjection>True</NeedInjection>
+    </PropertyGroup>
+
+</Project>
diff --git a/test/Tizen.NUI.UIThread/shared/res/Tizen.NUI.UIThread.png b/test/Tizen.NUI.UIThread/shared/res/Tizen.NUI.UIThread.png
new file mode 100644 (file)
index 0000000..9f3cb98
Binary files /dev/null and b/test/Tizen.NUI.UIThread/shared/res/Tizen.NUI.UIThread.png differ
diff --git a/test/Tizen.NUI.UIThread/tizen-manifest.xml b/test/Tizen.NUI.UIThread/tizen-manifest.xml
new file mode 100644 (file)
index 0000000..ba61a12
--- /dev/null
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns="http://tizen.org/ns/packages" api-version="6" package="org.tizen.example.Tizen.NUI.UIThread" version="1.0.0">
+  <profile name="common" />
+  <ui-application appid="org.tizen.example.Tizen.NUI.UIThread"
+                                       exec="Tizen.NUI.UIThread.dll"
+                                       type="dotnet"
+                                       multiple="false"
+                                       taskmanage="true"
+                                       nodisplay="false"
+                                       launch_mode="single"
+          >
+    <label>Tizen.NUI.UIThread</label>
+    <icon>Tizen.NUI.UIThread.png</icon>
+    <metadata key="http://tizen.org/metadata/prefer_dotnet_aot" value="true" />
+    <metadata key="http://tizen.org/metadata/direct-launch" value="yes"/>
+  </ui-application>
+</manifest>