2 * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 using System.Collections.Concurrent;
19 using System.ComponentModel;
20 using System.Globalization;
21 using System.Runtime.InteropServices;
23 using System.Threading;
24 using System.Threading.Tasks;
26 using Tizen.Applications.CoreBackend;
28 namespace Tizen.Applications
31 /// This class represents an application controlled lifecycles by the backend system.
33 /// <since_tizen> 3 </since_tizen>
34 public class CoreApplication : Application
36 private readonly ICoreBackend _backend;
37 private readonly ICoreTask _task;
38 private bool _disposedValue = false;
41 /// Initializes the CoreApplication class.
43 /// <param name="backend">The backend instance implementing ICoreBacked interface.</param>
44 /// <since_tizen> 3 </since_tizen>
45 public CoreApplication(ICoreBackend backend)
52 /// Initializes the CoreApplication class.
54 /// <param name="backend">The backend instance implementing ICoreBackend interface.</param>
55 /// <param name="task">The backend instance implmenting ICoreTask interface.</param>
56 /// <since_tizen> 10 </since_tizen>
57 [EditorBrowsable(EditorBrowsableState.Never)]
58 public CoreApplication(ICoreBackend backend, ICoreTask task)
65 /// Occurs when the application is launched.
67 /// <since_tizen> 3 </since_tizen>
68 public event EventHandler Created;
71 /// Occurs when the application is about to shutdown.
73 /// <since_tizen> 3 </since_tizen>
74 public event EventHandler Terminated;
77 /// Occurs whenever the application receives the appcontrol message.
79 /// <since_tizen> 3 </since_tizen>
80 public event EventHandler<AppControlReceivedEventArgs> AppControlReceived;
83 /// Occurs when the system memory is low.
85 /// <since_tizen> 3 </since_tizen>
86 public event EventHandler<LowMemoryEventArgs> LowMemory;
89 /// Occurs when the system battery is low.
91 /// <since_tizen> 3 </since_tizen>
92 public event EventHandler<LowBatteryEventArgs> LowBattery;
95 /// Occurs when the system language is chagned.
97 /// <since_tizen> 3 </since_tizen>
98 public event EventHandler<LocaleChangedEventArgs> LocaleChanged;
101 /// Occurs when the region format is changed.
103 /// <since_tizen> 3 </since_tizen>
104 public event EventHandler<RegionFormatChangedEventArgs> RegionFormatChanged;
107 /// Occurs when the device orientation is changed.
109 /// <since_tizen> 3 </since_tizen>
110 public event EventHandler<DeviceOrientationEventArgs> DeviceOrientationChanged;
113 /// Occurs when the time zone is changed.
115 /// <since_tizen> 11 </since_tizen>
116 public event EventHandler<TimeZoneChangedEventArgs> TimeZoneChanged;
119 /// The backend instance.
121 /// <since_tizen> 3 </since_tizen>
122 protected ICoreBackend Backend { get { return _backend; } }
125 /// Runs the application's main loop.
127 /// <param name="args">Arguments from commandline.</param>
128 /// <since_tizen> 3 </since_tizen>
129 public override void Run(string[] args)
133 _backend.AddEventHandler(EventType.Created, OnCreate);
134 _backend.AddEventHandler(EventType.Terminated, OnTerminate);
135 _backend.AddEventHandler<AppControlReceivedEventArgs>(EventType.AppControlReceived, OnAppControlReceived);
136 _backend.AddEventHandler<LowMemoryEventArgs>(EventType.LowMemory, OnLowMemory);
137 _backend.AddEventHandler<LowBatteryEventArgs>(EventType.LowBattery, OnLowBattery);
138 _backend.AddEventHandler<LocaleChangedEventArgs>(EventType.LocaleChanged, OnLocaleChanged);
139 _backend.AddEventHandler<RegionFormatChangedEventArgs>(EventType.RegionFormatChanged, OnRegionFormatChanged);
140 _backend.AddEventHandler<DeviceOrientationEventArgs>(EventType.DeviceOrientationChanged, OnDeviceOrientationChanged);
141 _backend.AddEventHandler<TimeZoneChangedEventArgs>(EventType.TimeZoneChanged, OnTimeZoneChanged);
143 string[] argsClone = new string[args == null ? 1 : args.Length + 1];
144 if (args != null && args.Length > 1)
146 args.CopyTo(argsClone, 1);
148 argsClone[0] = string.Empty;
152 ICoreTaskBackend backend = (ICoreTaskBackend)_backend;
153 backend.SetCoreTask(_task);
154 backend.Run(argsClone);
158 _backend.Run(argsClone);
163 /// Exits the main loop of the application.
165 /// <since_tizen> 3 </since_tizen>
166 public override void Exit()
172 /// Overrides this method if want to handle behavior when the application is launched.
173 /// If base.OnCreated() is not called, the event 'Created' will not be emitted.
175 /// <since_tizen> 3 </since_tizen>
176 protected virtual void OnCreate()
180 TizenUISynchronizationContext.Initialize();
183 if (!GlobalizationMode.Invariant)
185 string locale = ULocale.GetDefaultLocale();
186 ChangeCurrentUICultureInfo(locale);
187 ChangeCurrentCultureInfo(locale);
191 Log.Warn(LogTag, "Run in invariant mode");
194 Created?.Invoke(this, EventArgs.Empty);
198 /// Overrides this method if want to handle behavior when the application is terminated.
199 /// If base.OnTerminate() is not called, the event 'Terminated' will not be emitted.
201 /// <since_tizen> 3 </since_tizen>
202 protected virtual void OnTerminate()
204 Terminated?.Invoke(this, EventArgs.Empty);
208 /// Overrides this method if want to handle behavior when the application receives the appcontrol message.
209 /// If base.OnAppControlReceived() is not called, the event 'AppControlReceived' will not be emitted.
211 /// <param name="e"></param>
212 /// <since_tizen> 3 </since_tizen>
213 protected virtual void OnAppControlReceived(AppControlReceivedEventArgs e)
217 Log.Error(LogTag, "e is null");
221 AppControlReceived?.Invoke(this, e);
225 /// Overrides this method if want to handle behavior when the system memory is low.
226 /// If base.OnLowMemory() is not called, the event 'LowMemory' will not be emitted.
228 /// <param name="e">The low memory event argument</param>
229 /// <since_tizen> 3 </since_tizen>
230 protected virtual void OnLowMemory(LowMemoryEventArgs e)
234 Log.Error(LogTag, "e is null");
238 LowMemory?.Invoke(this, e);
239 if (e.LowMemoryStatus == LowMemoryStatus.SoftWarning || e.LowMemoryStatus == LowMemoryStatus.HardWarning)
246 /// Overrides this method if want to handle behavior when the system battery is low.
247 /// If base.OnLowBattery() is not called, the event 'LowBattery' will not be emitted.
249 /// <param name="e">The low battery event argument</param>
250 /// <since_tizen> 3 </since_tizen>
251 protected virtual void OnLowBattery(LowBatteryEventArgs e)
255 Log.Error(LogTag, "e is null");
259 LowBattery?.Invoke(this, e);
263 /// Overrides this method if want to handle behavior when the system language is changed.
264 /// If base.OnLocaleChanged() is not called, the event 'LocaleChanged' will not be emitted.
266 /// <param name="e">The locale changed event argument</param>
267 /// <since_tizen> 3 </since_tizen>
268 protected virtual void OnLocaleChanged(LocaleChangedEventArgs e)
272 Log.Error(LogTag, "e is null");
276 if (!GlobalizationMode.Invariant)
278 ChangeCurrentUICultureInfo(e.Locale);
281 LocaleChanged?.Invoke(this, e);
285 /// Overrides this method if want to handle behavior when the region format is changed.
286 /// If base.OnRegionFormatChanged() is not called, the event 'RegionFormatChanged' will not be emitted.
288 /// <param name="e">The region format changed event argument</param>
289 /// <since_tizen> 3 </since_tizen>
290 protected virtual void OnRegionFormatChanged(RegionFormatChangedEventArgs e)
294 Log.Error(LogTag, "e is null");
298 if (!GlobalizationMode.Invariant)
300 ChangeCurrentCultureInfo(e.Region);
303 RegionFormatChanged?.Invoke(this, e);
307 /// Overrides this method if want to handle behavior when the device orientation is changed.
308 /// If base.OnRegionFormatChanged() is not called, the event 'RegionFormatChanged' will not be emitted.
310 /// <param name="e">The device orientation changed event argument</param>
311 /// <since_tizen> 3 </since_tizen>
312 protected virtual void OnDeviceOrientationChanged(DeviceOrientationEventArgs e)
314 DeviceOrientationChanged?.Invoke(this, e);
318 /// Override this method if you want to handle behavior when the time zone is changed.
319 /// If base.OnTimeZoneChanged() is not called, the event "TimeZoneChanged" will not be emitted.
321 /// <param name="e">The time zone changed event argument</param>
322 /// <since_tizen> 11 </since_tizen>
323 protected virtual void OnTimeZoneChanged(TimeZoneChangedEventArgs e)
325 TimeZoneChanged?.Invoke(this, e);
329 /// Dispatches an asynchronous message to a main loop of the CoreApplication.
332 /// If an application uses UI thread App Model, the asynchronous message will be delivered to the UI thread.
333 /// If not, the asynchronous message will be delivered to the main thread.
335 /// <param name="runner">The runner callaback.</param>
336 /// <exception cref="ArgumentNullException">Thrown when the runner is null.</exception>
337 /// <since_tizen> 10 </since_tizen>
338 [EditorBrowsable(EditorBrowsableState.Never)]
339 public static void Post(Action runner)
343 throw new ArgumentNullException(nameof(runner));
346 GSourceManager.Post(runner, true);
350 /// Dispatches an asynchronous message to a main loop of the CoreApplication.
353 /// If an application uses UI thread App Model, the asynchronous message will be delivered to the UI thread.
354 /// If not, the asynchronous message will be delivered to the main thread.
356 /// <typeparam name="T">The type of the result.</typeparam>
357 /// <param name="runner">The runner callback.</param>
358 /// <exception cref="ArgumentNullException">Thrown when the runner is null.</exception>
359 /// <returns>A task with the result.</returns>
360 /// <since_tizen> 10 </since_tizen>
361 [EditorBrowsable(EditorBrowsableState.Never)]
362 public static async Task<T> Post<T>(Func<T> runner)
366 throw new ArgumentNullException(nameof(runner));
369 var task = new TaskCompletionSource<T>();
370 GSourceManager.Post(() => { task.SetResult(runner()); }, true);
371 return await task.Task.ConfigureAwait(false);
375 /// Releases any unmanaged resources used by this object. Can also dispose any other disposable objects.
377 /// <param name="disposing">If true, disposes any disposable objects. If false, does not dispose disposable objects.</param>
378 /// <since_tizen> 3 </since_tizen>
379 protected override void Dispose(bool disposing)
388 _disposedValue = true;
390 base.Dispose(disposing);
393 private CultureInfo ConvertCultureInfo(string locale)
395 ULocale pLocale = new ULocale(locale);
396 string cultureName = CultureInfoHelper.GetCultureName(pLocale.Locale.Replace("_", "-"));
398 if (!string.IsNullOrEmpty(cultureName))
402 return new CultureInfo(cultureName);
404 catch (CultureNotFoundException)
406 Log.Error(LogTag, "CultureNotFoundException occurs. CultureName: " + cultureName);
412 return new CultureInfo(pLocale.LCID);
414 catch (ArgumentOutOfRangeException)
416 return GetFallbackCultureInfo(pLocale);
418 catch (CultureNotFoundException)
420 return GetFallbackCultureInfo(pLocale);
424 private void ChangeCurrentCultureInfo(string locale)
426 CultureInfo cultureInfo = ConvertCultureInfo(locale);
427 if (cultureInfo != null)
429 CultureInfo.CurrentCulture = cultureInfo;
433 Log.Error(LogTag, "CultureInfo is null. locale: " + locale);
437 private void ChangeCurrentUICultureInfo(string locale)
439 CultureInfo cultureInfo = ConvertCultureInfo(locale);
440 if (cultureInfo != null)
442 CultureInfo.CurrentUICulture = cultureInfo;
446 Log.Error(LogTag, "CultureInfo is null. locale: " + locale);
450 private bool ExistCultureInfo(string locale)
452 foreach (var cultureInfo in CultureInfo.GetCultures(CultureTypes.AllCultures))
454 if (cultureInfo.Name == locale)
463 private CultureInfo GetCultureInfo(string locale)
465 if (!ExistCultureInfo(locale))
472 return new CultureInfo(locale);
474 catch (CultureNotFoundException)
480 private CultureInfo GetFallbackCultureInfo(ULocale uLocale)
482 CultureInfo fallbackCultureInfo = null;
483 string locale = string.Empty;
485 if (uLocale.Locale != null)
487 locale = uLocale.Locale.Replace("_", "-");
488 fallbackCultureInfo = GetCultureInfo(locale);
491 if (fallbackCultureInfo == null && uLocale.Language != null && uLocale.Script != null && uLocale.Country != null)
493 locale = uLocale.Language + "-" + uLocale.Script + "-" + uLocale.Country;
494 fallbackCultureInfo = GetCultureInfo(locale);
497 if (fallbackCultureInfo == null && uLocale.Language != null && uLocale.Script != null)
499 locale = uLocale.Language + "-" + uLocale.Script;
500 fallbackCultureInfo = GetCultureInfo(locale);
503 if (fallbackCultureInfo == null && uLocale.Language != null && uLocale.Country != null)
505 locale = uLocale.Language + "-" + uLocale.Country;
506 fallbackCultureInfo = GetCultureInfo(locale);
509 if (fallbackCultureInfo == null && uLocale.Language != null)
511 locale = uLocale.Language;
512 fallbackCultureInfo = GetCultureInfo(locale);
515 if (fallbackCultureInfo == null)
519 fallbackCultureInfo = new CultureInfo("en");
521 catch (CultureNotFoundException e)
523 Log.Error(LogTag, "Failed to create CultureInfo. err = " + e.Message);
527 return fallbackCultureInfo;
531 internal static class GlobalizationMode
533 private static int _invariant = -1;
535 internal static bool Invariant
539 if (_invariant == -1)
541 string value = Environment.GetEnvironmentVariable("DOTNET_SYSTEM_GLOBALIZATION_INVARIANT");
542 _invariant = value != null ? (value.Equals("1") ? 1 : 0) : 0;
545 return _invariant != 0;
550 internal class ULocale
552 private const int ULOC_FULLNAME_CAPACITY = 157;
553 private const int ULOC_LANG_CAPACITY = 12;
554 private const int ULOC_SCRIPT_CAPACITY = 6;
555 private const int ULOC_COUNTRY_CAPACITY = 4;
556 private const int ULOC_VARIANT_CAPACITY = ULOC_FULLNAME_CAPACITY;
558 internal ULocale(string locale)
560 Locale = Canonicalize(locale);
561 Language = GetLanguage(Locale);
562 Script = GetScript(Locale);
563 Country = GetCountry(Locale);
564 Variant = GetVariant(Locale);
565 LCID = GetLCID(Locale);
568 internal string Locale { get; private set; }
569 internal string Language { get; private set; }
570 internal string Script { get; private set; }
571 internal string Country { get; private set; }
572 internal string Variant { get; private set; }
573 internal int LCID { get; private set; }
575 private string Canonicalize(string localeName)
577 // Get the locale name from ICU
578 StringBuilder sb = new StringBuilder(ULOC_FULLNAME_CAPACITY);
579 if (Interop.BaseUtilsi18n.Canonicalize(localeName, sb, sb.Capacity) <= 0)
584 return sb.ToString();
587 private string GetLanguage(string locale)
589 // Get the language name from ICU
590 StringBuilder sb = new StringBuilder(ULOC_LANG_CAPACITY);
591 if (Interop.BaseUtilsi18n.GetLanguage(locale, sb, sb.Capacity, out int bufSizeLanguage) != 0)
596 return sb.ToString();
599 private string GetScript(string locale)
601 // Get the script name from ICU
602 StringBuilder sb = new StringBuilder(ULOC_SCRIPT_CAPACITY);
603 if (Interop.BaseUtilsi18n.GetScript(locale, sb, sb.Capacity) <= 0)
608 return sb.ToString();
611 private string GetCountry(string locale)
615 // Get the country name from ICU
616 StringBuilder sb = new StringBuilder(ULOC_COUNTRY_CAPACITY);
617 if (Interop.BaseUtilsi18n.GetCountry(locale, sb, sb.Capacity, out err) <= 0)
622 return sb.ToString();
625 private string GetVariant(string locale)
627 // Get the variant name from ICU
628 StringBuilder sb = new StringBuilder(ULOC_VARIANT_CAPACITY);
629 if (Interop.BaseUtilsi18n.GetVariant(locale, sb, sb.Capacity) <= 0)
634 return sb.ToString();
637 private int GetLCID(string locale)
639 // Get the LCID from ICU
640 uint lcid = Interop.BaseUtilsi18n.GetLCID(locale);
644 internal static string GetDefaultLocale()
646 IntPtr stringPtr = Interop.Libc.GetEnvironmentVariable("LANG");
647 if (stringPtr == IntPtr.Zero)
652 return Marshal.PtrToStringAnsi(stringPtr);