Skip to content

Commit

Permalink
Merge pull request #7 from Khiro95/dev
Browse files Browse the repository at this point in the history
- Add Awqat Salaat WinUI app to support Windows 11.
- Fix a critical error in date serialization on systems that use different calendars other than Gregorian.
- Fix settings loss after the installation of Windows Updates (such as cumulative updates).
- Show Hijri date in English when Display language is not set to Arabic.
  • Loading branch information
Khiro95 authored Apr 24, 2024
2 parents 164ae72 + f0de68c commit a9ad308
Show file tree
Hide file tree
Showing 112 changed files with 2,824 additions and 265 deletions.
42 changes: 42 additions & 0 deletions AwqatSalaat.Common/AwqatSalaat.Common.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace>AwqatSalaat</RootNamespace>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Win32.Registry" Version="5.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="System.Configuration.ConfigurationManager" Version="8.0.0" />
<PackageReference Include="TimeZoneConverter" Version="6.1.0" />
</ItemGroup>

<ItemGroup>
<Compile Update="Properties\Resources.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Update="Properties\Settings.Designer.cs">
<DesignTimeSharedInput>True</DesignTimeSharedInput>
<AutoGen>True</AutoGen>
<DependentUpon>Settings.settings</DependentUpon>
</Compile>
</ItemGroup>

<ItemGroup>
<EmbeddedResource Update="Properties\Resources.resx">
<Generator>PublicResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>

<ItemGroup>
<None Update="Properties\Settings.settings">
<Generator>PublicSettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
</None>
</ItemGroup>

</Project>
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
70 changes: 70 additions & 0 deletions AwqatSalaat.Common/Helpers/HijriDateHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
using System;
using System.Globalization;

namespace AwqatSalaat.Helpers
{
public static class HijriDateHelper
{
private static readonly DateTimeFormatInfo FallbackHijriDateTimeFormatInfo;

static HijriDateHelper()
{
// Start with the Arabic culture info since it provide all features
var dateTimeFormat = new CultureInfo("ar-SA").DateTimeFormat;
// Use English names as a fallback
dateTimeFormat.MonthNames = new string[]
{
"Muharram",
"Safar",
"Rabiʻ I",
"Rabiʻ II",
"Jumada I",
"Jumada II",
"Rajab",
"Shaʻban",
"Ramadan",
"Shawwal",
"Dhuʻl-Qiʻdah",
"Dhuʻl-Hijjah",
""
};
// MonthGenitiveNames is what provide values for formatter
dateTimeFormat.MonthGenitiveNames = dateTimeFormat.MonthNames;

FallbackHijriDateTimeFormatInfo = dateTimeFormat;
}

public static string Format(DateTime dateTime, string format, string language)
{
if (string.IsNullOrEmpty(language))
{
language = CultureInfo.CurrentUICulture.TwoLetterISOLanguageName;
}

if (language.Length > 2)
{
language = language.Substring(0, 2);
}

language += "-SA";

CultureInfo culture = CultureInfo.GetCultureInfo(language);

DateTimeFormatInfo dateTimeFormat = culture.DateTimeFormat;

if (dateTimeFormat.Calendar.GetType() != typeof(UmAlQuraCalendar))
{
if (culture.TextInfo.IsRightToLeft)
{
dateTimeFormat = CultureInfo.GetCultureInfo("ar-SA").DateTimeFormat;
}
else
{
dateTimeFormat = FallbackHijriDateTimeFormatInfo;
}
}

return dateTime.ToString(format, dateTimeFormat);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,25 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;

namespace AwqatSalaat.Helpers
{
public static class LocaleManager
public class LocaleManager : INotifyPropertyChanged
{
private static readonly List<string> AvailableLocales = new List<string>() { "ar", "en" };
private static string _current;

public static string Current { get => _current; set => SetLocale(value); }
public static LocaleManager Default { get; } = new LocaleManager();

public static event EventHandler CurrentChanged;

static LocaleManager()
private string _current;

public string Current { get => _current; set => SetLocale(value); }

public event EventHandler CurrentChanged;
public event PropertyChangedEventHandler PropertyChanged;

private LocaleManager()
{
var lang = Properties.Settings.Default.DisplayLanguage;

Expand All @@ -25,9 +31,9 @@ static LocaleManager()
SetLocale(lang);
}

public static string Get(string key) => Properties.Resources.ResourceManager.GetString(key, Properties.Resources.Culture);
public string Get(string key) => Properties.Resources.ResourceManager.GetString(key, Properties.Resources.Culture);

private static void SetLocale(string locale)
private void SetLocale(string locale)
{
if (locale == _current)
{
Expand Down Expand Up @@ -59,7 +65,8 @@ private static void SetLocale(string locale)
_current = locale;
Properties.Resources.Culture = new System.Globalization.CultureInfo(locale);
Properties.Settings.Default.DisplayLanguage = locale;
CurrentChanged?.Invoke(null, EventArgs.Empty);
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Current)));
CurrentChanged?.Invoke(this, EventArgs.Empty);
}
}
}
74 changes: 74 additions & 0 deletions AwqatSalaat.Common/Helpers/ObservableObject.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Threading;

namespace AwqatSalaat.Helpers
{
// using SynchronizationContext is necessary here when this class is used with Windows App SDK
// because WinUI 3 still doesn't support dispatching PropertyChanged event to UI thread
public class ObservableObject : INotifyPropertyChanged
{
private class PropertyChangedEventState
{
public readonly ObservableObject Sender;
public readonly PropertyChangedEventArgs Args;

public PropertyChangedEventState(ObservableObject sender, PropertyChangedEventArgs args)
{
Sender = sender;
Args = args;
}
}

private event PropertyChangedEventHandler propertyChanged;
private SynchronizationContext synchronizationContext;

public event PropertyChangedEventHandler PropertyChanged
{
add
{
propertyChanged += value;

if (synchronizationContext is null && !(SynchronizationContext.Current is null))
{
if (SynchronizationContext.Current.GetType().FullName.StartsWith("Microsoft.UI"))
{
synchronizationContext = synchronizationContext ?? SynchronizationContext.Current;
}
}
}
remove
{
propertyChanged -= value;
}
}

protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var args = new PropertyChangedEventArgs(propertyName);

if (synchronizationContext is null)
{
propertyChanged?.Invoke(this, args);
}
else
{
synchronizationContext.Post((state) =>
{
PropertyChangedEventState eventState = (PropertyChangedEventState)state;
eventState.Sender.propertyChanged?.Invoke(eventState.Sender, eventState.Args);
},
new PropertyChangedEventState(this, args));
}
}

protected void SetProperty<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
{
if (!Equals(field, value))
{
field = value;
OnPropertyChanged(propertyName);
}
}
}
}
72 changes: 72 additions & 0 deletions AwqatSalaat.Common/Helpers/RelayCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
using System;
using System.Windows.Input;

