Add exception handling for creating CultureInfo (#3637)
[platform/core/csapi/tizenfx.git] / src / Tizen.Applications.Common / Tizen.Applications / CoreApplication.cs
1 /*
2  * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 using System;
18 using System.Globalization;
19 using System.Runtime.InteropServices;
20 using System.Text;
21 using System.Timers;
22 using Tizen.Applications.CoreBackend;
23
24 namespace Tizen.Applications
25 {
26     /// <summary>
27     /// This class represents an application controlled lifecycles by the backend system.
28     /// </summary>
29     /// <since_tizen> 3 </since_tizen>
30     public class CoreApplication : Application
31     {
32         private readonly ICoreBackend _backend;
33         private bool _disposedValue = false;
34
35         private static Timer sTimer;
36
37         /// <summary>
38         /// Initializes the CoreApplication class.
39         /// </summary>
40         /// <param name="backend">The backend instance implementing ICoreBacked interface.</param>
41         /// <since_tizen> 3 </since_tizen>
42         public CoreApplication(ICoreBackend backend)
43         {
44             _backend = backend;
45         }
46
47         /// <summary>
48         /// Occurs when the application is launched.
49         /// </summary>
50         /// <since_tizen> 3 </since_tizen>
51         public event EventHandler Created;
52
53         /// <summary>
54         /// Occurs when the application is about to shutdown.
55         /// </summary>
56         /// <since_tizen> 3 </since_tizen>
57         public event EventHandler Terminated;
58
59         /// <summary>
60         /// Occurs whenever the application receives the appcontrol message.
61         /// </summary>
62         /// <since_tizen> 3 </since_tizen>
63         public event EventHandler<AppControlReceivedEventArgs> AppControlReceived;
64
65         /// <summary>
66         /// Occurs when the system memory is low.
67         /// </summary>
68         /// <since_tizen> 3 </since_tizen>
69         public event EventHandler<LowMemoryEventArgs> LowMemory;
70
71         /// <summary>
72         /// Occurs when the system battery is low.
73         /// </summary>
74         /// <since_tizen> 3 </since_tizen>
75         public event EventHandler<LowBatteryEventArgs> LowBattery;
76
77         /// <summary>
78         /// Occurs when the system language is chagned.
79         /// </summary>
80         /// <since_tizen> 3 </since_tizen>
81         public event EventHandler<LocaleChangedEventArgs> LocaleChanged;
82
83         /// <summary>
84         /// Occurs when the region format is changed.
85         /// </summary>
86         /// <since_tizen> 3 </since_tizen>
87         public event EventHandler<RegionFormatChangedEventArgs> RegionFormatChanged;
88
89         /// <summary>
90         /// Occurs when the device orientation is changed.
91         /// </summary>
92         /// <since_tizen> 3 </since_tizen>
93         public event EventHandler<DeviceOrientationEventArgs> DeviceOrientationChanged;
94
95         /// <summary>
96         /// The backend instance.
97         /// </summary>
98         /// <since_tizen> 3 </since_tizen>
99         protected ICoreBackend Backend { get { return _backend; } }
100
101         /// <summary>
102         /// Runs the application's main loop.
103         /// </summary>
104         /// <param name="args">Arguments from commandline.</param>
105         /// <since_tizen> 3 </since_tizen>
106         public override void Run(string[] args)
107         {
108             base.Run(args);
109
110             _backend.AddEventHandler(EventType.Created, OnCreate);
111             _backend.AddEventHandler(EventType.Terminated, OnTerminate);
112             _backend.AddEventHandler<AppControlReceivedEventArgs>(EventType.AppControlReceived, OnAppControlReceived);
113             _backend.AddEventHandler<LowMemoryEventArgs>(EventType.LowMemory, OnLowMemory);
114             _backend.AddEventHandler<LowBatteryEventArgs>(EventType.LowBattery, OnLowBattery);
115             _backend.AddEventHandler<LocaleChangedEventArgs>(EventType.LocaleChanged, OnLocaleChanged);
116             _backend.AddEventHandler<RegionFormatChangedEventArgs>(EventType.RegionFormatChanged, OnRegionFormatChanged);
117             _backend.AddEventHandler<DeviceOrientationEventArgs>(EventType.DeviceOrientationChanged, OnDeviceOrientationChanged);
118
119             string[] argsClone = new string[args.Length + 1];
120             if (args.Length > 1)
121             {
122                 args.CopyTo(argsClone, 1);
123             }
124             argsClone[0] = string.Empty;
125
126             _backend.Run(argsClone);
127         }
128
129         /// <summary>
130         /// Exits the main loop of the application.
131         /// </summary>
132         /// <since_tizen> 3 </since_tizen>
133         public override void Exit()
134         {
135             _backend.Exit();
136         }
137
138         /// <summary>
139         /// Overrides this method if want to handle behavior when the application is launched.
140         /// If base.OnCreated() is not called, the event 'Created' will not be emitted.
141         /// </summary>
142         /// <since_tizen> 3 </since_tizen>
143         protected virtual void OnCreate()
144         {
145             string locale = ULocale.GetDefaultLocale();
146             ChangeCurrentUICultureInfo(locale);
147             ChangeCurrentCultureInfo(locale);
148
149             Created?.Invoke(this, EventArgs.Empty);
150         }
151
152         /// <summary>
153         /// Overrides this method if want to handle behavior when the application is terminated.
154         /// If base.OnTerminate() is not called, the event 'Terminated' will not be emitted.
155         /// </summary>
156         /// <since_tizen> 3 </since_tizen>
157         protected virtual void OnTerminate()
158         {
159             Terminated?.Invoke(this, EventArgs.Empty);
160         }
161
162         /// <summary>
163         /// Overrides this method if want to handle behavior when the application receives the appcontrol message.
164         /// If base.OnAppControlReceived() is not called, the event 'AppControlReceived' will not be emitted.
165         /// </summary>
166         /// <param name="e"></param>
167         /// <since_tizen> 3 </since_tizen>
168         protected virtual void OnAppControlReceived(AppControlReceivedEventArgs e)
169         {
170             AppControlReceived?.Invoke(this, e);
171         }
172
173         /// <summary>
174         /// Overrides this method if want to handle behavior when the system memory is low.
175         /// If base.OnLowMemory() is not called, the event 'LowMemory' will not be emitted.
176         /// </summary>
177         /// <param name="e">The low memory event argument</param>
178         /// <since_tizen> 3 </since_tizen>
179         protected virtual void OnLowMemory(LowMemoryEventArgs e)
180         {
181             LowMemory?.Invoke(this, e);
182             double interval = new Random().Next(10 * 1000);
183             if (interval <= 0)
184                 interval = 10 * 1000;
185
186             sTimer = new Timer(interval);
187             sTimer.Elapsed += OnTimedEvent;
188             sTimer.AutoReset = false;
189             sTimer.Enabled = true;
190         }
191
192         private static void OnTimedEvent(Object source, ElapsedEventArgs e)
193         {
194             System.GC.Collect();
195         }
196
197         /// <summary>
198         /// Overrides this method if want to handle behavior when the system battery is low.
199         /// If base.OnLowBattery() is not called, the event 'LowBattery' will not be emitted.
200         /// </summary>
201         /// <param name="e">The low battery event argument</param>
202         /// <since_tizen> 3 </since_tizen>
203         protected virtual void OnLowBattery(LowBatteryEventArgs e)
204         {
205             LowBattery?.Invoke(this, e);
206         }
207
208         /// <summary>
209         /// Overrides this method if want to handle behavior when the system language is changed.
210         /// If base.OnLocaleChanged() is not called, the event 'LocaleChanged' will not be emitted.
211         /// </summary>
212         /// <param name="e">The locale changed event argument</param>
213         /// <since_tizen> 3 </since_tizen>
214         protected virtual void OnLocaleChanged(LocaleChangedEventArgs e)
215         {
216             ChangeCurrentUICultureInfo(e.Locale);
217             LocaleChanged?.Invoke(this, e);
218         }
219
220         /// <summary>
221         /// Overrides this method if want to handle behavior when the region format is changed.
222         /// If base.OnRegionFormatChanged() is not called, the event 'RegionFormatChanged' will not be emitted.
223         /// </summary>
224         /// <param name="e">The region format changed event argument</param>
225         /// <since_tizen> 3 </since_tizen>
226         protected virtual void OnRegionFormatChanged(RegionFormatChangedEventArgs e)
227         {
228             ChangeCurrentCultureInfo(e.Region);
229             RegionFormatChanged?.Invoke(this, e);
230         }
231
232         /// <summary>
233         /// Overrides this method if want to handle behavior when the device orientation is changed.
234         /// If base.OnRegionFormatChanged() is not called, the event 'RegionFormatChanged' will not be emitted.
235         /// </summary>
236         /// <param name="e">The device orientation changed event argument</param>
237         /// <since_tizen> 3 </since_tizen>
238         protected virtual void OnDeviceOrientationChanged(DeviceOrientationEventArgs e)
239         {
240             DeviceOrientationChanged?.Invoke(this, e);
241         }
242
243         /// <summary>
244         /// Releases any unmanaged resources used by this object. Can also dispose any other disposable objects.
245         /// </summary>
246         /// <param name="disposing">If true, disposes any disposable objects. If false, does not dispose disposable objects.</param>
247         /// <since_tizen> 3 </since_tizen>
248         protected override void Dispose(bool disposing)
249         {
250             if (!_disposedValue)
251             {
252                 if (disposing)
253                 {
254                     _backend.Dispose();
255                 }
256
257                 _disposedValue = true;
258             }
259             base.Dispose(disposing);
260         }
261
262         private CultureInfo ConvertCultureInfo(string locale)
263         {
264             ULocale pLocale = new ULocale(locale);
265
266             try
267             {
268                 return new CultureInfo(pLocale.LCID);
269             }
270             catch (ArgumentOutOfRangeException)
271             {
272                 return GetFallbackCultureInfo(pLocale);
273             }
274             catch (CultureNotFoundException)
275             {
276                 return GetFallbackCultureInfo(pLocale);
277             }
278         }
279
280         private void ChangeCurrentCultureInfo(string locale)
281         {
282             CultureInfo cultureInfo = ConvertCultureInfo(locale);
283             if (cultureInfo != null)
284             {
285                 CultureInfo.CurrentCulture = cultureInfo;
286             }
287             else
288             {
289                 Log.Error(LogTag, "CultureInfo is null. locale: " + locale);
290             }
291         }
292
293         private void ChangeCurrentUICultureInfo(string locale)
294         {
295             CultureInfo cultureInfo = ConvertCultureInfo(locale);
296             if (cultureInfo != null)
297             {
298                 CultureInfo.CurrentUICulture = cultureInfo;
299             }
300             else
301             {
302                 Log.Error(LogTag, "CultureInfo is null. locale: " + locale);
303             }
304         }
305
306         private bool ExistCultureInfo(string locale)
307         {
308             foreach (var cultureInfo in CultureInfo.GetCultures(CultureTypes.AllCultures))
309             {
310                 if (cultureInfo.Name == locale)
311                 {
312                     return true;
313                 }
314             }
315
316             return false;
317         }
318
319         private CultureInfo GetCultureInfo(string locale)
320         {
321             if (!ExistCultureInfo(locale))
322             {
323                 return null;
324             }
325
326             try
327             {
328                 return new CultureInfo(locale);
329             }
330             catch (CultureNotFoundException)
331             {
332                 return null;
333             }
334         }
335
336         private CultureInfo GetFallbackCultureInfo(ULocale uLocale)
337         {
338             CultureInfo fallbackCultureInfo = null;
339             string locale = string.Empty;
340
341             if (uLocale.Locale != null)
342             {
343                 locale = uLocale.Locale.Replace("_", "-");
344                 fallbackCultureInfo = GetCultureInfo(locale);
345             }
346
347             if (fallbackCultureInfo == null && uLocale.Language != null && uLocale.Script != null && uLocale.Country != null)
348             {
349                 locale = uLocale.Language + "-" + uLocale.Script + "-" + uLocale.Country;
350                 fallbackCultureInfo = GetCultureInfo(locale);
351             }
352
353             if (fallbackCultureInfo == null && uLocale.Language != null && uLocale.Script != null)
354             {
355                 locale = uLocale.Language + "-" + uLocale.Script;
356                 fallbackCultureInfo = GetCultureInfo(locale);
357             }
358
359             if (fallbackCultureInfo == null && uLocale.Language != null && uLocale.Country != null)
360             {
361                 locale = uLocale.Language + "-" + uLocale.Country;
362                 fallbackCultureInfo = GetCultureInfo(locale);
363             }
364
365             if (fallbackCultureInfo == null && uLocale.Language != null)
366             {
367                 locale = uLocale.Language;
368                 fallbackCultureInfo = GetCultureInfo(locale);
369             }
370
371             if (fallbackCultureInfo == null)
372             {
373                 try
374                 {
375                     fallbackCultureInfo = new CultureInfo("en");
376                 }
377                 catch (CultureNotFoundException e)
378                 {
379                     Log.Error(LogTag, "Failed to create CultureInfo. err = " + e.Message);
380                 }
381             }
382
383             return fallbackCultureInfo;
384         }
385     }
386
387     internal class ULocale
388     {
389         private const int ULOC_FULLNAME_CAPACITY = 157;
390         private const int ULOC_LANG_CAPACITY = 12;
391         private const int ULOC_SCRIPT_CAPACITY = 6;
392         private const int ULOC_COUNTRY_CAPACITY = 4;
393         private const int ULOC_VARIANT_CAPACITY = ULOC_FULLNAME_CAPACITY;
394
395         internal ULocale(string locale)
396         {
397             Locale = Canonicalize(locale);
398             Language = GetLanguage(Locale);
399             Script = GetScript(Locale);
400             Country = GetCountry(Locale);
401             Variant = GetVariant(Locale);
402             LCID = GetLCID(Locale);
403         }
404
405         internal string Locale { get; private set; }
406         internal string Language { get; private set; }
407         internal string Script { get; private set; }
408         internal string Country { get; private set; }
409         internal string Variant { get; private set; }
410         internal int LCID { get; private set; }
411
412         private string Canonicalize(string localeName)
413         {
414             // Get the locale name from ICU
415             StringBuilder sb = new StringBuilder(ULOC_FULLNAME_CAPACITY);
416             if (Interop.BaseUtilsi18n.Canonicalize(localeName, sb, sb.Capacity) <= 0)
417             {
418                 return null;
419             }
420
421             return sb.ToString();
422         }
423
424         private string GetLanguage(string locale)
425         {
426             // Get the language name from ICU
427             StringBuilder sb = new StringBuilder(ULOC_LANG_CAPACITY);
428             if (Interop.BaseUtilsi18n.GetLanguage(locale, sb, sb.Capacity, out int bufSizeLanguage) != 0)
429             {
430                 return null;
431             }
432
433             return sb.ToString();
434         }
435
436         private string GetScript(string locale)
437         {
438             // Get the script name from ICU
439             StringBuilder sb = new StringBuilder(ULOC_SCRIPT_CAPACITY);
440             if (Interop.BaseUtilsi18n.GetScript(locale, sb, sb.Capacity) <= 0)
441             {
442                 return null;
443             }
444
445             return sb.ToString();
446         }
447
448         private string GetCountry(string locale)
449         {
450             int err = 0;
451
452             // Get the country name from ICU
453             StringBuilder sb = new StringBuilder(ULOC_COUNTRY_CAPACITY);
454             if (Interop.BaseUtilsi18n.GetCountry(locale, sb, sb.Capacity, out err) <= 0)
455             {
456                 return null;
457             }
458
459             return sb.ToString();
460         }
461
462         private string GetVariant(string locale)
463         {
464             // Get the variant name from ICU
465             StringBuilder sb = new StringBuilder(ULOC_VARIANT_CAPACITY);
466             if (Interop.BaseUtilsi18n.GetVariant(locale, sb, sb.Capacity) <= 0)
467             {
468                 return null;
469             }
470
471             return sb.ToString();
472         }
473
474         private int GetLCID(string locale)
475         {
476             // Get the LCID from ICU
477             uint lcid = Interop.BaseUtilsi18n.GetLCID(locale);
478             return (int)lcid;
479         }
480
481         internal static string GetDefaultLocale()
482         {
483             IntPtr stringPtr = IntPtr.Zero;
484             if (Interop.BaseUtilsi18n.GetDefault(out stringPtr) != 0)
485             {
486                 return string.Empty;
487             }
488
489             if (stringPtr == IntPtr.Zero)
490             {
491                 return string.Empty;
492             }
493
494             return Marshal.PtrToStringAnsi(stringPtr);
495         }
496     }
497 }