using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Threading.Tasks; using Xamarin.Forms.Platform; namespace Xamarin.Forms { public class Application : Element, IResourcesProvider, IApplicationController, IElementConfiguration { static Application s_current; readonly Task> _propertiesTask; readonly Lazy> _platformConfigurationRegistry; IAppIndexingProvider _appIndexProvider; bool _isSaving; ReadOnlyCollection _logicalChildren; Page _mainPage; ResourceDictionary _resources; bool _saveAgain; protected Application() { var f = false; if (f) Loader.Load(); NavigationProxy = new NavigationImpl(this); Current = this; _propertiesTask = GetPropertiesAsync(); SystemResources = DependencyService.Get().GetSystemResources(); SystemResources.ValuesChanged += OnParentResourcesChanged; _platformConfigurationRegistry = new Lazy>(() => new PlatformConfigurationRegistry(this)); } 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; } } public static Application Current { get { return s_current; } internal 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 Properties { get { return _propertiesTask.Result; } } internal override ReadOnlyCollection LogicalChildrenInternal { get { return _logicalChildren ?? (_logicalChildren = new ReadOnlyCollection(InternalChildren)); } } internal NavigationProxy NavigationProxy { get; } internal int PanGestureId { get; set; } internal IResourceDictionary SystemResources { get; } ObservableCollection InternalChildren { get; } = new ObservableCollection(); void IApplicationController.SetAppIndexingProvider(IAppIndexingProvider provider) { _appIndexProvider = provider; } public ResourceDictionary Resources { get { 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 ModalPopped; public event EventHandler ModalPopping; public event EventHandler ModalPushed; public event EventHandler ModalPushing; public async Task SavePropertiesAsync() { if (Device.IsInvokeRequired) Device.BeginInvokeOnMainThread(async () => await SetPropertiesAsync()); else await SetPropertiesAsync(); } public IPlatformElementConfiguration On() where T : IConfigPlatform { return _platformConfigurationRegistry.Value.On(); } 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() { } internal static void ClearCurrent() { s_current = null; } internal static bool IsApplicationOrNull(Element element) { return element == null || element is Application; } internal override void OnParentResourcesChanged(IEnumerable> values) { if (Resources == null || Resources.Count == 0) { base.OnParentResourcesChanged(values); return; } var innerKeys = new HashSet(); var changedResources = new List>(); foreach (KeyValuePair c in Resources) innerKeys.Add(c.Key); foreach (KeyValuePair value in values) { if (innerKeys.Add(value.Key)) changedResources.Add(value); } OnResourcesChanged(changedResources); } internal event EventHandler PopCanceled; internal void SendOnAppLinkRequestReceived(Uri uri) { OnAppLinkRequestReceived(uri); } internal void SendResume() { s_current = this; OnResume(); } internal Task SendSleepAsync() { OnSleep(); return SavePropertiesAsync(); } internal void SendStart() { OnStart(); } async Task> GetPropertiesAsync() { var deserializer = DependencyService.Get(); if (deserializer == null) { Log.Warning("Startup", "No IDeserialzier was found registered"); return new Dictionary(4); } IDictionary properties = await deserializer.DeserializePropertiesAsync().ConfigureAwait(false); if (properties == null) properties = new Dictionary(4); return properties; } void OnModalPopped(Page modalPage) { EventHandler handler = ModalPopped; if (handler != null) handler(this, new ModalPoppedEventArgs(modalPage)); } bool OnModalPopping(Page modalPage) { EventHandler handler = ModalPopping; var args = new ModalPoppingEventArgs(modalPage); if (handler != null) handler(this, args); return args.Cancel; } void OnModalPushed(Page modalPage) { EventHandler handler = ModalPushed; if (handler != null) handler(this, new ModalPushedEventArgs(modalPage)); } void OnModalPushing(Page modalPage) { EventHandler handler = ModalPushing; if (handler != null) handler(this, new ModalPushingEventArgs(modalPage)); } void OnPopCanceled() { EventHandler handler = PopCanceled; if (handler != null) handler(this, EventArgs.Empty); } async Task SetPropertiesAsync() { if (_isSaving) { _saveAgain = true; return; } _isSaving = true; await DependencyService.Get().SerializePropertiesAsync(Properties); if (_saveAgain) await DependencyService.Get().SerializePropertiesAsync(Properties); _isSaving = _saveAgain = false; } class NavigationImpl : NavigationProxy { readonly Application _owner; public NavigationImpl(Application owner) { _owner = owner; } protected override async Task 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); } } } }