namespace AwqatSalaat.Helpers
{
/// <summary>
/// A command whose sole purpose is to relay its functionality
/// to other objects by invoking delegates.
/// The default return value for the CanExecute method is 'true'.
/// RaiseCanExecuteChanged needs to be called whenever
/// CanExecute is expected to return a different value.
/// </summary>
public class RelayCommand : ICommand
{
private readonly Action<object> _execute;
private readonly Predicate<object> _canExecute;
/// <summary>
/// Raised when RaiseCanExecuteChanged is called.
/// </summary>
public event EventHandler CanExecuteChanged;
/// <summary>
/// Creates a new command that can always execute.
/// </summary>
/// <param name="execute">The execution logic.</param>
public RelayCommand(Action<object> execute)
: this(execute, null)
{
}
/// <summary>
/// Creates a new command.
/// </summary>
/// <param name="execute">The execution logic.</param>
/// <param name="canExecute">The execution status logic.</param>
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
_execute = execute ?? throw new ArgumentNullException("execute");
_canExecute = canExecute;
}
/// <summary>
/// Determines whether this RelayCommand can execute in its current state.
/// </summary>
/// <param name="parameter">
/// Data used by the command. If the command does not require data to be passed,
/// this object can be set to null.
/// </param>
/// <returns>true if this command can be executed; otherwise, false.</returns>
public bool CanExecute(object parameter)
{
return _canExecute == null || _canExecute(parameter);
}
/// <summary>
/// Executes the RelayCommand on the current command target.
/// </summary>
/// <param name="parameter">
/// Data used by the command. If the command does not require data to be passed,
/// this object can be set to null.
/// </param>
public void Execute(object parameter)
{
_execute(parameter);
}
/// <summary>
/// Method used to raise the CanExecuteChanged event
/// to indicate that the return value of the CanExecute
/// method has changed.
/// </summary>
public void RaiseCanExecuteChanged()
{
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
}
}
Loading

0 comments on commit a9ad308

Please sign in to comment.