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;
40 private static System.Timers.Timer sTimer;
43 /// Initializes the CoreApplication class.
45 /// <param name="backend">The backend instance implementing ICoreBacked interface.</param>
46 /// <since_tizen> 3 </since_tizen>
47 public CoreApplication(ICoreBackend backend)
54 /// Initializes the CoreApplication class.
56 /// <param name="backend">The backend instance implementing ICoreBackend interface.</param>
57 /// <param name="task">The backend instance implmenting ICoreTask interface.</param>
58 /// <since_tizen> 10 </since_tizen>
59 [EditorBrowsable(EditorBrowsableState.Never)]
60 public CoreApplication(ICoreBackend backend, ICoreTask task)
67 /// Occurs when the application is launched.
69 /// <since_tizen> 3 </since_tizen>
70 public event EventHandler Created;
73 /// Occurs when the application is about to shutdown.
75 /// <since_tizen> 3 </since_tizen>
76 public event EventHandler Terminated;
79 /// Occurs whenever the application receives the appcontrol message.
81 /// <since_tizen> 3 </since_tizen>
82 public event EventHandler<AppControlReceivedEventArgs> AppControlReceived;
85 /// Occurs when the system memory is low.
87 /// <since_tizen> 3 </since_tizen>
88 public event EventHandler<LowMemoryEventArgs> LowMemory;
91 /// Occurs when the system battery is low.
93 /// <since_tizen> 3 </since_tizen>
94 public event EventHandler<LowBatteryEventArgs> LowBattery;
97 /// Occurs when the system language is chagned.
99 /// <since_tizen> 3 </since_tizen>
100 public event EventHandler<LocaleChangedEventArgs> LocaleChanged;
103 /// Occurs when the region format is changed.
105 /// <since_tizen> 3 </since_tizen>
106 public event EventHandler<RegionFormatChangedEventArgs> RegionFormatChanged;
109 /// Occurs when the device orientation is changed.
111 /// <since_tizen> 3 </since_tizen>
112 public event EventHandler<DeviceOrientationEventArgs> DeviceOrientationChanged;
115 /// The backend instance.
117 /// <since_tizen> 3 </since_tizen>
118 protected ICoreBackend Backend { get { return _backend; } }
121 /// Runs the application's main loop.
123 /// <param name="args">Arguments from commandline.</param>
124 /// <since_tizen> 3 </since_tizen>
125 public override void Run(string[] args)
129 _backend.AddEventHandler(EventType.Created, OnCreate);
130 _backend.AddEventHandler(EventType.Terminated, OnTerminate);
131 _backend.AddEventHandler<AppControlReceivedEventArgs>(EventType.AppControlReceived, OnAppControlReceived);
132 _backend.AddEventHandler<LowMemoryEventArgs>(EventType.LowMemory, OnLowMemory);
133 _backend.AddEventHandler<LowBatteryEventArgs>(EventType.LowBattery, OnLowBattery);
134 _backend.AddEventHandler<LocaleChangedEventArgs>(EventType.LocaleChanged, OnLocaleChanged);
135 _backend.AddEventHandler<RegionFormatChangedEventArgs>(EventType.RegionFormatChanged, OnRegionFormatChanged);
136 _backend.AddEventHandler<DeviceOrientationEventArgs>(EventType.DeviceOrientationChanged, OnDeviceOrientationChanged);
138 string[] argsClone = new string[args == null ? 1 : args.Length + 1];
139 if (args != null && args.Length > 1)
141 args.CopyTo(argsClone, 1);
143 argsClone[0] = string.Empty;
147 ICoreTaskBackend backend = (ICoreTaskBackend)_backend;
148 backend.SetCoreTask(_task);
149 backend.Run(argsClone);
153 _backend.Run(argsClone);
158 /// Exits the main loop of the application.
160 /// <since_tizen> 3 </since_tizen>
161 public override void Exit()
167 /// Overrides this method if want to handle behavior when the application is launched.
168 /// If base.OnCreated() is not called, the event 'Created' will not be emitted.
170 /// <since_tizen> 3 </since_tizen>
171 protected virtual void OnCreate()
175 TizenUISynchronizationContext.Initialize();
178 if (!GlobalizationMode.Invariant)
180 string locale = ULocale.GetDefaultLocale();
181 ChangeCurrentUICultureInfo(locale);
182 ChangeCurrentCultureInfo(locale);
186 Log.Warn(LogTag, "Run in invariant mode");
189 Created?.Invoke(this, EventArgs.Empty);
193 /// Overrides this method if want to handle behavior when the application is terminated.
194 /// If base.OnTerminate() is not called, the event 'Terminated' will not be emitted.
196 /// <since_tizen> 3 </since_tizen>
197 protected virtual void OnTerminate()
199 Terminated?.Invoke(this, EventArgs.Empty);
203 /// Overrides this method if want to handle behavior when the application receives the appcontrol message.
204 /// If base.OnAppControlReceived() is not called, the event 'AppControlReceived' will not be emitted.
206 /// <param name="e"></param>
207 /// <since_tizen> 3 </since_tizen>
208 protected virtual void OnAppControlReceived(AppControlReceivedEventArgs e)
210 AppControlReceived?.Invoke(this, e);
214 /// Overrides this method if want to handle behavior when the system memory is low.
215 /// If base.OnLowMemory() is not called, the event 'LowMemory' will not be emitted.
217 /// <param name="e">The low memory event argument</param>
218 /// <since_tizen> 3 </since_tizen>
219 protected virtual void OnLowMemory(LowMemoryEventArgs e)
221 LowMemory?.Invoke(this, e);
222 if (e.LowMemoryStatus == LowMemoryStatus.SoftWarning || e.LowMemoryStatus == LowMemoryStatus.HardWarning)
224 double interval = new Random().Next(10 * 1000);
226 interval = 10 * 1000;
228 sTimer = new System.Timers.Timer(interval);
229 sTimer.Elapsed += OnTimedEvent;
230 sTimer.AutoReset = false;
231 sTimer.Enabled = true;
235 private static void OnTimedEvent(Object source, ElapsedEventArgs e)
241 /// Overrides this method if want to handle behavior when the system battery is low.
242 /// If base.OnLowBattery() is not called, the event 'LowBattery' will not be emitted.
244 /// <param name="e">The low battery event argument</param>
245 /// <since_tizen> 3 </since_tizen>
246 protected virtual void OnLowBattery(LowBatteryEventArgs e)
248 LowBattery?.Invoke(this, e);
252 /// Overrides this method if want to handle behavior when the system language is changed.
253 /// If base.OnLocaleChanged() is not called, the event 'LocaleChanged' will not be emitted.
255 /// <param name="e">The locale changed event argument</param>
256 /// <since_tizen> 3 </since_tizen>
257 protected virtual void OnLocaleChanged(LocaleChangedEventArgs e)
259 if (!GlobalizationMode.Invariant)
261 ChangeCurrentUICultureInfo(e.Locale);
264 LocaleChanged?.Invoke(this, e);
268 /// Overrides this method if want to handle behavior when the region format is changed.
269 /// If base.OnRegionFormatChanged() is not called, the event 'RegionFormatChanged' will not be emitted.
271 /// <param name="e">The region format changed event argument</param>
272 /// <since_tizen> 3 </since_tizen>
273 protected virtual void OnRegionFormatChanged(RegionFormatChangedEventArgs e)
275 if (!GlobalizationMode.Invariant)
277 ChangeCurrentCultureInfo(e.Region);
280 RegionFormatChanged?.Invoke(this, e);
284 /// Overrides this method if want to handle behavior when the device orientation is changed.
285 /// If base.OnRegionFormatChanged() is not called, the event 'RegionFormatChanged' will not be emitted.
287 /// <param name="e">The device orientation changed event argument</param>
288 /// <since_tizen> 3 </since_tizen>
289 protected virtual void OnDeviceOrientationChanged(DeviceOrientationEventArgs e)
291 DeviceOrientationChanged?.Invoke(this, e);
295 /// Dispatches an asynchronous message to the main loop of the CoreTask.
297 /// <param name="runner">The runner callaback.</param>
298 /// <exception cref="ArgumentNullException">Thrown when the runner is null.</exception>
299 /// <since_tizen> 10 </since_tizen>
300 [EditorBrowsable(EditorBrowsableState.Never)]
301 public void Post(Action runner)
305 throw new ArgumentNullException(nameof(runner));
308 GSourceManager.Post(runner);
312 /// Dispatches an asynchronous message to the main loop of the CoreTask.
314 /// <typeparam name="T">The type of the result.</typeparam>
315 /// <param name="runner">The runner callback.</param>
316 /// <exception cref="ArgumentNullException">Thrown when the runner is null.</exception>
317 /// <returns>A task with the result.</returns>
318 /// <since_tizen> 10 </since_tizen>
319 [EditorBrowsable(EditorBrowsableState.Never)]
320 public async Task<T> Post<T>(Func<T> runner)
324 throw new ArgumentNullException(nameof(runner));
327 var task = new TaskCompletionSource<T>();
328 GSourceManager.Post(() => { task.SetResult(runner()); });
329 return await task.Task.ConfigureAwait(false);
333 /// Releases any unmanaged resources used by this object. Can also dispose any other disposable objects.
335 /// <param name="disposing">If true, disposes any disposable objects. If false, does not dispose disposable objects.</param>
336 /// <since_tizen> 3 </since_tizen>
337 protected override void Dispose(bool disposing)
346 _disposedValue = true;
348 base.Dispose(disposing);
351 private CultureInfo ConvertCultureInfo(string locale)
353 ULocale pLocale = new ULocale(locale);
354 string cultureName = CultureInfoHelper.GetCultureName(pLocale.Locale.Replace("_", "-"));
356 if (!string.IsNullOrEmpty(cultureName))
360 return new CultureInfo(cultureName);
362 catch (CultureNotFoundException)
364 Log.Error(LogTag, "CultureNotFoundException occurs. CultureName: " + cultureName);
370 return new CultureInfo(pLocale.LCID);
372 catch (ArgumentOutOfRangeException)
374 return GetFallbackCultureInfo(pLocale);
376 catch (CultureNotFoundException)
378 return GetFallbackCultureInfo(pLocale);
382 private void ChangeCurrentCultureInfo(string locale)
384 CultureInfo cultureInfo = ConvertCultureInfo(locale);
385 if (cultureInfo != null)
387 CultureInfo.CurrentCulture = cultureInfo;
391 Log.Error(LogTag, "CultureInfo is null. locale: " + locale);
395 private void ChangeCurrentUICultureInfo(string locale)
397 CultureInfo cultureInfo = ConvertCultureInfo(locale);
398 if (cultureInfo != null)
400 CultureInfo.CurrentUICulture = cultureInfo;
404 Log.Error(LogTag, "CultureInfo is null. locale: " + locale);
408 private bool ExistCultureInfo(string locale)
410 foreach (var cultureInfo in CultureInfo.GetCultures(CultureTypes.AllCultures))
412 if (cultureInfo.Name == locale)
421 private CultureInfo GetCultureInfo(string locale)
423 if (!ExistCultureInfo(locale))
430 return new CultureInfo(locale);
432 catch (CultureNotFoundException)
438 private CultureInfo GetFallbackCultureInfo(ULocale uLocale)
440 CultureInfo fallbackCultureInfo = null;
441 string locale = string.Empty;
443 if (uLocale.Locale != null)
445 locale = uLocale.Locale.Replace("_", "-");
446 fallbackCultureInfo = GetCultureInfo(locale);
449 if (fallbackCultureInfo == null && uLocale.Language != null && uLocale.Script != null && uLocale.Country != null)
451 locale = uLocale.Language + "-" + uLocale.Script + "-" + uLocale.Country;
452 fallbackCultureInfo = GetCultureInfo(locale);
455 if (fallbackCultureInfo == null && uLocale.Language != null && uLocale.Script != null)
457 locale = uLocale.Language + "-" + uLocale.Script;
458 fallbackCultureInfo = GetCultureInfo(locale);
461 if (fallbackCultureInfo == null && uLocale.Language != null && uLocale.Country != null)
463 locale = uLocale.Language + "-" + uLocale.Country;
464 fallbackCultureInfo = GetCultureInfo(locale);
467 if (fallbackCultureInfo == null && uLocale.Language != null)
469 locale = uLocale.Language;
470 fallbackCultureInfo = GetCultureInfo(locale);
473 if (fallbackCultureInfo == null)
477 fallbackCultureInfo = new CultureInfo("en");
479 catch (CultureNotFoundException e)
481 Log.Error(LogTag, "Failed to create CultureInfo. err = " + e.Message);
485 return fallbackCultureInfo;
489 internal static class GlobalizationMode
491 private static int _invariant = -1;
493 internal static bool Invariant
497 if (_invariant == -1)
499 string value = Environment.GetEnvironmentVariable("DOTNET_SYSTEM_GLOBALIZATION_INVARIANT");
500 _invariant = value != null ? (value.Equals("1") ? 1 : 0) : 0;
503 return _invariant != 0;
508 internal class ULocale
510 private const int ULOC_FULLNAME_CAPACITY = 157;
511 private const int ULOC_LANG_CAPACITY = 12;
512 private const int ULOC_SCRIPT_CAPACITY = 6;
513 private const int ULOC_COUNTRY_CAPACITY = 4;
514 private const int ULOC_VARIANT_CAPACITY = ULOC_FULLNAME_CAPACITY;
516 internal ULocale(string locale)
518 Locale = Canonicalize(locale);
519 Language = GetLanguage(Locale);
520 Script = GetScript(Locale);
521 Country = GetCountry(Locale);
522 Variant = GetVariant(Locale);
523 LCID = GetLCID(Locale);
526 internal string Locale { get; private set; }
527 internal string Language { get; private set; }
528 internal string Script { get; private set; }
529 internal string Country { get; private set; }
530 internal string Variant { get; private set; }
531 internal int LCID { get; private set; }
533 private string Canonicalize(string localeName)
535 // Get the locale name from ICU
536 StringBuilder sb = new StringBuilder(ULOC_FULLNAME_CAPACITY);
537 if (Interop.BaseUtilsi18n.Canonicalize(localeName, sb, sb.Capacity) <= 0)
542 return sb.ToString();
545 private string GetLanguage(string locale)
547 // Get the language name from ICU
548 StringBuilder sb = new StringBuilder(ULOC_LANG_CAPACITY);
549 if (Interop.BaseUtilsi18n.GetLanguage(locale, sb, sb.Capacity, out int bufSizeLanguage) != 0)
554 return sb.ToString();
557 private string GetScript(string locale)
559 // Get the script name from ICU
560 StringBuilder sb = new StringBuilder(ULOC_SCRIPT_CAPACITY);
561 if (Interop.BaseUtilsi18n.GetScript(locale, sb, sb.Capacity) <= 0)
566 return sb.ToString();
569 private string GetCountry(string locale)
573 // Get the country name from ICU
574 StringBuilder sb = new StringBuilder(ULOC_COUNTRY_CAPACITY);
575 if (Interop.BaseUtilsi18n.GetCountry(locale, sb, sb.Capacity, out err) <= 0)
580 return sb.ToString();
583 private string GetVariant(string locale)
585 // Get the variant name from ICU
586 StringBuilder sb = new StringBuilder(ULOC_VARIANT_CAPACITY);
587 if (Interop.BaseUtilsi18n.GetVariant(locale, sb, sb.Capacity) <= 0)
592 return sb.ToString();
595 private int GetLCID(string locale)
597 // Get the LCID from ICU
598 uint lcid = Interop.BaseUtilsi18n.GetLCID(locale);
602 internal static string GetDefaultLocale()
604 IntPtr stringPtr = IntPtr.Zero;
605 if (Interop.BaseUtilsi18n.GetDefault(out stringPtr) != 0)
610 if (stringPtr == IntPtr.Zero)
615 return Marshal.PtrToStringAnsi(stringPtr);