[Applications] Set CultureInfo using LCID instread of locale (#3634)
[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.CurrentCulture = ConvertCultureInfo(locale);
283         }
284
285         private void ChangeCurrentUICultureInfo(string locale)
286         {
287             CultureInfo.CurrentUICulture = ConvertCultureInfo(locale);
288         }
289
290         private bool ExistCultureInfo(string locale)
291         {
292             foreach (var cultureInfo in CultureInfo.GetCultures(CultureTypes.AllCultures))
293             {
294                 if (cultureInfo.Name == locale)
295                 {
296                     return true;
297                 }
298             }
299
300             return false;
301         }
302
303         private CultureInfo GetCultureInfo(string locale)
304         {
305             if (!ExistCultureInfo(locale))
306             {
307                 return null;
308             }
309
310             try
311             {
312                 return new CultureInfo(locale);
313             }
314             catch (CultureNotFoundException)
315             {
316                 return null;
317             }
318         }
319
320         private CultureInfo GetFallbackCultureInfo(ULocale uLocale)
321         {
322             string locale = uLocale.Locale.Replace("_", "-");
323             CultureInfo fallbackCultureInfo = GetCultureInfo(locale);
324
325             if (fallbackCultureInfo == null && uLocale.Script != null && uLocale.Country != null)
326             {
327                 locale = uLocale.Language + "-" + uLocale.Script + "-" + uLocale.Country;
328                 fallbackCultureInfo = GetCultureInfo(locale);
329             }
330
331             if (fallbackCultureInfo == null && uLocale.Script != null)
332             {
333                 locale = uLocale.Language + "-" + uLocale.Script;
334                 fallbackCultureInfo = GetCultureInfo(locale);
335             }
336
337             if (fallbackCultureInfo == null && uLocale.Country != null)
338             {
339                 locale = uLocale.Language + "-" + uLocale.Country;
340                 fallbackCultureInfo = GetCultureInfo(locale);
341             }
342
343             if (fallbackCultureInfo == null)
344             {
345                 try
346                 {
347                     fallbackCultureInfo = new CultureInfo(uLocale.Language);
348                 }
349                 catch (CultureNotFoundException)
350                 {
351                     fallbackCultureInfo = new CultureInfo("en");
352                 }
353             }
354
355             return fallbackCultureInfo;
356         }
357     }
358
359     internal class ULocale
360     {
361         private const int ULOC_FULLNAME_CAPACITY = 157;
362         private const int ULOC_LANG_CAPACITY = 12;
363         private const int ULOC_SCRIPT_CAPACITY = 6;
364         private const int ULOC_COUNTRY_CAPACITY = 4;
365         private const int ULOC_VARIANT_CAPACITY = ULOC_FULLNAME_CAPACITY;
366
367         internal ULocale(string locale)
368         {
369             Locale = Canonicalize(locale);
370             Language = GetLanguage(Locale);
371             Script = GetScript(Locale);
372             Country = GetCountry(Locale);
373             Variant = GetVariant(Locale);
374             LCID = GetLCID(Locale);
375         }
376
377         internal string Locale { get; private set; }
378         internal string Language { get; private set; }
379         internal string Script { get; private set; }
380         internal string Country { get; private set; }
381         internal string Variant { get; private set; }
382         internal int LCID { get; private set; }
383
384         private string Canonicalize(string localeName)
385         {
386             // Get the locale name from ICU
387             StringBuilder sb = new StringBuilder(ULOC_FULLNAME_CAPACITY);
388             if (Interop.BaseUtilsi18n.Canonicalize(localeName, sb, sb.Capacity) <= 0)
389             {
390                 return null;
391             }
392
393             return sb.ToString();
394         }
395
396         private string GetLanguage(string locale)
397         {
398             // Get the language name from ICU
399             StringBuilder sb = new StringBuilder(ULOC_LANG_CAPACITY);
400             if (Interop.BaseUtilsi18n.GetLanguage(locale, sb, sb.Capacity, out int bufSizeLanguage) != 0)
401             {
402                 return null;
403             }
404
405             return sb.ToString();
406         }
407
408         private string GetScript(string locale)
409         {
410             // Get the script name from ICU
411             StringBuilder sb = new StringBuilder(ULOC_SCRIPT_CAPACITY);
412             if (Interop.BaseUtilsi18n.GetScript(locale, sb, sb.Capacity) <= 0)
413             {
414                 return null;
415             }
416
417             return sb.ToString();
418         }
419
420         private string GetCountry(string locale)
421         {
422             int err = 0;
423
424             // Get the country name from ICU
425             StringBuilder sb = new StringBuilder(ULOC_COUNTRY_CAPACITY);
426             if (Interop.BaseUtilsi18n.GetCountry(locale, sb, sb.Capacity, out err) <= 0)
427             {
428                 return null;
429             }
430
431             return sb.ToString();
432         }
433
434         private string GetVariant(string locale)
435         {
436             // Get the variant name from ICU
437             StringBuilder sb = new StringBuilder(ULOC_VARIANT_CAPACITY);
438             if (Interop.BaseUtilsi18n.GetVariant(locale, sb, sb.Capacity) <= 0)
439             {
440                 return null;
441             }
442
443             return sb.ToString();
444         }
445
446         private int GetLCID(string locale)
447         {
448             // Get the LCID from ICU
449             uint lcid = Interop.BaseUtilsi18n.GetLCID(locale);
450             return (int)lcid;
451         }
452
453         internal static string GetDefaultLocale()
454         {
455             IntPtr stringPtr = IntPtr.Zero;
456             if (Interop.BaseUtilsi18n.GetDefault(out stringPtr) != 0)
457             {
458                 return string.Empty;
459             }
460
461             if (stringPtr == IntPtr.Zero)
462             {
463                 return string.Empty;
464             }
465
466             return Marshal.PtrToStringAnsi(stringPtr);
467         }
468     }
469 }