Enable converters

This commit is contained in:
Dr.Rx 2019-05-22 11:33:30 -04:00
commit 93fdfe02c6
38 changed files with 7255 additions and 957 deletions

View file

@ -20,6 +20,7 @@
<AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
<AndroidUseIntermediateDesignerFile>True</AndroidUseIntermediateDesignerFile>
<ResourcesDirectory>..\Calculator.Shared\Strings</ResourcesDirectory>
<LangVersion>7.3</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>

View file

@ -19,6 +19,21 @@ namespace CalculatorApp
m_vector = new List<TType>(source.m_vector);
}
public TType this[int index]
{
get => m_vector[index];
}
public TType this[uint index]
{
get => m_vector[(int)index];
}
public TType At(int index)
{
return m_vector[index];
}
public bool GetAt(int index, out TType item)
{
item = m_vector[index];
@ -70,12 +85,27 @@ namespace CalculatorApp
return true;
}
public void Add(TType item)
{
m_vector.Add(item);
}
public bool Append(TType item)
{
m_vector.Add(item);
return true;
}
public void EmplaceBack(TType item)
{
m_vector.Add(item);
}
public void PushBack(TType item)
{
m_vector.Add(item);
}
public bool RemoveAtEnd()
{
m_vector.RemoveAt(m_vector.Count - 1);
@ -88,6 +118,28 @@ namespace CalculatorApp
return true;
}
public bool IsEmpty()
{
return m_vector.Count == 0;
}
public uint Size()
{
return (uint)m_vector.Count;
}
public void Sort(Func<TType, TType, bool> comparison)
{
m_vector.Sort((t1, t2) => comparison(t1, t2) ? -1 : 1);
}
public int IndexOf(TType item)
{
return m_vector.IndexOf(item);
}
public int Count => m_vector.Count;
public bool GetString(out string expression)
{
// UNO TODO

View file

@ -0,0 +1,19 @@
using System;
using System.Collections.ObjectModel;
using System.Linq;
namespace CalculatorApp
{
public class CalculatorObservableCollection<TType> : ObservableCollection<TType>
{
public void Append(TType item)
{
Add(item);
}
public TType GetAt(int index)
{
return this[index];
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -22,6 +22,7 @@
<Compile Include="$(MSBuildThisFileDirectory)CalcManager\CalculatorManager.Interop.cs" />
<Compile Include="$(MSBuildThisFileDirectory)CalcManager\CalculatorManager.cs" />
<Compile Include="$(MSBuildThisFileDirectory)CalcManager\CalculatorMode.cs" />
<Compile Include="$(MSBuildThisFileDirectory)CalcManager\CalculatorObservableCollection.cs" />
<Compile Include="$(MSBuildThisFileDirectory)CalcManager\CCalcEngine.cs" />
<Compile Include="$(MSBuildThisFileDirectory)CalcManager\CCommand.cs" />
<Compile Include="$(MSBuildThisFileDirectory)CalcManager\Command.cs" />
@ -34,10 +35,12 @@
<Compile Include="$(MSBuildThisFileDirectory)Common\AppLifecycleLogger.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Common\AppResourceProvider.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Common\Automation\NarratorAnnouncement.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Common\Automation\NarratorNotifier.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Common\BindableBase.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Common\CalculatorButtonPressedEventArgs.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Common\CalculatorButtonUser.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Common\CalculatorDisplay.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Common\ConversionResultTaskHelper.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Common\CopyPasteManager.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Common\DateCalculator.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Common\DelegateCommand.cs" />
@ -48,13 +51,16 @@
<Compile Include="$(MSBuildThisFileDirectory)Common\LocalizationStringUtil.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Common\MyVirtualKey.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Common\NavCategory.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Common\NetworkManager.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Common\TitleBarHelper.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Common\TraceLogger.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Common\Utils.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Common\ValidSelectedItemConverter.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Controls\CalculationResult.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Controls\CalculationResultAutomationPeer.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Controls\CalculatorButton.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Controls\FlipButtons.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Controls\HorizontalNoOverflowStackPanel.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Controls\KeyboardShortcutManager.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Controls\OperandTextBox.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Controls\OperatorTextBox.cs" />
@ -72,6 +78,11 @@
<Compile Include="$(MSBuildThisFileDirectory)Converters\ItemSizeToVisibilityConverter.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Converters\RadixToStringConverter.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Converters\VisibilityNegationConverter.cs" />
<Compile Include="$(MSBuildThisFileDirectory)DataLoaders\CurrencyDataLoader.cs" />
<Compile Include="$(MSBuildThisFileDirectory)DataLoaders\CurrencyHttpClient.cs" />
<Compile Include="$(MSBuildThisFileDirectory)DataLoaders\ICurrencyHttpClient.cs" />
<Compile Include="$(MSBuildThisFileDirectory)DataLoaders\UnitConverterDataConstants.cs" />
<Compile Include="$(MSBuildThisFileDirectory)DataLoaders\UnitConverterDataLoader.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ViewModels\ApplicationViewModel.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ViewModels\DateCalculatorViewModel.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ViewModels\HistoryItemViewModel.cs" />
@ -138,6 +149,7 @@
<Compile Include="$(MSBuildThisFileDirectory)Views\UnitConverter.xaml.cs">
<DependentUpon>UnitConverter.xaml</DependentUpon>
</Compile>
<Compile Include="$(MSBuildThisFileDirectory)_adapters\StringExtensions.cs" />
</ItemGroup>
<ItemGroup>
<Page Include="$(MSBuildThisFileDirectory)Styles.xaml">

View file

@ -1,4 +1,5 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using Windows.Foundation;
@ -8,5 +9,9 @@ namespace WindowsCalculator.Shared.Common
{
class AlwaysSelectedCollectionView
{
public AlwaysSelectedCollectionView(IEnumerable result)
{
}
}
}

View file

@ -0,0 +1,62 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using System.Text;
using Windows.UI.Xaml;
namespace CalculatorApp.Common.Automation
{
public sealed partial class NarratorNotifier : DependencyObject
{
public static DependencyProperty AnnouncementProperty { get; } = DependencyProperty.RegisterAttached(
"Announcement",
typeof(NarratorAnnouncement),
typeof(NarratorNotifier),
new PropertyMetadata(default(NarratorAnnouncement), OnAnnouncementChanged));
public static NarratorAnnouncement GetAnnouncement(Windows.UI.Xaml.DependencyObject element)
{
return element.GetValue(AnnouncementProperty) as NarratorAnnouncement;
}
public static void SetAnnouncement(Windows.UI.Xaml.DependencyObject element, NarratorAnnouncement value)
{
element.SetValue(AnnouncementProperty, value);
}
public NarratorAnnouncement Announcement
{
get { return GetAnnouncement(this); }
set { SetAnnouncement(this, value); }
}
//TODO UNO INarratorAnnouncementHost m_announcementHost;
public NarratorNotifier()
{
// TODO UNO
// m_announcementHost = NarratorAnnouncementHostFactory.MakeHost();
}
public void Announce(NarratorAnnouncement announcement)
{
// TODO UNO:
//if (NarratorAnnouncement.IsValid(announcement) && m_announcementHost != null)
//{
// m_announcementHost.Announce(announcement);
//}
}
static void OnAnnouncementChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
var instance = dependencyObject as NarratorNotifier;
if (instance != null)
{
instance.Announce((NarratorAnnouncement)(e.NewValue));
}
}
}
}

View file

@ -0,0 +1,44 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace CalculatorApp.Common
{
class ConversionResultTaskHelper
{
uint m_delay;
CancellationTokenSource m_cts = new CancellationTokenSource();
Action m_storedFunction;
public ConversionResultTaskHelper(uint delay, Action functionToRun)
{
m_delay = delay;
m_storedFunction = functionToRun;
var delayTask = CompleteAfter(delay);
}
~ConversionResultTaskHelper()
{
m_cts.Cancel();
}
// Creates a task that completes after the specified delay.
//
// Taken from: How to: Create a Task that Completes After a Delay
// https://msdn.microsoft.com/en-us/library/hh873170.aspx
async Task CompleteAfter(uint timeout)
{
await Task.Delay(TimeSpan.FromMilliseconds(timeout));
if (!m_cts.Token.IsCancellationRequested)
{
m_storedFunction();
}
}
}
}

View file

@ -28,7 +28,7 @@ namespace CalculatorApp.Common
public sealed class LocalizationService
{
static string DefaultCurrencyCode = "USD";
internal static string DefaultCurrencyCode = "USD";
@ -167,7 +167,7 @@ namespace CalculatorApp.Common
}
}
FlowDirection GetFlowDirection()
public FlowDirection GetFlowDirection()
{
return m_flowDirection;
}

View file

@ -335,7 +335,7 @@ namespace CalculatorApp
return m_currencyTrailingDigits;
}
int GetCurrencySymbolPrecedence()
public int GetCurrencySymbolPrecedence()
{
return m_currencySymbolPrecedence;
}

View file

@ -140,7 +140,7 @@ namespace CalculatorApp
public string AccessKey => m_accessKey;
bool SupportsNegative
public bool SupportsNegative
{
get
{
@ -531,13 +531,13 @@ namespace CalculatorApp
public CategoryGroupType GroupType { get => m_GroupType; private set { m_GroupType = value; RaisePropertyChanged("GroupType"); } }
private ObservableCollection<NavCategory> m_Categories;
public ObservableCollection<NavCategory> Categories { get => m_Categories; private set { m_Categories = value; RaisePropertyChanged("Categories"); } }
private CalculatorObservableCollection<NavCategory> m_Categories;
public CalculatorObservableCollection<NavCategory> Categories { get => m_Categories; private set { m_Categories = value; RaisePropertyChanged("Categories"); } }
internal NavCategoryGroup(NavCategoryGroupInitializer groupInitializer)
{
m_Categories = new ObservableCollection<NavCategory>();
m_Categories = new CalculatorObservableCollection<NavCategory>();
m_GroupType = groupInitializer.type;
var resProvider = AppResourceProvider.GetInstance();
@ -573,9 +573,9 @@ namespace CalculatorApp
}
}
public static ObservableCollection<NavCategoryGroup> CreateMenuOptions()
public static CalculatorObservableCollection<NavCategoryGroup> CreateMenuOptions()
{
var menuOptions = new ObservableCollection<NavCategoryGroup>();
var menuOptions = new CalculatorObservableCollection<NavCategoryGroup>();
menuOptions.Add(CreateCalculatorCategory());
menuOptions.Add(CreateConverterCategory());
return menuOptions;
@ -586,7 +586,7 @@ namespace CalculatorApp
return new NavCategoryGroup(NavCategory.s_categoryGroupManifest[0].Value);
}
static NavCategoryGroup CreateConverterCategory()
public static NavCategoryGroup CreateConverterCategory()
{
return new NavCategoryGroup(NavCategory.s_categoryGroupManifest[1].Value);
}

View file

@ -0,0 +1,68 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using System.Text;
using Windows.Networking.Connectivity;
namespace CalculatorApp
{
public enum NetworkAccessBehavior
{
Normal = 0,
OptIn = 1,
Offline = 2
};
public delegate void NetworkBehaviorChangedHandler(NetworkAccessBehavior behavior);
public sealed class NetworkManager
{
public event NetworkBehaviorChangedHandler NetworkBehaviorChanged;
public NetworkManager()
{
NetworkInformation.NetworkStatusChanged += new NetworkStatusChangedEventHandler(OnNetworkStatusChange);
}
~NetworkManager()
{
NetworkInformation.NetworkStatusChanged -= OnNetworkStatusChange;
}
public static NetworkAccessBehavior GetNetworkAccessBehavior()
{
NetworkAccessBehavior behavior = NetworkAccessBehavior.Offline;
ConnectionProfile connectionProfile = NetworkInformation.GetInternetConnectionProfile();
if (connectionProfile != null)
{
NetworkConnectivityLevel connectivityLevel = connectionProfile.GetNetworkConnectivityLevel();
if (connectivityLevel == NetworkConnectivityLevel.InternetAccess || connectivityLevel == NetworkConnectivityLevel.ConstrainedInternetAccess)
{
ConnectionCost connectionCost = connectionProfile.GetConnectionCost();
behavior = ConvertCostInfoToBehavior(connectionCost);
}
}
return behavior;
}
void OnNetworkStatusChange(object sender)
{
NetworkBehaviorChanged?.Invoke(GetNetworkAccessBehavior());
}
// See app behavior guidelines at https://msdn.microsoft.com/en-us/library/windows/apps/xaml/jj835821(v=win.10).aspx
static NetworkAccessBehavior ConvertCostInfoToBehavior(ConnectionCost connectionCost)
{
if (connectionCost.Roaming || connectionCost.OverDataLimit || connectionCost.NetworkCostType == NetworkCostType.Variable
|| connectionCost.NetworkCostType == NetworkCostType.Fixed)
{
return NetworkAccessBehavior.OptIn;
}
return NetworkAccessBehavior.Normal;
}
}
}

View file

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Text;
using Windows.Globalization;
using CalculatorApp.Common;
@ -63,7 +64,7 @@ namespace CalculatorApp
public void LogWindowActivated() {}
public void LogWindowLaunched() {}
public void LogUserRequestedRefreshFailed() {}
//public void LogConversionResult(std::wstring_view fromValue, std::wstring_view fromUnit, std::wstring_view toValue, std::wstring_view toUnit) {}
public void LogConversionResult(string fromValue, string fromUnit, string toValue, string toUnit) {}
public void LogAboutFlyoutOpened() {}
public void LogNavBarOpened() {}
public void LogViewClosingTelemetry(int i) {}
@ -74,8 +75,8 @@ namespace CalculatorApp
public void LogDateAddSubtractModeUsed(int windowId, bool isAddMode) {}
public void LogDateClippedTimeDifferenceFound(Calendar today, DateTime clippedTime) {}
//public void LogStandardException(std::wstring_view functionName, _In_ const std::exception& e) {}
//public void LogWinRTException(std::wstring_view functionName, _In_ winrt::hresult_error const& e) {}
//public void LogPlatformException(std::wstring_view functionName, _In_ Platform::Exception ^ e) {}
public void LogStandardException(Exception e, [CallerMemberName] string functionName = null) { }
public void LogWinRTException(Exception e, [CallerMemberName] string functionName = null) { }
public void LogPlatformException(Exception e, [CallerMemberName] string functionName = null) {}
}
}

