[NUI] Rollback split-nui (#887)
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI / src / internal / Application.cs
index ad2f8ac..d518199 100755 (executable)
@@ -25,6 +25,8 @@ namespace Tizen.NUI
     using System.Runtime.InteropServices;
     using System.Threading;
     using System.Threading.Tasks;
+    using Tizen.NUI.Binding;
+    using Tizen.NUI.Binding.Internals;
 
     /**
       * @brief Event arguments that passed via NUIApplicationInit signal
@@ -307,18 +309,445 @@ namespace Tizen.NUI
         }
     }
 
-    internal class Application : BaseHandle
+    /// <summary>
+    /// A class to get resources in current application.
+    /// </summary>
+    public class GetResourcesProvider
     {
+        /// <summary>
+        /// Get resources in current application.
+        /// </summary>
+        static public IResourcesProvider Get()
+        {
+            return Tizen.NUI.Application.Current;
+        }
+    }
+
+    internal class Application : BaseHandle, IResourcesProvider, IApplicationController, IElementConfiguration<Application>
+    {
+
+        static Application s_current;
+        Task<IDictionary<string, object>> _propertiesTask;
+        readonly Lazy<PlatformConfigurationRegistry<Application>> _platformConfigurationRegistry;
+
+        IAppIndexingProvider _appIndexProvider;
+
+        ReadOnlyCollection<Element> _logicalChildren;
+
+        Page _mainPage;
+
         static SemaphoreSlim SaveSemaphore = new SemaphoreSlim(1, 1);
 
+        public IAppLinks AppLinks
+        {
+            get
+            {
+                if (_appIndexProvider == null)
+                    throw new ArgumentException("No IAppIndexingProvider was provided");
+                if (_appIndexProvider.AppLinks == null)
+                    throw new ArgumentException("No AppLinks implementation was found, if in Android make sure you installed the Xamarin.Forms.AppLinks");
+                return _appIndexProvider.AppLinks;
+            }
+        }
+
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public static void SetCurrentApplication(Application value) => Current = value;
+
+        public static Application Current
+        {
+            get { return s_current; }
+            set
+            {
+                if (s_current == value)
+                    return;
+                if (value == null)
+                    s_current = null; //Allow to reset current for unittesting
+                s_current = value;
+            }
+        }
+
+        public Page MainPage
+        {
+            get { return _mainPage; }
+            set
+            {
+                if (value == null)
+                    throw new ArgumentNullException("value");
+
+                if (_mainPage == value)
+                    return;
+
+                OnPropertyChanging();
+                if (_mainPage != null)
+                {
+                    InternalChildren.Remove(_mainPage);
+                    _mainPage.Parent = null;
+                }
+
+                _mainPage = value;
+
+                if (_mainPage != null)
+                {
+                    _mainPage.Parent = this;
+                    _mainPage.NavigationProxy.Inner = NavigationProxy;
+                    InternalChildren.Add(_mainPage);
+                }
+                OnPropertyChanged();
+            }
+        }
+
+        public IDictionary<string, object> Properties
+        {
+            get
+            {
+                if (_propertiesTask == null)
+                {
+                    _propertiesTask = GetPropertiesAsync();
+                }
+
+                return _propertiesTask.Result;
+            }
+        }
+
+        internal override ReadOnlyCollection<Element> LogicalChildrenInternal
+        {
+            get { return _logicalChildren ?? (_logicalChildren = new ReadOnlyCollection<Element>(InternalChildren)); }
+        }
+
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public new NavigationProxy NavigationProxy { get; }
+
         [EditorBrowsable(EditorBrowsableState.Never)]
         public int PanGestureId { get; set; }
 
+        internal IResourceDictionary SystemResources { get; }
+
+        ObservableCollection<Element> InternalChildren { get; } = new ObservableCollection<Element>();
+
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public void SetAppIndexingProvider(IAppIndexingProvider provider)
+        {
+            _appIndexProvider = provider;
+        }
+
+        ResourceDictionary _resources;
+        public bool IsResourcesCreated => _resources != null;
+
+        public delegate void resChangeCb(object sender, ResourcesChangedEventArgs e);
+
+        static private Dictionary<object, Dictionary<resChangeCb, int>> resourceChangeCallbackDict = new Dictionary<object, Dictionary<resChangeCb, int>>();
+        static public void AddResourceChangedCallback(object handle, resChangeCb cb)
+        {
+            Dictionary<resChangeCb, int> cbDict;
+            resourceChangeCallbackDict.TryGetValue(handle, out cbDict);
+
+            if (null == cbDict)
+            {
+                cbDict = new Dictionary<resChangeCb, int>();
+                resourceChangeCallbackDict.Add(handle, cbDict);
+            }
+
+            if (false == cbDict.ContainsKey(cb))
+            {
+                cbDict.Add(cb, 0);
+            }
+        }
+
+        internal override void OnResourcesChanged(object sender, ResourcesChangedEventArgs e)
+        {
+            base.OnResourcesChanged(sender, e);
+
+            foreach (KeyValuePair<object, Dictionary<resChangeCb, int>> resourcePair in resourceChangeCallbackDict)
+            {
+                foreach (KeyValuePair<resChangeCb, int> cbPair in resourcePair.Value)
+                {
+                    cbPair.Key(sender, e);
+                }
+            }
+        }
+
+        public ResourceDictionary XamlResources
+        {
+            get
+            {
+                if (_resources != null)
+                    return _resources;
+
+                _resources = new ResourceDictionary();
+                int hashCode = _resources.GetHashCode();
+                ((IResourceDictionary)_resources).ValuesChanged += OnResourcesChanged;
+                return _resources;
+            }
+            set
+            {
+                if (_resources == value)
+                    return;
+                OnPropertyChanging();
+
+                if (_resources != null)
+                    ((IResourceDictionary)_resources).ValuesChanged -= OnResourcesChanged;
+                _resources = value;
+                OnResourcesChanged(value);
+                if (_resources != null)
+                    ((IResourceDictionary)_resources).ValuesChanged += OnResourcesChanged;
+
+                OnPropertyChanged();
+            }
+        }
+
+        public event EventHandler<ModalPoppedEventArgs> ModalPopped;
+
+        public event EventHandler<ModalPoppingEventArgs> ModalPopping;
+
+        public event EventHandler<ModalPushedEventArgs> ModalPushed;
+
+        public event EventHandler<ModalPushingEventArgs> ModalPushing;
+
+        public event EventHandler<Page> PageAppearing;
+
+        public event EventHandler<Page> PageDisappearing;
+
+
+        async void SaveProperties()
+        {
+            try
+            {
+                await SetPropertiesAsync();
+            }
+            catch (Exception exc)
+            {
+                Console.WriteLine(nameof(Application), $"Exception while saving Application Properties: {exc}");
+            }
+        }
+
+        public async Task SavePropertiesAsync()
+        {
+            if (Device.IsInvokeRequired)
+            {
+                Device.BeginInvokeOnMainThread(SaveProperties);
+            }
+            else
+            {
+                await SetPropertiesAsync();
+            }
+        }
+
+        // Don't use this unless there really is no better option
+        internal void SavePropertiesAsFireAndForget()
+        {
+            if (Device.IsInvokeRequired)
+            {
+                Device.BeginInvokeOnMainThread(SaveProperties);
+            }
+            else
+            {
+                SaveProperties();
+            }
+        }
+
+        public IPlatformElementConfiguration<T, Application> On<T>() where T : IConfigPlatform
+        {
+            return _platformConfigurationRegistry.Value.On<T>();
+        }
+
+        protected virtual void OnAppLinkRequestReceived(Uri uri)
+        {
+        }
+
+        protected override void OnParentSet()
+        {
+            throw new InvalidOperationException("Setting a Parent on Application is invalid.");
+        }
+
+        protected virtual void OnResume()
+        {
+        }
+
+        protected virtual void OnSleep()
+        {
+        }
+
+        protected virtual void OnStart()
+        {
+        }
+
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public static void ClearCurrent()
+        {
+            s_current = null;
+        }
+
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public static bool IsApplicationOrNull(Element element)
+        {
+            return element == null || element is Application;
+        }
+
+        internal override void OnParentResourcesChanged(IEnumerable<KeyValuePair<string, object>> values)
+        {
+            if (!((IResourcesProvider)this).IsResourcesCreated || XamlResources.Count == 0)
+            {
+                base.OnParentResourcesChanged(values);
+                return;
+            }
+
+            var innerKeys = new HashSet<string>();
+            var changedResources = new List<KeyValuePair<string, object>>();
+            foreach (KeyValuePair<string, object> c in XamlResources)
+                innerKeys.Add(c.Key);
+            foreach (KeyValuePair<string, object> value in values)
+            {
+                if (innerKeys.Add(value.Key))
+                    changedResources.Add(value);
+            }
+            OnResourcesChanged(changedResources);
+        }
+
+        internal event EventHandler PopCanceled;
+
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public void SendOnAppLinkRequestReceived(Uri uri)
+        {
+            OnAppLinkRequestReceived(uri);
+        }
+
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public void SendResume()
+        {
+            s_current = this;
+            OnResume();
+        }
+
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public void SendSleep()
+        {
+            OnSleep();
+            SavePropertiesAsFireAndForget();
+        }
+
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public Task SendSleepAsync()
+        {
+            OnSleep();
+            return SavePropertiesAsync();
+        }
+
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public void SendStart()
+        {
+            OnStart();
+        }
+
+        async Task<IDictionary<string, object>> GetPropertiesAsync()
+        {
+            var deserializer = DependencyService.Get<IDeserializer>();
+            if (deserializer == null)
+            {
+                Console.WriteLine("Startup", "No IDeserialzier was found registered");
+                return new Dictionary<string, object>(4);
+            }
+
+            IDictionary<string, object> properties = await deserializer.DeserializePropertiesAsync().ConfigureAwait(false);
+            if (properties == null)
+                properties = new Dictionary<string, object>(4);
+
+            return properties;
+        }
+
+        internal void OnPageAppearing(Page page)
+            => PageAppearing?.Invoke(this, page);
+
+        internal void OnPageDisappearing(Page page)
+            => PageDisappearing?.Invoke(this, page);
+
+        void OnModalPopped(Page modalPage)
+            => ModalPopped?.Invoke(this, new ModalPoppedEventArgs(modalPage));
+
+        bool OnModalPopping(Page modalPage)
+        {
+            var args = new ModalPoppingEventArgs(modalPage);
+            ModalPopping?.Invoke(this, args);
+            return args.Cancel;
+        }
+
+        void OnModalPushed(Page modalPage)
+            => ModalPushed?.Invoke(this, new ModalPushedEventArgs(modalPage));
+
+        void OnModalPushing(Page modalPage)
+            => ModalPushing?.Invoke(this, new ModalPushingEventArgs(modalPage));
+
+        void OnPopCanceled()
+            => PopCanceled?.Invoke(this, EventArgs.Empty);
+
+        async Task SetPropertiesAsync()
+        {
+            await SaveSemaphore.WaitAsync();
+            try
+            {
+                await DependencyService.Get<IDeserializer>()?.SerializePropertiesAsync(Properties);
+            }
+            finally
+            {
+                SaveSemaphore.Release();
+            }
+
+        }
+
+        class NavigationImpl : NavigationProxy
+        {
+            readonly Application _owner;
+
+            public NavigationImpl(Application owner)
+            {
+                _owner = owner;
+            }
+
+            protected override async Task<Page> OnPopModal(bool animated)
+            {
+                Page modal = ModalStack[ModalStack.Count - 1];
+                if (_owner.OnModalPopping(modal))
+                {
+                    _owner.OnPopCanceled();
+                    return null;
+                }
+                Page result = await base.OnPopModal(animated);
+                result.Parent = null;
+                _owner.OnModalPopped(result);
+                return result;
+            }
+
+            protected override async Task OnPushModal(Page modal, bool animated)
+            {
+                _owner.OnModalPushing(modal);
+
+                modal.Parent = _owner;
+
+                if (modal.NavigationProxy.ModalStack.Count == 0)
+                {
+                    modal.NavigationProxy.Inner = this;
+                    await base.OnPushModal(modal, animated);
+                }
+                else
+                {
+                    await base.OnPushModal(modal, animated);
+                    modal.NavigationProxy.Inner = this;
+                }
+
+                _owner.OnModalPushed(modal);
+            }
+        }
+
         private global::System.Runtime.InteropServices.HandleRef swigCPtr;
 
         internal Application(global::System.IntPtr cPtr, bool cMemoryOwn) : base(NDalicPINVOKE.Application_SWIGUpcast(cPtr), cMemoryOwn)
         {
+            NavigationProxy = new NavigationImpl(this);
+            SetCurrentApplication(this);
+
+            _platformConfigurationRegistry = new Lazy<PlatformConfigurationRegistry<Application>>(() => new PlatformConfigurationRegistry<Application>(this));
             swigCPtr = new global::System.Runtime.InteropServices.HandleRef(this, cPtr);
+
+            SendResume();
         }
 
         internal static global::System.Runtime.InteropServices.HandleRef getCPtr(Application obj)
@@ -1106,6 +1535,7 @@ namespace Tizen.NUI
         {
             Application ret = new Application(Interop.Application.Application_New__SWIG_3(argc, stylesheet, (int)windowMode), true);
             if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
+            ret.SendResume();
             return ret;
         }