diff --git a/src/CalcViewModel/Snapshots.cpp b/src/CalcViewModel/Snapshots.cpp index ff651dd4..e12c44cf 100644 --- a/src/CalcViewModel/Snapshots.cpp +++ b/src/CalcViewModel/Snapshots.cpp @@ -35,6 +35,11 @@ namespace CalculatorApp::ViewModel::Snapshot } } + Windows::Foundation::Collections::IVectorView ^ OperandCommand::Commands::get() + { + return ref new Platform::Collections::VectorView(m_cmds); + } + ICalcManagerIExprCommand ^ CreateExprCommand(const IExpressionCommand* exprCmd) { switch (exprCmd->GetCommandType()) { diff --git a/src/CalcViewModel/Snapshots.h b/src/CalcViewModel/Snapshots.h index b75d424c..f22d1c36 100644 --- a/src/CalcViewModel/Snapshots.h +++ b/src/CalcViewModel/Snapshots.h @@ -14,6 +14,7 @@ public { }; +public ref struct UnaryCommand sealed : public ICalcManagerIExprCommand { property Windows::Foundation::Collections::IVectorView ^ Commands { Windows::Foundation::Collections::IVectorView ^ get(); }; @@ -28,6 +29,7 @@ public std::vector m_cmds; }; +public ref struct BinaryCommand sealed : public ICalcManagerIExprCommand { property int Command; @@ -37,6 +39,7 @@ public } }; +public ref struct OperandCommand sealed : public ICalcManagerIExprCommand { property bool IsNegative; @@ -56,6 +59,7 @@ public std::vector m_cmds; }; +public ref struct Parentheses sealed : public ICalcManagerIExprCommand { property int Command; diff --git a/src/Calculator/Utils/SerdeUtils.cs b/src/Calculator/Utils/SerdeUtils.cs index 053f2a54..bf4ec663 100644 --- a/src/Calculator/Utils/SerdeUtils.cs +++ b/src/Calculator/Utils/SerdeUtils.cs @@ -1,9 +1,11 @@ +using System; using System.Collections.Generic; using System.Linq; +using System.Security.Cryptography.X509Certificates; using System.Text.Json.Serialization; using CalculatorApp.ViewModel.Snapshot; -namespace CalculatorApp +namespace CalculatorApp.SerdeUtils { internal class CalcManagerHistoryTokenAlias { @@ -20,13 +22,55 @@ namespace CalculatorApp public int CommandIndex { get => Value.CommandIndex; } } - internal class CalcManagerIExprCommandAlias + [JsonPolymorphic(TypeDiscriminatorPropertyName = "$t")] + [JsonDerivedType(typeof(UnaryCommandAlias), typeDiscriminator: 0)] + [JsonDerivedType(typeof(BinaryCommandAlias), typeDiscriminator: 1)] + [JsonDerivedType(typeof(OperandCommandAlias), typeDiscriminator: 2)] + [JsonDerivedType(typeof(ParenthesesAlias), typeDiscriminator: 3)] + internal interface ICalcManagerIExprCommandAlias { - [JsonPropertyName("t")] - public string Type; + } + + internal class UnaryCommandAlias : ICalcManagerIExprCommandAlias + { + [JsonIgnore] + public UnaryCommand Value; [JsonPropertyName("c")] - public string CmdString; + public IList Commands { get => Value.Commands.ToList(); } + } + + internal class BinaryCommandAlias : ICalcManagerIExprCommandAlias + { + [JsonIgnore] + public BinaryCommand Value; + + [JsonPropertyName("c")] + public int Command { get => Value.Command; } + } + + internal class OperandCommandAlias : ICalcManagerIExprCommandAlias + { + [JsonIgnore] + public OperandCommand Value; + + [JsonPropertyName("n")] + public bool IsNegative { get => Value.IsNegative; } + [JsonPropertyName("d")] + public bool IsDecimalPresent { get => Value.IsDecimalPresent; } + [JsonPropertyName("s")] + public bool IsSciFmt { get => Value.IsSciFmt; } + [JsonPropertyName("c")] + public IList Commands { get => Value.Commands.ToList(); } + } + + internal class ParenthesesAlias : ICalcManagerIExprCommandAlias + { + [JsonIgnore] + public Parentheses Value; + + [JsonPropertyName("c")] + public int Command { get => Value.Command; } } internal class CalcManagerHistoryItemAlias @@ -38,12 +82,100 @@ namespace CalculatorApp public IList Tokens { get => Value.Tokens.Select(x => new CalcManagerHistoryTokenAlias { Value = x }).ToList(); - set => Value.Tokens = value.Select(x => new CalcManagerHistoryToken(x.OpCodeName, x.CommandIndex)).ToList(); } - //public IList Commands - //{ - // get => Value.Commands; - //} + [JsonPropertyName("c")] + public IList Commands + { + get => Value.Commands.Select(Helpers.MapCommandAlias).ToList(); + } + + [JsonPropertyName("e")] + public string Expression { get => Value.Expression; } + + [JsonPropertyName("r")] + public string Result { get => Value.Result; } + + } + + internal class CalcManagerSnapshotAlias + { + [JsonIgnore] + public CalcManagerSnapshot Value; + + [JsonPropertyName("h")] + public IList HistoryItems { get => Value.HistoryItems.Select(x => new CalcManagerHistoryItemAlias { Value = x }).ToList(); } + } + + internal class PrimaryDisplaySnapshotAlias + { + [JsonIgnore] + public PrimaryDisplaySnapshot Value; + + [JsonPropertyName("d")] + public string DisplayValue { get => Value.DisplayValue; } + [JsonPropertyName("e")] + public bool IsError { get => Value.IsError; } + } + + internal class ExpressionDisplaySnapshotAlias + { + [JsonIgnore] + public ExpressionDisplaySnapshot Value; + + [JsonPropertyName("t")] + public IList Tokens { get => Value.Tokens.Select(x => new CalcManagerHistoryTokenAlias { Value = x }).ToList(); } + [JsonPropertyName("c")] + public IList Commands { get => Value.Commands.Select(Helpers.MapCommandAlias).ToList(); } + } + + internal class StandardCalculatorSnapshotAlias + { + [JsonIgnore] + public StandardCalculatorSnapshot Value; + + [JsonPropertyName("m")] + public CalcManagerSnapshotAlias CalcManager { get => new CalcManagerSnapshotAlias { Value = Value.CalcManager }; } + [JsonPropertyName("p")] + public PrimaryDisplaySnapshotAlias PrimaryDisplay { get => new PrimaryDisplaySnapshotAlias { Value = Value.PrimaryDisplay }; } + [JsonPropertyName("e")] + public ExpressionDisplaySnapshotAlias ExpressionDisplay { get => new ExpressionDisplaySnapshotAlias { Value = Value.ExpressionDisplay }; } + [JsonPropertyName("c")] + public IList Commands { get => Value.DisplayCommands.Select(Helpers.MapCommandAlias).ToList(); } + } + + internal class ApplicationSnapshotAlias + { + [JsonIgnore] + public ApplicationSnapshot Value; + + [JsonPropertyName("m")] + public int Mode { get => Value.Mode; } + [JsonPropertyName("s")] + public StandardCalculatorSnapshotAlias StandardCalculatorSnapshot { get => new StandardCalculatorSnapshotAlias { Value = Value.StandardCalculator }; } + } + + internal static class Helpers + { + public static ICalcManagerIExprCommandAlias MapCommandAlias(ICalcManagerIExprCommand exprCmd) + { + if (exprCmd is UnaryCommand unary) + { + return new UnaryCommandAlias { Value = unary }; + } + else if (exprCmd is BinaryCommand binary) + { + return new BinaryCommandAlias { Value = binary }; + } + else if (exprCmd is OperandCommand operand) + { + return new OperandCommandAlias { Value = operand }; + } + else if (exprCmd is Parentheses paren) + { + return new ParenthesesAlias { Value = paren }; + } + throw new NotImplementedException("unhandled command type."); + } } } diff --git a/src/Calculator/Views/MainPage.xaml.cs b/src/Calculator/Views/MainPage.xaml.cs index 5f656db2..cb6a5036 100644 --- a/src/Calculator/Views/MainPage.xaml.cs +++ b/src/Calculator/Views/MainPage.xaml.cs @@ -24,6 +24,9 @@ using CalculatorApp.ViewModel.Common.Automation; using wuxc = Windows.UI.Xaml.Controls; using System.Text.Json; +using System.IO.Compression; +using System.IO; +using System.Text; namespace CalculatorApp { @@ -62,25 +65,40 @@ namespace CalculatorApp UserActivityRequestManager.GetForCurrentView().UserActivityRequested += async (_, args) => { - var deferral = args.GetDeferral(); - if (deferral == null) + using (var deferral = args.GetDeferral()) { - // Windows Bug in ni_moment won't return the deferral propoerly, see https://microsoft.visualstudio.com/DefaultCollection/OS/_workitems/edit/47775705/ - return; + if (deferral == null) + { + // Windows Bug in ni_moment won't return the deferral propoerly, see https://microsoft.visualstudio.com/DefaultCollection/OS/_workitems/edit/47775705/ + return; + } + var channel = UserActivityChannel.GetDefault(); + var activity = await channel.GetOrCreateUserActivityAsync($"{Guid.NewGuid()}"); + string embeddedData; + try + { + var json = JsonSerializer.Serialize(new SerdeUtils.ApplicationSnapshotAlias { Value = Model.Snapshot }); + var json2 = JsonSerializer.Serialize(Model.Snapshot); + embeddedData = Convert.ToBase64String(Compress(json)); + var e2 = Convert.ToBase64String(Compress(json2)); + var diff = embeddedData.Length - e2.Length; + } + catch (Exception) + { + // TODO: trace errors + deferral.Complete(); + return; + } + activity.ActivationUri = new Uri($"ms-calculator:snapshot/{embeddedData}"); + activity.IsRoamable = false; + var resProvider = AppResourceProvider.GetInstance(); + activity.VisualElements.DisplayText = + $"{resProvider.GetResourceString("AppName")} - {resProvider.GetResourceString(NavCategoryStates.GetNameResourceKey(Model.Mode))}"; + await activity.SaveAsync(); + args.Request.SetUserActivity(activity); + deferral.Complete(); + TraceLogger.GetInstance().LogRecallSnapshot(Model.Mode); } - var channel = UserActivityChannel.GetDefault(); - var activity = await channel.GetOrCreateUserActivityAsync($"{Guid.NewGuid()}"); - var s = Model.Snapshot; - var j = JsonSerializer.Serialize(s); - activity.ActivationUri = new Uri($"ms-calculator:snapshot/TODO"); - activity.IsRoamable = false; - var resProvider = AppResourceProvider.GetInstance(); - activity.VisualElements.DisplayText = - $"{resProvider.GetResourceString("AppName")} - {resProvider.GetResourceString(NavCategoryStates.GetNameResourceKey(Model.Mode))}"; - await activity.SaveAsync(); - args.Request.SetUserActivity(activity); - deferral.Complete(); - TraceLogger.GetInstance().LogRecallSnapshot(Model.Mode); }; } @@ -705,6 +723,41 @@ namespace CalculatorApp await dialog.ShowAsync(); } + private static byte[] Compress(string text) + { + var data = Encoding.UTF8.GetBytes(text); + using (var compressed = new MemoryStream()) + { + using (var deflater = new DeflateStream(compressed, CompressionLevel.Optimal)) + { + deflater.Write(data, 0, data.Length); + } + return compressed.ToArray(); + } + + //encodedSnapshot = null; + //try + //{ + // var rawJson = Encoding.UTF8.GetBytes(JsonSerializer.Serialize(new SerdeUtils.ApplicationSnapshotAlias { Value = Model.Snapshot })); + // using (var compressed = new MemoryStream()) + // { + // using (var deflater = new DeflateStream(compressed, CompressionLevel.Optimal, true)) + // { + // deflater.Write(rawJson, 0, rawJson.Length); + // } + // byte[] data; + // compressed.Read(data, 0, ); + // Convert.ToBase64String(compressed.GetBuffer()); + // } + //} + //catch (Exception) + //{ + // // TODO: trace errors + // return false; + //} + //return true; + } + private Calculator m_calculator; private GraphingCalculator m_graphingCalculator; private UnitConverter m_converter;