View file

@ -1,7 +1,10 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.Storage;
using Windows.UI.Core;
using Windows.UI.ViewManagement;
@ -31,5 +34,47 @@ namespace CalculatorApp
{
return !string.IsNullOrEmpty(input) && input.Last() == target;
}
public static void Swap<T>(ref T field1, ref T field2)
{
var tmp = field1;
field1 = field2;
field2 = tmp;
}
public static bool IsDateTimeOlderThan(DateTime dateTime, long duration)
{
return dateTime + TimeSpan.FromTicks(duration) < DateTime.Now;
}
public static async Task<string> ReadFileFromFolder(StorageFolder folder, string filename)
{
if (folder == null)
{
return null;
}
var filePath = Path.Combine(folder.Path, filename);
using (var reader = new StreamReader(filePath))
{
return await reader.ReadToEndAsync();
}
}
public static async Task WriteFileToFolder(StorageFolder folder, string filename, string contents, CreationCollisionOption colisionOption)
{
if (folder== null)
{
return;
}
var filePath = Path.Combine(folder.Path, filename);
using (var writer = new StreamWriter(filePath, append: false))
{
await writer.WriteAsync(contents);
}
}
public static DateTime GetUniversalSystemTime() => DateTime.UtcNow;
}
}

View file

@ -0,0 +1,37 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using System.Text;
using Windows.UI.Xaml.Data;
namespace CalculatorApp.Common
{
public sealed class ValidSelectedItemConverter : IValueConverter
{
public ValidSelectedItemConverter()
{
}
public object Convert(object value, Type targetType, object parameter, string language)
{
// Pass through as we don't want to change the value from the source
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
if (value != null)
{
if (value is int i && i > 0)
{
return value;
}
}
// Stop the binding if the object is null
return Windows.UI.Xaml.DependencyProperty.UnsetValue;
}
}
}

View file

@ -10,6 +10,7 @@ using Windows.UI.Xaml.Automation.Peers;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using CalculatorApp.ViewModel;
namespace CalculatorApp
{
@ -17,7 +18,7 @@ namespace CalculatorApp
{
public delegate void SelectedEventHandler(object sender);
public sealed partial class CalculationResult : Windows.UI.Xaml.Controls.Control
public sealed partial class CalculationResult : Windows.UI.Xaml.Controls.Control, IActivatable
{
public Visibility ExpressionVisibility
{
@ -130,7 +131,7 @@ namespace CalculatorApp
public static readonly DependencyProperty IsOperatorCommandProperty =
DependencyProperty.Register("IsOperatorCommand", typeof(bool), typeof(CalculationResult), new PropertyMetadata(false));
event SelectedEventHandler Selected;
public event SelectedEventHandler Selected;
private Windows.UI.Xaml.Controls.ScrollViewer m_textContainer;
@ -163,7 +164,7 @@ namespace CalculatorApp
m_haveCalculatedMax = false;
}
string GetRawDisplayValue()
public string GetRawDisplayValue()
{
string rawValue = null;
@ -298,7 +299,7 @@ namespace CalculatorApp
}
}
void UpdateTextState()
public void UpdateTextState()
{
if ((m_textContainer == null) || (m_textBlock == null))
{

View file

@ -0,0 +1,73 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Windows.Foundation;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
namespace CalculatorApp.Controls
{
public partial class HorizontalNoOverflowStackPanel : Panel
{
// TODO UNO: DEPENDENCY_PROPERTY_OWNER(HorizontalNoOverflowStackPanel);
protected override Size MeasureOverride(Size availableSize)
{
double maxHeight = 0;
double width = 0;
foreach (UIElement child in Children)
{
child.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
maxHeight = Math.Max(maxHeight, child.DesiredSize.Height);
width += child.DesiredSize.Width;
}
return new Size(Math.Min(width, availableSize.Width), Math.Min(availableSize.Height, maxHeight));
}
protected virtual bool ShouldPrioritizeLastItem()
{
return false;
}
protected override Size ArrangeOverride(Size finalSize)
{
if (Children.Count == 0)
{
return finalSize;
}
double posX = 0;
var lastChild = (UIElement)Children.Last();
double lastChildWidth = 0;
if (Children.Count > 2 && ShouldPrioritizeLastItem())
{
lastChildWidth = lastChild.DesiredSize.Width;
}
foreach (UIElement item in Children)
{
var widthAvailable = finalSize.Width - posX;
if (item != lastChild)
{
widthAvailable -= lastChildWidth;
}
double itemWidth = item.DesiredSize.Width;
if (widthAvailable > 0 && itemWidth <= widthAvailable)
{
// stack the items horizontally (left to right)
item.Arrange(new Rect(posX, 0, itemWidth, finalSize.Height));
posX += item.RenderSize.Width;
}
else
{
// Not display the item
item.Arrange(new Rect(0, 0, 0, 0));
}
}
return finalSize;
}
};
}

View file

@ -1,3 +1,6 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using System.Text;
@ -5,6 +8,7 @@ using Windows.UI.Xaml;
using Windows.UI.Xaml.Automation;
using Windows.UI.Xaml.Automation.Peers;
using Windows.UI.Xaml.Controls;
using CalculatorApp.ViewModel;
namespace CalculatorApp.Controls
{
@ -20,12 +24,11 @@ namespace CalculatorApp.Controls
{
base.PrepareContainerForItemOverride(element, item);
// UNO TODO
//var supplementaryResult = (SupplementaryResult)(item);
//if (supplementaryResult)
//{
// AutomationProperties.SetName(element, supplementaryResult.GetLocalizedAutomationName());
//}
var supplementaryResult = (SupplementaryResult)(item);
if (supplementaryResult != null)
{
AutomationProperties.SetName(element, supplementaryResult.GetLocalizedAutomationName());
}
}
}

View file

@ -1,39 +1,44 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using WindowsCalculator.Shared.Common;
namespace CalculatorApp
{
namespace Common
{
//class AlwaysSelectedCollectionViewConverter : Windows.UI.Xaml.Data.IValueConverter
//{
// public AlwaysSelectedCollectionViewConverter()
// {
// }
class AlwaysSelectedCollectionViewConverter : Windows.UI.Xaml.Data.IValueConverter
{
public AlwaysSelectedCollectionViewConverter()
{
}
// object Convert(
// object value,
// Type targetType,
// object parameter,
// string language)
// {
// var result = (IEnumerable)(value);
// if (result)
// {
// return new AlwaysSelectedCollectionView(result);
// }
// return Windows.UI.Xaml.DependencyProperty.UnsetValue; // Can't convert
// }
public object Convert(
object value,
Type targetType,
object parameter,
string language)
{
var result = value as IEnumerable;
if (result != null)
{
return new AlwaysSelectedCollectionView(result);
}
return Windows.UI.Xaml.DependencyProperty.UnsetValue; // Can't convert
}
// object ConvertBack(
// object value,
// Type targetType,
// object parameter,
// string language)
// {
// return Windows.UI.Xaml.DependencyProperty.UnsetValue;
// }
//}
public object ConvertBack(
object value,
Type targetType,
object parameter,
string language)
{
return Windows.UI.Xaml.DependencyProperty.UnsetValue;
}
}
}
}

View file

@ -1,4 +1,7 @@
using System;
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using System.Text;
using Windows.UI.Xaml.Interop;
@ -6,7 +9,7 @@ using Windows.Foundation.Metadata;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml;
namespace CalculatorApp.Converters
namespace CalculatorApp.Common
{
[WebHostHidden]
public sealed class VisibilityNegationConverter : IValueConverter

View file

@ -0,0 +1,792 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Windows.Data.Json;
using Windows.Foundation.Collections;
using Windows.Globalization.DateTimeFormatting;
using Windows.Storage;
using Windows.System.UserProfile;
using Windows.UI.Core;
using CalculatorApp.Common;
using CalculatorApp.DataLoaders;
using UCM = UnitConversionManager;
using CurrencyRatioMap = System.Collections.Generic.Dictionary<string, UnitConversionManager.CurrencyRatio>;
using SelectedUnits = System.Collections.Generic.KeyValuePair<string, string>;
using CategorySelectionInitializer = System.Tuple<CalculatorApp.CalculatorList<UnitConversionManager.Unit>, UnitConversionManager.Unit, UnitConversionManager.Unit>;
using UnitToUnitToConversionDataMap = System.Collections.Generic.Dictionary<UnitConversionManager.Unit, System.Collections.Generic.Dictionary<UnitConversionManager.Unit, UnitConversionManager.ConversionData>>;
using CategoryToUnitVectorMap = System.Collections.Generic.Dictionary<UnitConversionManager.Category, CalculatorApp.CalculatorList<UnitConversionManager.Unit>>;
namespace CalculatorApp.ViewModel
{
public enum CurrencyLoadStatus
{
NotLoaded = 0,
FailedToLoad = 1,
LoadedFromCache = 2,
LoadedFromWeb = 3
}
public static class UnitConverterResourceKeys
{
public const string CurrencyUnitFromKey = CurrencyDataLoader.CURRENCY_UNIT_FROM_KEY;
public const string CurrencyUnitToKey = CurrencyDataLoader.CURRENCY_UNIT_TO_KEY;
}
public static class CurrencyDataLoaderConstants
{
public const string CacheTimestampKey = CurrencyDataLoader.CACHE_TIMESTAMP_KEY;
public const string CacheLangcodeKey = CurrencyDataLoader.CACHE_LANGCODE_KEY;
public const string CacheDelimiter = CurrencyDataLoader.CACHE_DELIMITER;
public const string StaticDataFilename = CurrencyDataLoader.STATIC_DATA_FILENAME;
public const string AllRatiosDataFilename = CurrencyDataLoader.ALL_RATIOS_DATA_FILENAME;
public const long DayDuration = CurrencyDataLoader.DAY_DURATION;
}
struct CurrencyUnitMetadata
{
public CurrencyUnitMetadata(string s)
{
symbol = s;
}
public string symbol;
};
public class CurrencyDataLoader : UCM.IConverterDataLoader, UCM.ICurrencyConverterDataLoader
{
//private:
string m_responseLanguage;
CalculatorApp.DataLoaders.ICurrencyHttpClient m_client;
bool m_isRtlLanguage;
//Mutex m_currencyUnitsMutex;
object m_currencyUnitsMutex = new object();
CalculatorList<UCM.Unit> m_currencyUnits = new CalculatorList<UCM.Unit>();
UnitToUnitToConversionDataMap m_currencyRatioMap = new UnitToUnitToConversionDataMap(EqualityComparer<UCM.Unit>.Default);
Dictionary<UCM.Unit, CurrencyUnitMetadata> m_currencyMetadata = new Dictionary<UCM.Unit, CurrencyUnitMetadata>(EqualityComparer<UCM.Unit>.Default);
UCM.IViewModelCurrencyCallback m_vmCallback;
Windows.Globalization.NumberFormatting.DecimalFormatter m_ratioFormatter;
string m_ratioFormat;
DateTime m_cacheTimestamp;
string m_timestampFormat;
CurrencyLoadStatus m_loadStatus;
CalculatorApp.NetworkManager m_networkManager;
CalculatorApp.NetworkAccessBehavior m_networkAccessBehavior;
//Windows.Foundation.EventRegistrationToken m_networkBehaviorToken;
bool m_meteredOverrideSet;
internal const string CURRENCY_UNIT_FROM_KEY = "CURRENCY_UNIT_FROM_KEY";
internal const string CURRENCY_UNIT_TO_KEY = "CURRENCY_UNIT_TO_KEY";
// Calculate number of 100-nanosecond intervals-per-day
// (1 interval/100 nanosecond)(100 nanosecond/1e-7 s)(60 s/1 min)(60 min/1 hr)(24 hr/1 day) = (interval/day)
internal const long DAY_DURATION = 1L * 60 * 60 * 24 * 10000000;
internal const long WEEK_DURATION = DAY_DURATION * 7;
static int FORMATTER_DIGIT_COUNT = 4;
internal const string CACHE_TIMESTAMP_KEY = "CURRENCY_CONVERTER_TIMESTAMP";
internal const string CACHE_LANGCODE_KEY = "CURRENCY_CONVERTER_LANGCODE";
internal const string CACHE_DELIMITER = "%";
internal const string STATIC_DATA_FILENAME = "CURRENCY_CONVERTER_STATIC_DATA.txt";
private static string[] STATIC_DATA_PROPERTIES =
{
"CountryCode",
"CountryName",
"CurrencyCode",
"CurrencyName",
"CurrencySymbol"
};
internal const string ALL_RATIOS_DATA_FILENAME = "CURRENCY_CONVERTER_ALL_RATIOS_DATA.txt";
internal const string RATIO_KEY = "Rt";
internal const string CURRENCY_CODE_KEY = "An";
internal static string[] ALL_RATIOS_DATA_PROPERTIES = {RATIO_KEY, CURRENCY_CODE_KEY};
static string DEFAULT_FROM_TO_CURRENCY_FILE_URI = "ms-appx:///DataLoaders/DefaultFromToCurrency.json";
static string FROM_KEY = "from";
static string TO_KEY = "to";
// Fallback default values.
static string DEFAULT_FROM_CURRENCY = DefaultCurrencyCode;
static string DEFAULT_TO_CURRENCY = "EUR";
private static string DefaultCurrencyCode = LocalizationService.DefaultCurrencyCode;
public CurrencyDataLoader(ICurrencyHttpClient client)
{
this.m_client = client;
;
this.m_loadStatus = CurrencyLoadStatus.NotLoaded;
this.m_responseLanguage = "en-US";
this.m_ratioFormat = "";
this.m_timestampFormat = "";
this.m_networkManager = new NetworkManager();
this.m_meteredOverrideSet = false;
if (GlobalizationPreferences.Languages.Count > 0)
{
m_responseLanguage = GlobalizationPreferences.Languages[0];
}
if (m_client != null)
{
m_client.SetSourceCurrencyCode(DefaultCurrencyCode);
m_client.SetResponseLanguage(m_responseLanguage);
}
if (CoreWindow.GetForCurrentThread() != null)
{
// Must have a CoreWindow to access the resource context.
m_isRtlLanguage = LocalizationService.GetInstance().IsRtlLayout();
}
m_ratioFormatter = LocalizationService.GetRegionalSettingsAwareDecimalFormatter();
m_ratioFormatter.IsGrouped = true;
m_ratioFormatter.IsDecimalPointAlwaysDisplayed = true;
m_ratioFormatter.FractionDigits = FORMATTER_DIGIT_COUNT;
m_ratioFormat = AppResourceProvider.GetInstance().GetResourceString("CurrencyFromToRatioFormat");
m_timestampFormat = AppResourceProvider.GetInstance().GetResourceString("CurrencyTimestampFormat");
}
~CurrencyDataLoader()
{
UnregisterForNetworkBehaviorChanges();
}
void UnregisterForNetworkBehaviorChanges()
{
m_networkManager.NetworkBehaviorChanged -= OnNetworkBehaviorChanged;
}
void RegisterForNetworkBehaviorChanges()
{
UnregisterForNetworkBehaviorChanges();
m_networkManager.NetworkBehaviorChanged += new NetworkBehaviorChangedHandler(OnNetworkBehaviorChanged);
OnNetworkBehaviorChanged(NetworkManager.GetNetworkAccessBehavior());
}
void OnNetworkBehaviorChanged(NetworkAccessBehavior newBehavior)
{
m_networkAccessBehavior = newBehavior;
if (m_vmCallback != null)
{
m_vmCallback.NetworkBehaviorChanged((int)(m_networkAccessBehavior));
}
}
bool LoadFinished()
{
return m_loadStatus != CurrencyLoadStatus.NotLoaded;
}
bool LoadedFromCache()
{
return m_loadStatus == CurrencyLoadStatus.LoadedFromCache;
}
bool LoadedFromWeb()
{
return m_loadStatus == CurrencyLoadStatus.LoadedFromWeb;
}
void ResetLoadStatus()
{
m_loadStatus = CurrencyLoadStatus.NotLoaded;
}
public async void LoadData()
{
RegisterForNetworkBehaviorChanges();
if (!LoadFinished())
{
var loadFunctions = new Func<Task<bool>>[]
{
TryLoadDataFromCacheAsync,
TryLoadDataFromWebAsync
};
bool didLoad = false;
foreach (var f in loadFunctions)
{
didLoad = await f();
if (didLoad)
{
break;
}
}
UpdateDisplayedTimestamp();
NotifyDataLoadFinished(didLoad);
}
}
public CalculatorList<UCM.Category> LoadOrderedCategories()
{
// This function should not be called
// The model will use the categories from UnitConverterDataLoader
return new CalculatorList<UCM.Category>();
}
public CalculatorList<UCM.Unit> LoadOrderedUnits(UCM.Category category)
{
lock (m_currencyUnitsMutex)
{
return m_currencyUnits;
}
}
public Dictionary<UCM.Unit, UCM.ConversionData> LoadOrderedRatios(UCM.Unit unit)
{
lock (m_currencyUnitsMutex) ;
{
return m_currencyRatioMap[unit];
}
}
public bool SupportsCategory(UCM.Category target)
{
int currencyId = NavCategory.Serialize(ViewMode.Currency);
return target.id == currencyId;
}
public void SetViewModelCallback(UCM.IViewModelCurrencyCallback callback)
{
m_vmCallback = callback;
OnNetworkBehaviorChanged(m_networkAccessBehavior);
}
public KeyValuePair<string, string> GetCurrencySymbols(UCM.Unit unit1, UCM.Unit unit2)
{
lock (m_currencyUnitsMutex)
{
string symbol1 = "";
string symbol2 = "";
if (m_currencyMetadata.TryGetValue(unit1, out var itr1) && m_currencyMetadata.TryGetValue(unit2, out var itr2))
{
symbol1 = itr1.symbol;
symbol2 = itr2.symbol;
}
return new KeyValuePair<string, string>(symbol1, symbol2);
}
}
public KeyValuePair<string, string> GetCurrencyRatioEquality(UCM.Unit unit1, UCM.Unit unit2)
{
try
{
if (m_currencyRatioMap.TryGetValue(unit1, out var ratioMap))
{
if (ratioMap.TryGetValue(unit2, out var iter2))
{
var ratio = iter2.ratio;
// Round the ratio to FORMATTER_DIGIT_COUNT digits using int math.
// Ex: to round 1.23456 to three digits, use
// ((int) 1.23456 * (10^3)) / (10^3)
double scale = Math.Pow(10, FORMATTER_DIGIT_COUNT);
double rounded = (int)(ratio * (int)(scale)) / scale;
string digitSymbol = LocalizationSettings.GetInstance().GetDigitSymbolFromEnUsDigit('1').ToString();
string roundedFormat = m_ratioFormatter.Format(rounded);
string ratioString = LocalizationStringUtil.GetLocalizedString(
m_ratioFormat,
digitSymbol,
unit1.abbreviation,
roundedFormat,
unit2.abbreviation);
string accessibleRatioString = LocalizationStringUtil.GetLocalizedString(
m_ratioFormat,
digitSymbol,
unit1.accessibleName,
roundedFormat,
unit2.accessibleName);
return new KeyValuePair<string, string>(ratioString, accessibleRatioString);
}
}
}
catch
{
}
return new KeyValuePair<string, string>("", "");
}
#pragma optimize("", off) // Turn off optimizations to work around DevDiv 393321
public async Task<bool> TryLoadDataFromCacheAsync()
{
try
{
ResetLoadStatus();
var localSettings = ApplicationData.Current.LocalSettings;
if (localSettings == null || !localSettings.Values.ContainsKey(CurrencyDataLoaderConstants.CacheTimestampKey))
{
return false;
}
bool loadComplete = false;
m_cacheTimestamp = DateTime.Parse((string)localSettings.Values[CurrencyDataLoaderConstants.CacheTimestampKey]);
if (Utils.IsDateTimeOlderThan(m_cacheTimestamp, DAY_DURATION) && m_networkAccessBehavior == NetworkAccessBehavior.Normal)
{
loadComplete = await TryLoadDataFromWebAsync();
}
if (!loadComplete)
{
loadComplete = await TryFinishLoadFromCacheAsync();
}
return loadComplete;
}
catch (Exception e)
{
TraceLogger.GetInstance().LogStandardException(e);
return false;
}
}
public async Task<bool> TryFinishLoadFromCacheAsync()
{
var localSettings = ApplicationData.Current.LocalSettings;
if (localSettings == null)
{
return false;
}
if (!localSettings.Values.ContainsKey(CurrencyDataLoaderConstants.CacheLangcodeKey) || !((string)localSettings.Values[CurrencyDataLoaderConstants.CacheLangcodeKey]).Equals(m_responseLanguage))
{
return false;
}
StorageFolder localCacheFolder = ApplicationData.Current.LocalCacheFolder;
if (localCacheFolder == null)
{
return false;
}
string staticDataResponse = await Utils.ReadFileFromFolder(localCacheFolder, CurrencyDataLoaderConstants.StaticDataFilename);
string allRatiosResponse = await Utils.ReadFileFromFolder(localCacheFolder, CurrencyDataLoaderConstants.AllRatiosDataFilename);
CalculatorList<UCM.CurrencyStaticData> staticData = new CalculatorList<UCM.CurrencyStaticData>();
CurrencyRatioMap ratioMap = new CurrencyRatioMap();
bool didParse = TryParseWebResponses(staticDataResponse, allRatiosResponse, staticData, ratioMap);
if (!didParse)
{
return false;
}
m_loadStatus = CurrencyLoadStatus.LoadedFromCache;
await FinalizeUnits(staticData, ratioMap);
return true;
}
public async Task<bool> TryLoadDataFromWebAsync()
{
try
{
ResetLoadStatus();
if (m_client == null)
{
return false;
}
if (m_networkAccessBehavior == NetworkAccessBehavior.Offline || (m_networkAccessBehavior == NetworkAccessBehavior.OptIn && !m_meteredOverrideSet))
{
return false;
}
String staticDataResponse = await m_client.GetCurrencyMetadata();
String allRatiosResponse = await m_client.GetCurrencyRatios();
if (staticDataResponse == null || allRatiosResponse == null)
{
return false;
}
CalculatorList<UCM.CurrencyStaticData> staticData = new CalculatorList<UCM.CurrencyStaticData>();
CurrencyRatioMap ratioMap = new CurrencyRatioMap();
bool didParse = TryParseWebResponses(staticDataResponse, allRatiosResponse, staticData, ratioMap);
if (!didParse)
{
return false;
}
// Set the timestamp before saving it below.
m_cacheTimestamp = Utils.GetUniversalSystemTime();
try
{
CalculatorList<KeyValuePair<string, string>> cachedFiles = new CalculatorList<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>(CurrencyDataLoaderConstants.StaticDataFilename, staticDataResponse),
new KeyValuePair<string, string>(CurrencyDataLoaderConstants.AllRatiosDataFilename, allRatiosResponse)
};
StorageFolder localCacheFolder = ApplicationData.Current.LocalCacheFolder;
foreach (var fileInfo in cachedFiles)
{
await Utils.WriteFileToFolder(localCacheFolder, fileInfo.Key, fileInfo.Value, CreationCollisionOption.ReplaceExisting);
}
SaveLangCodeAndTimestamp();
}
catch
{
// If we fail to save to cache it's okay, we should still continue.
}
m_loadStatus = CurrencyLoadStatus.LoadedFromWeb;
await FinalizeUnits(staticData, ratioMap);
return true;
}
catch (Exception e)
{
TraceLogger.GetInstance().LogStandardException(e);
return false;
}
}
public async Task<bool> TryLoadDataFromWebOverrideAsync()
{
m_meteredOverrideSet = true;
bool didLoad = await TryLoadDataFromWebAsync();
if (!didLoad)
{
m_loadStatus = CurrencyLoadStatus.FailedToLoad;
TraceLogger.GetInstance().LogUserRequestedRefreshFailed();
}
return didLoad;
}
bool TryParseWebResponses(
String staticDataJson,
String allRatiosJson,
CalculatorList<UCM.CurrencyStaticData> staticData,
CurrencyRatioMap allRatiosData)
{
return TryParseStaticData(staticDataJson, staticData) && TryParseAllRatiosData(allRatiosJson, allRatiosData);
}
bool TryParseStaticData(String rawJson, CalculatorList<UCM.CurrencyStaticData> staticData)
{
JsonArray data = null;
if (!JsonArray.TryParse(rawJson, out data))
{
return false;
}
string countryCode = "";
string countryName = "";
string currencyCode = "";
string currencyName = "";
string currencySymbol = "";
string[] values = {countryCode, countryName, currencyCode, currencyName, currencySymbol};
Debug.Assert(values.Length == STATIC_DATA_PROPERTIES.Length);
staticData.Clear();
for (int i = 0; i < data.Count; i++)
{
JsonObject obj = data[i].GetObject();
countryCode = obj.GetNamedString(STATIC_DATA_PROPERTIES[0]);
countryName = obj.GetNamedString(STATIC_DATA_PROPERTIES[1]);
currencyCode = obj.GetNamedString(STATIC_DATA_PROPERTIES[2]);
currencyName = obj.GetNamedString(STATIC_DATA_PROPERTIES[3]);
currencySymbol = obj.GetNamedString(STATIC_DATA_PROPERTIES[4]);
staticData.Add(new UCM.CurrencyStaticData(countryCode, countryName, currencyCode, currencyName, currencySymbol));
}
// TODO - MSFT 8533667: this sort will be replaced by a WinRT call to sort localized strings
staticData.Sort((UCM.CurrencyStaticData unit1, UCM.CurrencyStaticData unit2) => { return unit1.countryName.CompareTo(unit2.countryName) < 0; });
return true;
}
public bool TryParseAllRatiosData(String rawJson, CurrencyRatioMap allRatios)
{
JsonArray data = null;
if (!JsonArray.TryParse(rawJson, out data))
{
return false;
}
string sourceCurrencyCode = DefaultCurrencyCode;
allRatios.Clear();
for (int i = 0; i < data.Count; i++)
{
JsonObject obj = data[i].GetObject();
// Rt is ratio, An is target currency ISO code.
double relativeRatio = obj.GetNamedNumber(RATIO_KEY);
string targetCurrencyCode = obj.GetNamedString(CURRENCY_CODE_KEY);
allRatios.Add(targetCurrencyCode, new UCM.CurrencyRatio(relativeRatio, sourceCurrencyCode, targetCurrencyCode));
}
return true;
}
// FinalizeUnits
//
// There are a few ways we can get the data needed for Currency Converter, including from cache or from web.
// This function accepts the data from any source, and acts as a 'last-steps' for the converter to be ready.
// This includes identifying which units will be selected and building the map of currency ratios.
public async Task FinalizeUnits(CalculatorList<UCM.CurrencyStaticData> staticData, CurrencyRatioMap ratioMap)
{
Dictionary<int, KeyValuePair<UCM.Unit, double>> idToUnit = new Dictionary<int, KeyValuePair<UCM.Unit, double>>();
SelectedUnits defaultCurrencies = await GetDefaultFromToCurrency();
string fromCurrency = defaultCurrencies.Key;
string toCurrency = defaultCurrencies.Value;
lock (m_currencyUnitsMutex)
{
int i = 1;
m_currencyUnits.Clear();
m_currencyMetadata.Clear();
bool isConversionSourceSet = false;
bool isConversionTargetSet = false;
foreach (UCM.CurrencyStaticData currencyUnit in staticData)
{
if (ratioMap.TryGetValue(currencyUnit.currencyCode, out var itr) && itr.ratio > 0)
{
int id = (int)(UnitConverterUnits.UnitEnd + i);
bool isConversionSource = (fromCurrency == currencyUnit.currencyCode);
isConversionSourceSet = isConversionSourceSet || isConversionSource;
bool isConversionTarget = (toCurrency == currencyUnit.currencyCode);
isConversionTargetSet = isConversionTargetSet || isConversionTarget;
UCM.Unit unit = new UCM.Unit(
id, // id
currencyUnit.currencyName, // currencyName
currencyUnit.countryName, // countryName
currencyUnit.currencyCode, // abbreviation
m_isRtlLanguage, // isRtlLanguage
isConversionSource, // isConversionSource
isConversionTarget // isConversionTarget
);
m_currencyUnits.PushBack(unit);
m_currencyMetadata.Add(unit, new CurrencyUnitMetadata(currencyUnit.currencySymbol));
idToUnit.Add(unit.id, new KeyValuePair<UCM.Unit, double>(unit, itr.ratio));
i++;
}
}
if (!isConversionSourceSet || !isConversionTargetSet)
{
GuaranteeSelectedUnits();
defaultCurrencies = new SelectedUnits(DEFAULT_FROM_CURRENCY, DEFAULT_TO_CURRENCY);
}
m_currencyRatioMap.Clear();
foreach (var unit in m_currencyUnits)
{
Dictionary<UCM.Unit, UCM.ConversionData> conversions = new Dictionary<UCM.Unit, UCM.ConversionData>(EqualityComparer<UCM.Unit>.Default);
double unitFactor = idToUnit[unit.id].Value;
foreach (var itr in idToUnit)
{
UCM.Unit targetUnit = (itr.Value).Key;
double conversionRatio = (itr.Value).Value;
UCM.ConversionData parsedData = new UCM.ConversionData(1.0, 0.0, false);
Debug.Assert(unitFactor > 0); // divide by zero assert
parsedData.ratio = conversionRatio / unitFactor;
conversions.Add(targetUnit, parsedData);
}
m_currencyRatioMap.Add(unit, conversions);
}
} // unlocked m_currencyUnitsMutex
SaveSelectedUnitsToLocalSettings(defaultCurrencies);
}
void GuaranteeSelectedUnits()
{
bool isConversionSourceSet = false;
bool isConversionTargetSet = false;
foreach (UCM.Unit unit in m_currencyUnits)
{
unit.isConversionSource = false;
unit.isConversionTarget = false;
if (!isConversionSourceSet && unit.abbreviation == DEFAULT_FROM_CURRENCY)
{
unit.isConversionSource = true;
isConversionSourceSet = true;
}
if (!isConversionTargetSet && unit.abbreviation == DEFAULT_TO_CURRENCY)
{
unit.isConversionTarget = true;
isConversionTargetSet = true;
}
}
}
void NotifyDataLoadFinished(bool didLoad)
{
if (!didLoad)
{
m_loadStatus = CurrencyLoadStatus.FailedToLoad;
}
if (m_vmCallback != null)
{
m_vmCallback.CurrencyDataLoadFinished(didLoad);
}
}
void SaveLangCodeAndTimestamp()
{
ApplicationDataContainer localSettings = ApplicationData.Current.LocalSettings;
if (localSettings == null)
{
return;
}
localSettings.Values[CurrencyDataLoaderConstants.CacheTimestampKey] = m_cacheTimestamp.ToString("R");
localSettings.Values[CurrencyDataLoaderConstants.CacheLangcodeKey] = m_responseLanguage;
}
void UpdateDisplayedTimestamp()
{
if (m_vmCallback != null)
{
string timestamp = GetCurrencyTimestamp();
bool isWeekOld = Utils.IsDateTimeOlderThan(m_cacheTimestamp, WEEK_DURATION);
m_vmCallback.CurrencyTimestampCallback(timestamp, isWeekOld);
}
}
public string GetCurrencyTimestamp()
{
string timestamp = "";
DateTime epoch = default(DateTime);
if (m_cacheTimestamp.ToUniversalTime() != epoch.ToUniversalTime())
{
DateTimeFormatter dateFormatter = new DateTimeFormatter("{month.abbreviated} {day.integer}, {year.full}");
string date = dateFormatter.Format(m_cacheTimestamp);
DateTimeFormatter timeFormatter = new DateTimeFormatter("shorttime");
string time = timeFormatter.Format(m_cacheTimestamp);
timestamp = LocalizationStringUtil.GetLocalizedString(m_timestampFormat, date, time);
}
return timestamp;
}
async Task<SelectedUnits> GetDefaultFromToCurrency()
{
string fromCurrency = DEFAULT_FROM_CURRENCY;
string toCurrency = DEFAULT_TO_CURRENCY;
// First, check if we previously stored the last used currencies.
bool foundInLocalSettings = TryGetLastUsedCurrenciesFromLocalSettings(out fromCurrency, out toCurrency);
if (!foundInLocalSettings)
{
try
{
// Second, see if the current locale has preset defaults in DefaultFromToCurrency.json.
Uri fileUri = new Uri(DEFAULT_FROM_TO_CURRENCY_FILE_URI);
StorageFile defaultFromToCurrencyFile = await StorageFile.GetFileFromApplicationUriAsync(fileUri); // TODO UNO
if (defaultFromToCurrencyFile != null)
{
String fileContents = await FileIO.ReadTextAsync(defaultFromToCurrencyFile);
JsonObject fromToObject = JsonObject.Parse(fileContents);
JsonObject regionalDefaults = fromToObject.GetNamedObject(m_responseLanguage);
// Get both values before assignment in-case either fails.
String selectedFrom = regionalDefaults.GetNamedString(FROM_KEY);
String selectedTo = regionalDefaults.GetNamedString(TO_KEY);
fromCurrency = selectedFrom;
toCurrency = selectedTo;
}
}
catch
{
}
}
return new SelectedUnits(fromCurrency, toCurrency);
}
bool TryGetLastUsedCurrenciesFromLocalSettings(out string fromCurrency, out string toCurrency)
{
fromCurrency = toCurrency = null;
String fromKey = UnitConverterResourceKeys.CurrencyUnitFromKey;
String toKey = UnitConverterResourceKeys.CurrencyUnitToKey;
ApplicationDataContainer localSettings = ApplicationData.Current.LocalSettings;
if (localSettings != null && localSettings.Values != null)
{
IPropertySet values = localSettings.Values;
if (values.ContainsKey(fromKey) && values.ContainsKey(toKey))
{
fromCurrency = (String)(values[fromKey]);
toCurrency = (String)(values[toKey]);
return true;
}
}
return false;
}
void SaveSelectedUnitsToLocalSettings(SelectedUnits selectedUnits)
{
String fromKey = UnitConverterResourceKeys.CurrencyUnitFromKey;
String toKey = UnitConverterResourceKeys.CurrencyUnitToKey;
ApplicationDataContainer localSettings = ApplicationData.Current.LocalSettings;
if (localSettings != null && localSettings.Values != null)
{
IPropertySet values = localSettings.Values;
values[fromKey] = selectedUnits.Key;
values[toKey] = selectedUnits.Value;
}
}
}
}

View file

@ -0,0 +1,53 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using System.Text;
using Windows.Foundation;
using Windows.Web.Http;
namespace CalculatorApp.DataLoaders
{
public class CurrencyHttpClient : ICurrencyHttpClient
{
Windows.Web.Http.HttpClient m_client;
string m_responseLanguage;
string m_sourceCurrencyCode;
static string sc_MetadataUriLocalizeFor = "https://go.microsoft.com/fwlink/?linkid=2041093&localizeFor=";
static string sc_RatiosUriRelativeTo = "https://go.microsoft.com/fwlink/?linkid=2041339&localCurrency=";
public CurrencyHttpClient()
{
m_client = new HttpClient();
m_responseLanguage = "en-US";
}
public void SetSourceCurrencyCode(String sourceCurrencyCode)
{
m_sourceCurrencyCode = sourceCurrencyCode;
}
public void SetResponseLanguage(String responseLanguage)
{
m_responseLanguage = responseLanguage;
}
public IAsyncOperationWithProgress<String, HttpProgress> GetCurrencyMetadata()
{
string uri = sc_MetadataUriLocalizeFor + m_responseLanguage;
var metadataUri = new Uri(uri);
return m_client.GetStringAsync(metadataUri);
}
public IAsyncOperationWithProgress<String, HttpProgress> GetCurrencyRatios()
{
string uri = sc_RatiosUriRelativeTo + m_sourceCurrencyCode;
var ratiosUri = new Uri(uri);
return m_client.GetStringAsync(ratiosUri);
}
}
}

View file

@ -0,0 +1,18 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using System.Text;
namespace CalculatorApp.DataLoaders
{
public interface ICurrencyHttpClient
{
void SetSourceCurrencyCode(string sourceCurrencyCode);
void SetResponseLanguage(string responseLanguage);
Windows.Foundation.IAsyncOperationWithProgress<string, Windows.Web.Http.HttpProgress> GetCurrencyMetadata();
Windows.Foundation.IAsyncOperationWithProgress<string, Windows.Web.Http.HttpProgress> GetCurrencyRatios();
}
}

View file

@ -0,0 +1,170 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using System.Text;
namespace CalculatorApp.ViewModel
{
public enum UnitConverterUnits
{
UnitStart = 0,
Area_Acre = UnitStart + 1,
Area_Hectare = UnitStart + 2,
Area_SquareCentimeter = UnitStart + 3,
Area_SquareFoot = UnitStart + 4,
Area_SquareInch = UnitStart + 5,
Area_SquareKilometer = UnitStart + 6,
Area_SquareMeter = UnitStart + 7,
Area_SquareMile = UnitStart + 8,
Area_SquareMillimeter = UnitStart + 9,
Area_SquareYard = UnitStart + 10,
Data_Bit = UnitStart + 11,
Data_Byte = UnitStart + 12,
Data_Gigabit = UnitStart + 13,
Data_Gigabyte = UnitStart + 14,
Data_Kilobit = UnitStart + 15,
Data_Kilobyte = UnitStart + 16,
Data_Megabit = UnitStart + 17,
Data_Megabyte = UnitStart + 18,
Data_Petabit = UnitStart + 19,
Data_Petabyte = UnitStart + 20,
Data_Terabit = UnitStart + 21,
Data_Terabyte = UnitStart + 22,
Energy_BritishThermalUnit = UnitStart + 23,
Energy_Calorie = UnitStart + 24,
Energy_ElectronVolt = UnitStart + 25,
Energy_FootPound = UnitStart + 26,
Energy_Joule = UnitStart + 27,
Energy_Kilocalorie = UnitStart + 28,
Energy_Kilojoule = UnitStart + 29,
Length_Centimeter = UnitStart + 30,
Length_Foot = UnitStart + 31,
Length_Inch = UnitStart + 32,
Length_Kilometer = UnitStart + 33,
Length_Meter = UnitStart + 34,
Length_Micron = UnitStart + 35,
Length_Mile = UnitStart + 36,
Length_Millimeter = UnitStart + 37,
Length_Nanometer = UnitStart + 38,
Length_NauticalMile = UnitStart + 39,
Length_Yard = UnitStart + 40,
Power_BritishThermalUnitPerMinute = UnitStart + 41,
Power_FootPoundPerMinute = UnitStart + 42,
Power_Horsepower = UnitStart + 43,
Power_Kilowatt = UnitStart + 44,
Power_Watt = UnitStart + 45,
Temperature_DegreesCelsius = UnitStart + 46,
Temperature_DegreesFahrenheit = UnitStart + 47,
Temperature_Kelvin = UnitStart + 48,
Time_Day = UnitStart + 49,
Time_Hour = UnitStart + 50,
Time_Microsecond = UnitStart + 51,
Time_Millisecond = UnitStart + 52,
Time_Minute = UnitStart + 53,
Time_Second = UnitStart + 54,
Time_Week = UnitStart + 55,
Time_Year = UnitStart + 56,
Speed_CentimetersPerSecond = UnitStart + 57,
Speed_FeetPerSecond = UnitStart + 58,
Speed_KilometersPerHour = UnitStart + 59,
Speed_Knot = UnitStart + 60,
Speed_Mach = UnitStart + 61,
Speed_MetersPerSecond = UnitStart + 62,
Speed_MilesPerHour = UnitStart + 63,
Volume_CubicCentimeter = UnitStart + 64,
Volume_CubicFoot = UnitStart + 65,
Volume_CubicInch = UnitStart + 66,
Volume_CubicMeter = UnitStart + 67,
Volume_CubicYard = UnitStart + 68,
Volume_CupUS = UnitStart + 69,
Volume_FluidOunceUK = UnitStart + 70,
Volume_FluidOunceUS = UnitStart + 71,
Volume_GallonUK = UnitStart + 72,
Volume_GallonUS = UnitStart + 73,
Volume_Liter = UnitStart + 74,
Volume_Milliliter = UnitStart + 75,
Volume_PintUK = UnitStart + 76,
Volume_PintUS = UnitStart + 77,
Volume_TablespoonUS = UnitStart + 78,
Volume_TeaspoonUS = UnitStart + 79,
Volume_QuartUK = UnitStart + 80,
Volume_QuartUS = UnitStart + 81,
Weight_Carat = UnitStart + 82,
Weight_Centigram = UnitStart + 83,
Weight_Decigram = UnitStart + 84,
Weight_Decagram = UnitStart + 85,
Weight_Gram = UnitStart + 86,
Weight_Hectogram = UnitStart + 87,
Weight_Kilogram = UnitStart + 88,
Weight_LongTon = UnitStart + 89,
Weight_Milligram = UnitStart + 90,
Weight_Ounce = UnitStart + 91,
Weight_Pound = UnitStart + 92,
Weight_ShortTon = UnitStart + 93,
Weight_Stone = UnitStart + 94,
Weight_Tonne = UnitStart + 95,
Area_SoccerField = UnitStart + 99,
Data_FloppyDisk = UnitStart + 100,
Data_CD = UnitStart + 101,
Data_DVD = UnitStart + 102,
Energy_Battery = UnitStart + 103,
Length_Paperclip = UnitStart + 105,
Length_JumboJet = UnitStart + 107,
Power_LightBulb = UnitStart + 108,
Power_Horse = UnitStart + 109,
Volume_Bathtub = UnitStart + 111,
Weight_Snowflake = UnitStart + 113,
Weight_Elephant = UnitStart + 114,
Volume_TeaspoonUK = UnitStart + 115,
Volume_TablespoonUK = UnitStart + 116,
Area_Hand = UnitStart + 118,
Speed_Turtle = UnitStart + 121,
Speed_Jet = UnitStart + 122,
Volume_CoffeeCup = UnitStart + 124,
Weight_Whale = UnitStart + 123,
Volume_SwimmingPool = UnitStart + 125,
Speed_Horse = UnitStart + 126,
Area_Paper = UnitStart + 127,
Area_Castle = UnitStart + 128,
Energy_Banana = UnitStart + 129,
Energy_SliceOfCake = UnitStart + 130,
Length_Hand = UnitStart + 131,
Power_TrainEngine = UnitStart + 132,
Weight_SoccerBall = UnitStart + 133,
Angle_Degree = UnitStart + 134,
Angle_Radian = UnitStart + 135,
Angle_Gradian = UnitStart + 136,
Pressure_Atmosphere = UnitStart + 137,
Pressure_Bar = UnitStart + 138,
Pressure_KiloPascal = UnitStart + 139,
Pressure_MillimeterOfMercury = UnitStart + 140,
Pressure_Pascal = UnitStart + 141,
Pressure_PSI = UnitStart + 142,
Data_Exabits = UnitStart + 143,
Data_Exabytes = UnitStart + 144,
Data_Exbibits = UnitStart + 145,
Data_Exbibytes = UnitStart + 146,
Data_Gibibits = UnitStart + 147,
Data_Gibibytes = UnitStart + 148,
Data_Kibibits = UnitStart + 149,
Data_Kibibytes = UnitStart + 150,
Data_Mebibits = UnitStart + 151,
Data_Mebibytes = UnitStart + 152,
Data_Pebibits = UnitStart + 153,
Data_Pebibytes = UnitStart + 154,
Data_Tebibits = UnitStart + 155,
Data_Tebibytes = UnitStart + 156,
Data_Yobibits = UnitStart + 157,
Data_Yobibytes = UnitStart + 158,
Data_Yottabit = UnitStart + 159,
Data_Yottabyte = UnitStart + 160,
Data_Zebibits = UnitStart + 161,
Data_Zebibytes = UnitStart + 162,
Data_Zetabits = UnitStart + 163,
Data_Zetabytes = UnitStart + 164,
Area_Pyeong = UnitStart + 165,
UnitEnd = Area_Pyeong
};
}

File diff suppressed because it is too large Load diff

View file

@ -21,6 +21,7 @@ using System.Windows.Input;
using System;
using System.Diagnostics;
using System.Collections.ObjectModel;
using CalculatorApp.DataLoaders;
namespace CalculatorApp.ViewModel
{
@ -38,10 +39,8 @@ namespace CalculatorApp.ViewModel
private DateCalculatorViewModel m_DateCalcViewModel;
public DateCalculatorViewModel DateCalcViewModel { get => m_DateCalcViewModel; set { m_DateCalcViewModel = value; RaisePropertyChanged("DateCalcViewModel"); } }
// UNO TODO
//private UnitConverterViewModel m_ConverterViewModel;
//public UnitConverterViewModel ConverterViewModel { get => m_ConverterViewModel; set { m_ConverterViewModel = value; RaisePropertyChanged("ConverterViewModel"); } }
private UnitConverterViewModel m_ConverterViewModel;
public UnitConverterViewModel ConverterViewModel { get => m_ConverterViewModel; set { m_ConverterViewModel = value; RaisePropertyChanged("ConverterViewModel"); } }
private CalculatorApp.Common.ViewMode m_PreviousMode;
@ -62,7 +61,7 @@ namespace CalculatorApp.ViewModel
ViewMode m_mode = ViewMode.None;
ObservableCollection<NavCategoryGroup> m_categories;
CalculatorObservableCollection<NavCategoryGroup> m_categories;
public ApplicationViewModel()
{
@ -87,7 +86,7 @@ namespace CalculatorApp.ViewModel
}
}
public ObservableCollection<NavCategoryGroup> Categories
public CalculatorObservableCollection<NavCategoryGroup> Categories
{
get => m_categories;
set
@ -166,16 +165,15 @@ namespace CalculatorApp.ViewModel
}
else if (NavCategory.IsConverterViewMode(m_mode))
{
// UNO TODO
//// TraceLogger.GetInstance().LogConverterModeViewed(m_mode, ApplicationView.GetApplicationViewIdForWindow(CoreWindow.GetForCurrentThread()));
//if (!m_ConverterViewModel)
//{
// var dataLoader = new UnitConverterDataLoader(new GeographicRegion());
// var currencyDataLoader = new CurrencyDataLoader(new CurrencyHttpClient());
// m_ConverterViewModel = new UnitConverterViewModel(new UnitConversionManager.UnitConverter(dataLoader, currencyDataLoader));
//}
// TraceLogger.GetInstance().LogConverterModeViewed(m_mode, ApplicationView.GetApplicationViewIdForWindow(CoreWindow.GetForCurrentThread()));
if (m_ConverterViewModel == null)
{
var dataLoader = new UnitConverterDataLoader(new GeographicRegion());
var currencyDataLoader = new CurrencyDataLoader(new CurrencyHttpClient());
m_ConverterViewModel = new UnitConverterViewModel(new UnitConversionManager.UnitConverter(dataLoader, currencyDataLoader));
}
//m_ConverterViewModel.Mode = m_mode;
m_ConverterViewModel.Mode = m_mode;
}
var resProvider = AppResourceProvider.GetInstance();
@ -193,13 +191,11 @@ namespace CalculatorApp.ViewModel
void OnCopyCommand(object parameter)
{
// UNO TODO
//if (NavCategory.IsConverterViewMode(m_mode))
//{
// ConverterViewModel.OnCopyCommand(parameter);
//}
//else
if (NavCategory.IsDateCalculatorViewMode(m_mode))
if (NavCategory.IsConverterViewMode(m_mode))
{
ConverterViewModel.OnCopyCommand(parameter);
}
else if (NavCategory.IsDateCalculatorViewMode(m_mode))
{
DateCalcViewModel.OnCopyCommand(parameter);
}
@ -211,13 +207,11 @@ namespace CalculatorApp.ViewModel
void OnPasteCommand(object parameter)
{
// UNO TODO
//if (NavCategory.IsConverterViewMode(m_mode))
//{
// ConverterViewModel.OnPasteCommand(parameter);
//}
//else
if (NavCategory.IsCalculatorViewMode(m_mode))
if (NavCategory.IsConverterViewMode(m_mode))
{
ConverterViewModel.OnPasteCommand(parameter);
}
else if (NavCategory.IsCalculatorViewMode(m_mode))
{
CalculatorViewModel.OnPasteCommand(parameter);
}

View file

@ -30,8 +30,8 @@ namespace CalculatorApp
public int ItemSize { get => m_ItemSize; set { m_ItemSize = value; RaisePropertyChanged("ItemSize"); } }
private ObservableCollection<HistoryItemViewModel> m_Items;
public ObservableCollection<HistoryItemViewModel> Items { get => m_Items; set { m_Items = value; RaisePropertyChanged("Items"); } }
private CalculatorObservableCollection<HistoryItemViewModel> m_Items;
public CalculatorObservableCollection<HistoryItemViewModel> Items { get => m_Items; set { m_Items = value; RaisePropertyChanged("Items"); } }
private bool m_AreHistoryShortcutsEnabled;
@ -70,7 +70,7 @@ namespace CalculatorApp
AreHistoryShortcutsEnabled = true;
Items = new ObservableCollection<HistoryItemViewModel>();
Items = new CalculatorObservableCollection<HistoryItemViewModel>();
ItemSize = 0;
}
@ -97,7 +97,7 @@ namespace CalculatorApp
}
var historyListModel = m_calculatorManager.GetHistoryItems(m_currentMode);
var historyListVM = new ObservableCollection<HistoryItemViewModel>();
var historyListVM = new CalculatorObservableCollection<HistoryItemViewModel>();
var localizer = LocalizationSettings.GetInstance();
if (historyListModel.Count > 0)
{

View file

@ -23,6 +23,7 @@ using System.Text;
using System.Linq;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using Calculator;
namespace CalculatorApp.ViewModel
{
@ -86,8 +87,8 @@ namespace CalculatorApp.ViewModel
public string DisplayStringExpression { get => m_DisplayStringExpression; set { m_DisplayStringExpression = value; RaisePropertyChanged("DisplayStringExpression"); } }
private ObservableCollection<Common.DisplayExpressionToken> m_ExpressionTokens;
public ObservableCollection<Common.DisplayExpressionToken> ExpressionTokens { get => m_ExpressionTokens; private set { m_ExpressionTokens = value; RaisePropertyChanged("ExpressionTokens"); } }
private CalculatorObservableCollection<Common.DisplayExpressionToken> m_ExpressionTokens;
public CalculatorObservableCollection<Common.DisplayExpressionToken> ExpressionTokens { get => m_ExpressionTokens; private set { m_ExpressionTokens = value; RaisePropertyChanged("ExpressionTokens"); } }
private string m_DecimalDisplayValue;
@ -518,7 +519,7 @@ namespace CalculatorApp.ViewModel
m_BinaryDisplayValue = "0";
m_OctalDisplayValue = "0";
m_standardCalculatorManager = new CalculatorManager(ref m_calculatorDisplay, ref m_resourceProvider);
m_ExpressionTokens = new ObservableCollection<DisplayExpressionToken>();
m_ExpressionTokens = new CalculatorObservableCollection<DisplayExpressionToken>();
m_MemorizedNumbers = new List<MemoryItemViewModel>();
m_IsMemoryEmpty = true;
m_IsFToEChecked = false;
@ -618,7 +619,7 @@ namespace CalculatorApp.ViewModel
if (Utils.IsLastCharacterTarget(displayValue, m_decimalSeparator))
{
// remove the decimal separator, to avoid a long pause between words
localizedValue = LocalizeDisplayValue(displayValue.Substring(0, displayValue.Length - 1), isError);
localizedValue = LocalizeDisplayValue(displayValue.substr(0, displayValue.Length - 1), isError);
// Use a format which has a word in the decimal separator's place
// "The Display is 10 point"

File diff suppressed because it is too large Load diff

View file

@ -25,14 +25,14 @@
-->
<!-- CalculatorBaseStyle -->
<Style x:Name="CalculatorBaseStyle"
<Style x:Key="CalculatorBaseStyle"
TargetType="local:Calculator">
<Setter Property="Margin"
Value="0,0,0,0"/>
</Style>
<!-- UnitConverterBaseStyle -->
<Style x:Name="UnitConverterBaseStyle"
<Style x:Key="UnitConverterBaseStyle"
TargetType="local:UnitConverter">
<Setter Property="Visibility"
Value="Collapsed"/>

View file

@ -225,8 +225,7 @@ namespace CalculatorApp
if (NavCategory.IsConverterViewMode(m_model.Mode))
{
int modeIndex = NavCategory.GetIndexInGroup(m_model.Mode, CategoryGroupType.Converter);
// UNO TODO
// m_model.ConverterViewModel.CurrentCategory = m_model.ConverterViewModel.Categories.GetAt(modeIndex);
m_model.ConverterViewModel.CurrentCategory = m_model.ConverterViewModel.Categories.GetAt(modeIndex);
}
}
@ -288,11 +287,10 @@ namespace CalculatorApp
{
m_dateCalculator.SetDefaultFocus();
}
// UNO TODO
//if (m_converter != null && m_converter.Visibility == Visibility.Visible)
//{
// m_converter.SetDefaultFocus();
//}
if (m_converter != null && m_converter.Visibility == Visibility.Visible)
{
m_converter.SetDefaultFocus();
}
}
void EnsureCalculator()
@ -312,11 +310,7 @@ namespace CalculatorApp
Binding isProgramerBinding = new Binding();
isProgramerBinding.Path = new PropertyPath("IsProgrammer");
m_calculator.SetBinding(CalculatorApp.Calculator.IsProgrammerProperty, isProgramerBinding);
#if NETFX_CORE
// UNO TODO
m_calculator.Style = CalculatorBaseStyle;
#endif
m_calculator.Style = (Style)Resources["CalculatorBaseStyle"];
CalcHolder.Child = m_calculator;
@ -354,16 +348,15 @@ namespace CalculatorApp
void EnsureConverter()
{
// UNO TODO
//if (m_converter == null)
//{
// // delay loading converter
// m_converter = new CalculatorApp.UnitConverter();
// m_converter.Name = "unitConverter";
// m_converter.DataContext = m_model.ConverterViewModel;
// m_converter.Style = UnitConverterBaseStyle;
// ConverterHolder.Child = m_converter;
//}
if (m_converter == null)
{
// delay loading converter
m_converter = new CalculatorApp.UnitConverter();
m_converter.Name = "unitConverter";
m_converter.DataContext = m_model.ConverterViewModel;
m_converter.Style = (Style)Resources["UnitConverterBaseStyle"];
ConverterHolder.Child = m_converter;
}
}
void OnNavLoaded(object sender, RoutedEventArgs e)
@ -449,7 +442,7 @@ namespace CalculatorApp
public CalculatorList<object> UIElementsForCategories => CreateUIElementsForCategories(Model.Categories);
public CalculatorList<object> CreateUIElementsForCategories(ObservableCollection<NavCategoryGroup> categories)
public CalculatorList<object> CreateUIElementsForCategories(CalculatorObservableCollection<NavCategoryGroup> categories)
{
var menuCategories = new CalculatorList<object>();
@ -552,14 +545,12 @@ namespace CalculatorApp
{
String categoryName = AutomationProperties.GetName(Header);
NarratorAnnouncement announcement = CalculatorAnnouncement.GetCategoryNameChangedAnnouncement(categoryName);
// UNO TODO
// NarratorNotifier.Announce(announcement);
//NarratorNotifier.Announce(announcement);
}
void OnNavItemInvoked(NavigationView sender, NavigationViewItemInvokedEventArgs e)
{
NavView.IsPaneOpen = false;
}
}
}

View file

@ -90,7 +90,7 @@ namespace CalculatorApp.Views.StateTriggers
);
AspectRatioTrigger()
public AspectRatioTrigger()
{
SetActive(false);
}

View file

@ -3,17 +3,18 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:common="using:CalculatorApp.Common"
xmlns:controls="using:CalculatorApp.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:designdata="using:CalculatorApp.DesignData"
xmlns:local="using:CalculatorApp.Views"
xmlns:local="using:CalculatorApp"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="using:CalculatorApp.ViewModel"
mc:Ignorable="">
d:DesignHeight="300"
d:DesignWidth="400"
mc:Ignorable="d">
<!--<UserControl.Resources>
<DataTemplate x:Name="SupplementaryValueTemplate"
x:DataType="vm:SupplementaryResult">
<StackPanel Margin="0"
Orientation="Horizontal">
<UserControl.Resources>
<DataTemplate x:Key="SupplementaryValueTemplate" x:DataType="vm:SupplementaryResult">
<StackPanel Margin="0" Orientation="Horizontal">
<TextBlock Margin="0,0,4,0"
Padding="0"
VerticalAlignment="Bottom"
@ -21,7 +22,7 @@
Foreground="{ThemeResource SystemControlPageTextBaseHighBrush}"
AutomationProperties.AutomationId="SupplementaryResultValue"
IsTextScaleFactorEnabled="False"
Text="{x:Bind Value}" />
Text="{x:Bind Value}"/>
<TextBlock Margin="0,0,16,0"
Padding="0"
VerticalAlignment="Bottom"
@ -29,18 +30,16 @@
Foreground="{ThemeResource SystemControlPageTextBaseMediumBrush}"
AutomationProperties.AutomationId="SupplementaryResultUnit"
IsTextScaleFactorEnabled="False"
Text="{x:Bind Unit.Abbreviation}" />
Text="{x:Bind Unit.Abbreviation}"/>
</StackPanel>
</DataTemplate>
<local:DelighterUnitToStyleConverter x:Key="DelighterUnitStyleConverter" />
<DataTemplate x:Name="DelighterValueTemplate"
x:DataType="vm:SupplementaryResult">
<StackPanel Margin="0"
Orientation="Horizontal">
<local:DelighterUnitToStyleConverter x:Key="DelighterUnitStyleConverter"/>
<DataTemplate x:Key="DelighterValueTemplate" x:DataType="vm:SupplementaryResult">
<StackPanel Margin="0" Orientation="Horizontal">
<TextBlock VerticalAlignment="Bottom"
Style="{Binding Unit, Converter={StaticResource DelighterUnitStyleConverter}, Mode=OneTime}"
AutomationProperties.AutomationId="DelighterResultGlyph" />
AutomationProperties.AutomationId="DelighterResultGlyph"/>
<TextBlock Margin="-2,0,4,0"
Padding="0"
VerticalAlignment="Bottom"
@ -48,7 +47,7 @@
Foreground="{ThemeResource SystemControlPageTextBaseHighBrush}"
AutomationProperties.AutomationId="DelighterResultValue"
IsTextScaleFactorEnabled="False"
Text="{x:Bind Value}" />
Text="{x:Bind Value}"/>
<TextBlock Margin="0"
Padding="0"
VerticalAlignment="Bottom"
@ -56,34 +55,32 @@
Foreground="{ThemeResource SystemControlPageTextBaseMediumBrush}"
AutomationProperties.AutomationId="DelighterResultUnit"
IsTextScaleFactorEnabled="False"
Text="{x:Bind Unit.Abbreviation}" />
Text="{x:Bind Unit.Abbreviation}"/>
</StackPanel>
</DataTemplate>
<local:SupplementaryResultDataTemplateSelector x:Key="ResultTemplateSelector"
DelighterTemplate="{StaticResource DelighterValueTemplate}"
RegularTemplate="{StaticResource SupplementaryValueTemplate}" />
RegularTemplate="{StaticResource SupplementaryValueTemplate}"/>
<Style x:Key="SupplementaryValuesStyle"
TargetType="controls:SupplementaryItemsControl">
<Setter Property="IsTabStop"
Value="False" />
<Style x:Key="SupplementaryValuesStyle" TargetType="controls:SupplementaryItemsControl">
<Setter Property="IsTabStop" Value="False"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="controls:SupplementaryItemsControl">
<ItemsPresenter />
<ItemsPresenter/>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="ItemContainerTransitions">
<Setter.Value>
<TransitionCollection />
<TransitionCollection/>
</Setter.Value>
</Setter>
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
<local:SupplementaryResultNoOverflowStackPanel/>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
@ -98,13 +95,13 @@
Style="{ThemeResource CaptionTextBlockStyle}"
Foreground="{ThemeResource SystemControlPageTextBaseMediumBrush}"
IsTextScaleFactorEnabled="False"
Text="Also equal to" />
Text="Also equal to"/>
<controls:SupplementaryItemsControl x:Name="SupplementaryValues"
MinHeight="27"
HorizontalAlignment="Left"
Style="{ThemeResource SupplementaryValuesStyle}"
IsTextScaleFactorEnabled="False"
ItemTemplateSelector="{StaticResource ResultTemplateSelector}"
LayoutUpdated="OnSupplementaryValuesLayoutUpdated" />
</StackPanel>-->
ItemsSource="{x:Bind Results, Mode=OneWay}"/>
</StackPanel>
</UserControl>

View file

@ -1,5 +1,10 @@
using System;
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
@ -12,16 +17,116 @@ using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
using CalculatorApp.Controls;
using CalculatorApp.ViewModel;
// The User Control item template is documented at https://go.microsoft.com/fwlink/?LinkId=234236
namespace CalculatorApp
{
public sealed class DelighterUnitToStyleConverter : Windows.UI.Xaml.Data.IValueConverter
{
private Windows.UI.Xaml.ResourceDictionary m_delighters;
public DelighterUnitToStyleConverter()
{
m_delighters = new Windows.UI.Xaml.ResourceDictionary();
m_delighters.Source = new Uri("ms-appx:///Views/DelighterUnitStyles.xaml");
}
public object Convert(object value, Type targetType, object parameter, string language)
{
Unit unit = value as Unit;
Debug.Assert(unit.GetModelUnit().isWhimsical);
if ((!unit?.GetModelUnit().isWhimsical) ?? true)
{
return null;
}
string key = "Unit_" + unit.GetModelUnit().id;
return m_delighters[key];
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
// We never use convert back, only one way binding supported
Debug.Assert(false);
return null;
}
};
public sealed class SupplementaryResultDataTemplateSelector : Windows.UI.Xaml.Controls.DataTemplateSelector
{
Windows.UI.Xaml.DataTemplate m_regularTemplate;
Windows.UI.Xaml.DataTemplate m_delighterTemplate;
public Windows.UI.Xaml.DataTemplate RegularTemplate
{
get { return m_regularTemplate; }
set { m_regularTemplate = value; }
}
public Windows.UI.Xaml.DataTemplate DelighterTemplate
{
get { return m_delighterTemplate; }
set { m_delighterTemplate = value; }
}
protected override Windows.UI.Xaml.DataTemplate SelectTemplateCore(object item, DependencyObject container)
{
SupplementaryResult result = item as SupplementaryResult;
if (result?.IsWhimsical() ?? false)
{
return DelighterTemplate;
}
else
{
return RegularTemplate;
}
}
}
public sealed partial class SupplementaryResults : UserControl
{
// TODO UNO: DEPENDENCY_PROPERTY_OWNER(SupplementaryResults);
// DEPENDENCY_PROPERTY_INITIALIZATION(SupplementaryResults, Results);
public static readonly DependencyProperty ResultProperty = DependencyProperty.Register(
"Results",
typeof(System.Collections.Generic.IEnumerable<ViewModel.SupplementaryResult>),
typeof(SupplementaryResults),
new PropertyMetadata(default(System.Collections.Generic.IEnumerable<ViewModel.SupplementaryResult>)));
public System.Collections.Generic.IEnumerable<ViewModel.SupplementaryResult> Results
{
get { return (System.Collections.Generic.IEnumerable<ViewModel.SupplementaryResult>)GetValue(ResultProperty); }
set { SetValue(ResultProperty, value); }
}
public SupplementaryResults()
{
Results = new CalculatorObservableCollection<SupplementaryResult>();
this.InitializeComponent();
}
}
public sealed partial class SupplementaryResultNoOverflowStackPanel : HorizontalNoOverflowStackPanel
{
protected override bool ShouldPrioritizeLastItem()
{
if (Children.Count == 0)
{
return false;
}
var lastChild = Children.Last() as FrameworkElement;
if (lastChild == null)
{
return false;
}
var suppResult = lastChild.DataContext as SupplementaryResult;
return suppResult == null ? false : suppResult.IsWhimsical();
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,17 +1,30 @@
using System;
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.System;
using Windows.UI.ViewManagement;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Automation;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
using Calculator;
using CalculatorApp.Common;
using CalculatorApp.Controls;
using CalculatorApp.ViewModel;
using UnitConversionManager;
// The User Control item template is documented at https://go.microsoft.com/fwlink/?LinkId=234236
@ -19,9 +32,365 @@ namespace CalculatorApp
{
public sealed partial class UnitConverter : UserControl
{
const long DURATION_500_MS = 10000 * 500;
Windows.UI.Xaml.FlowDirection m_layoutDirection;
//Windows.Foundation.EventRegistrationToken m_propertyChangedToken;
Windows.UI.Xaml.Controls.MenuFlyout m_resultsFlyout;
string m_chargesMayApplyText;
string m_failedToRefreshText;
bool m_meteredConnectionOverride;
Windows.UI.Xaml.DispatcherTimer m_delayTimer;
bool m_isAnimationEnabled;
// TODO UNO: DEPENDENCY_PROPERTY_OWNER(UnitConverter);
private HorizontalAlignment m_FlowDirectionHorizontalAlignment;
public HorizontalAlignment FlowDirectionHorizontalAlignment
{
get => m_FlowDirectionHorizontalAlignment;
set => m_FlowDirectionHorizontalAlignment = value;
}
public UnitConverterViewModel Model
{
get { return (UnitConverterViewModel)this.DataContext; }
}
public FlowDirection LayoutDirection
{
get { return m_layoutDirection;
}
}
public UnitConverter()
{
m_meteredConnectionOverride = false;
m_isAnimationEnabled = false;
m_layoutDirection = LocalizationService.GetInstance().GetFlowDirection();
m_FlowDirectionHorizontalAlignment = m_layoutDirection == FlowDirection.RightToLeft ? HorizontalAlignment.Right : HorizontalAlignment.Left;
this.InitializeComponent();
// adding ESC key shortcut binding to clear button
ClearEntryButtonPos0.SetValue(Common.KeyboardShortcutManager.VirtualKeyProperty, Common.MyVirtualKey.Escape);
// Is currency symbol preference set to right side
bool preferRight = LocalizationSettings.GetInstance().GetCurrencySymbolPrecedence() == 0;
VisualStateManager.GoToState(this, preferRight ? "CurrencySymbolRightState" : "CurrencySymbolLeftState", false);
var userSettings = new UISettings();
m_isAnimationEnabled = userSettings.AnimationsEnabled;
var resLoader = AppResourceProvider.GetInstance();
m_chargesMayApplyText = resLoader.GetResourceString("DataChargesMayApply");
m_failedToRefreshText = resLoader.GetResourceString("FailedToRefresh");
InitializeOfflineStatusTextBlock();
// TODO UNO: m_resultsFlyout = (MenuFlyout) (Resources["CalculationResultContextMenu"]);
// CopyMenuItem.Text = resLoader.GetResourceString("copyMenuItem");
// PasteMenuItem.Text = resLoader.GetResourceString("pasteMenuItem");
}
void OnPropertyChanged( object sender, PropertyChangedEventArgs e)
{
String propertyName = e.PropertyName;
if (propertyName == UnitConverterViewModel.NetworkBehaviorPropertyName || propertyName == UnitConverterViewModel.CurrencyDataLoadFailedPropertyName)
{
OnNetworkBehaviorChanged();
}
else if (propertyName == UnitConverterViewModel.CurrencyDataIsWeekOldPropertyName)
{
SetCurrencyTimestampFontWeight();
}
else if (propertyName == UnitConverterViewModel.IsCurrencyLoadingVisiblePropertyName)
{
OnIsDisplayVisibleChanged();
}
}
void OnNetworkBehaviorChanged()
{
switch (Model.NetworkBehavior)
{
case NetworkAccessBehavior.Normal:
OnNormalNetworkAccess();
break;
case NetworkAccessBehavior.OptIn:
OnOptInNetworkAccess();
break;
case NetworkAccessBehavior.Offline:
OnOfflineNetworkAccess();
break;
}
}
void OnNormalNetworkAccess()
{
CurrencyRefreshBlockControl.Visibility = Visibility.Visible;
OfflineBlock.Visibility = Visibility.Collapsed;
if (Model.CurrencyDataLoadFailed)
{
SetFailedToRefreshStatus();
}
else
{
SetNormalCurrencyStatus();
}
}
void OnOptInNetworkAccess()
{
CurrencyRefreshBlockControl.Visibility = Visibility.Visible;
OfflineBlock.Visibility = Visibility.Collapsed;
if (m_meteredConnectionOverride && Model.CurrencyDataLoadFailed)
{
SetFailedToRefreshStatus();
}
else
{
SetChargesMayApplyStatus();
}
}
void OnOfflineNetworkAccess()
{
CurrencyRefreshBlockControl.Visibility = Visibility.Collapsed;
OfflineBlock.Visibility = Visibility.Visible;
}
void SetNormalCurrencyStatus()
{
CurrencySecondaryStatus.Text = "";
}
void SetChargesMayApplyStatus()
{
VisualStateManager.GoToState(this, "ChargesMayApplyCurrencyStatus", false);
CurrencySecondaryStatus.Text = m_chargesMayApplyText;
}
void SetFailedToRefreshStatus()
{
VisualStateManager.GoToState(this, "FailedCurrencyStatus", false);
CurrencySecondaryStatus.Text = m_failedToRefreshText;
}
void InitializeOfflineStatusTextBlock()
{
var resProvider = AppResourceProvider.GetInstance();
string offlineStatusHyperlinkText = resProvider.GetResourceString("OfflineStatusHyperlinkText");
// The resource string has the 'NetworkSettings' hyperlink wrapped with '%HL%'.
// Break the string and assign pieces appropriately.
string delimiter = "%HL%" ;
int delimiterLength = delimiter.Length;
// Find the delimiters.
int firstSplitPosition = offlineStatusHyperlinkText.find(delimiter, 0);
Debug.Assert(firstSplitPosition != "".npos());
int secondSplitPosition = offlineStatusHyperlinkText.find(delimiter, firstSplitPosition + 1);
Debug.Assert(secondSplitPosition != "".npos());
int hyperlinkTextLength = secondSplitPosition - (firstSplitPosition + delimiterLength);
// Assign pieces.
var offlineStatusTextBeforeHyperlink = offlineStatusHyperlinkText.substr(0, firstSplitPosition);
var offlineStatusTextLink = offlineStatusHyperlinkText.substr(firstSplitPosition + delimiterLength, hyperlinkTextLength);
var offlineStatusTextAfterHyperlink = offlineStatusHyperlinkText.substr(secondSplitPosition + delimiterLength);
OfflineRunBeforeLink.Text = offlineStatusTextBeforeHyperlink;
OfflineRunLink.Text = offlineStatusTextLink;
OfflineRunAfterLink.Text = offlineStatusTextAfterHyperlink;
AutomationProperties.SetName(OfflineBlock, offlineStatusTextBeforeHyperlink + " " + offlineStatusTextLink + " " + offlineStatusTextAfterHyperlink);
}
void SetCurrencyTimestampFontWeight()
{
if (Model.CurrencyDataIsWeekOld)
{
VisualStateManager.GoToState(this, "WeekOldTimestamp", false);
}
else
{
VisualStateManager.GoToState(this, "DefaultTimestamp", false);
}
}
void OnValueKeyDown(object sender, Windows.UI.Xaml.Input.KeyRoutedEventArgs e)
{
if (e.Key == VirtualKey.Space)
{
OnValueSelected(sender);
}
}
void OnContextRequested(UIElement sender, ContextRequestedEventArgs e)
{
OnValueSelected(sender);
var requestedElement = sender as FrameworkElement;
// TODO UNO
//PasteMenuItem.IsEnabled = CopyPasteManager.HasStringToPaste();
Point point;
if (e.TryGetPosition(requestedElement, out point))
{
m_resultsFlyout.ShowAt(requestedElement, point);
}
else
{
// Not invoked via pointer, so let XAML choose a default location.
m_resultsFlyout.ShowAt(requestedElement);
}
e.Handled = true;
}
void OnContextCanceled(UIElement sender, RoutedEventArgs e)
{
m_resultsFlyout.Hide();
}
void OnCopyMenuItemClicked( object sender, RoutedEventArgs e)
{
var calcResult = (m_resultsFlyout.Target) as CalculationResult;
CopyPasteManager.CopyToClipboard(calcResult.GetRawDisplayValue());
}
async void OnPasteMenuItemClicked( object sender, RoutedEventArgs e)
{
var pastedString = await CopyPasteManager.GetStringToPaste(Model.Mode, CategoryGroupType.Converter, default(int), default(int));
Model.OnPaste(pastedString, Model.Mode);
}
public void AnimateConverter()
{
if (App.IsAnimationEnabled())
{
AnimationStory.Begin();
}
}
void OnValueSelected( object sender)
{
var value = (CalculationResult) sender;
// update the font size since the font is changed to bold
value.UpdateTextState();
((UnitConverterViewModel) this.DataContext).OnValueActivated(value);
}
void UpdateDropDownState( object sender, object e)
{
((UnitConverterViewModel)this.DataContext).IsDropDownOpen = (Units1.IsDropDownOpen) || (Units2.IsDropDownOpen);
// TODO UNO: KeyboardShortcutManager.UpdateDropDownState((Units1.IsDropDownOpen) || (Units2.IsDropDownOpen));
}
void OnLoaded(object sender, RoutedEventArgs args)
{
}
public void SetDefaultFocus()
{
CalculatorList<Control> focusPrecedence = new CalculatorList<Control>{ Value1, CurrencyRefreshBlockControl, OfflineBlock, ClearEntryButtonPos0 };
foreach (Control control in focusPrecedence)
{
if (control.Focus(FocusState.Programmatic))
{
break;
}
}
}
void CurrencyRefreshButton_Click( object sender, RoutedEventArgs e)
{
if (Model.NetworkBehavior == NetworkAccessBehavior.OptIn)
{
m_meteredConnectionOverride = true;
}
Model.RefreshCurrencyRatios();
}
void OnDataContextChanged( DependencyObject sender, DataContextChangedEventArgs args)
{
Model.PropertyChanged -= OnPropertyChanged;
Model.PropertyChanged += new PropertyChangedEventHandler(OnPropertyChanged);
OnNetworkBehaviorChanged();
}
void Units1_IsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if ((Units1.Visibility == Visibility.Visible) && Units1.IsEnabled)
{
SetDefaultFocus();
}
}
void OnIsDisplayVisibleChanged()
{
if (Model.IsCurrencyLoadingVisible)
{
StartProgressRingWithDelay();
}
else
{
HideProgressRing();
if (m_isAnimationEnabled && Model.IsCurrencyCurrentCategory && !Model.CurrencyTimestamp.IsEmpty())
{
TimestampFadeInAnimation.Begin();
}
}
}
void StartProgressRingWithDelay()
{
HideProgressRing();
TimeSpan delay = TimeSpan.FromMilliseconds(DURATION_500_MS);
m_delayTimer = new DispatcherTimer();
m_delayTimer.Interval = delay;
m_delayTimer.Tick += new EventHandler<object> (OnDelayTimerTick);
m_delayTimer.Start();
}
void OnDelayTimerTick(object sender, object e)
{
CurrencyLoadingProgressRing.IsActive = true;
m_delayTimer.Stop();
}
void HideProgressRing()
{
if (m_delayTimer != null)
{
m_delayTimer.Stop();
}
CurrencyLoadingProgressRing.IsActive = false;
}
// The function will make sure the UI will have enough space to display supplementary results and currency information
void SupplementaryResultsPanelInGrid_SizeChanged(object sender, Windows.UI.Xaml.SizeChangedEventArgs e)
{
// We add 0.01 to be sure to not create an infinite loop with SizeChanged events cascading due to float approximation
RowDltrUnits.MinHeight = Math.Max(48.0, e.NewSize.Height + 0.01);
}
}
}

View file

@ -0,0 +1,45 @@
using System;
using System.Linq;
namespace Calculator
{
public static class StringExtensions
{
private const int _npos = -1;
public static char back(this string str) => str.Last();
public static char front(this string str) => str.First();
public static int length(this string str) => str.Length;
public static char at(this string str, int index) => str[index];
public static bool empty(this string str) => string.IsNullOrEmpty(str);
public static bool IsEmpty(this string str) => string.IsNullOrEmpty(str);
public static int npos(this string str) => _npos;
public static int size(this string str) => str.Length;
public static int find(this string str, char c) => str.IndexOf(c);
public static int find(this string str, char c, int startIndex) => str.IndexOf(c, startIndex);
public static int find(this string str, string c) => str.IndexOf(c);
public static int find(this string str, string c, int startIndex) => str.IndexOf(c, startIndex);
public static string substr(this string str, int pos = 0, int len = _npos)
{
if (pos == str.Length)
{
return string.Empty;
}
if (pos > str.Length)
{
throw new ArgumentOutOfRangeException(nameof(pos));
}
if (len == _npos)
{
return str.Substring(pos);
}
else
{
return str.Substring(pos, Math.Min(len, str.Length - pos));
}
}
}
}

View file

@ -12,6 +12,7 @@
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
<ResourcesDirectory>..\Calculator.Shared\Strings</ResourcesDirectory>
<LangVersion>7.3</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|iPhoneSimulator' ">
<DebugSymbols>true</DebugSymbols>