diff --git a/src/CalcViewModel/ApplicationViewModel.cpp b/src/CalcViewModel/ApplicationViewModel.cpp index 7a54182d..f2835fb4 100644 --- a/src/CalcViewModel/ApplicationViewModel.cpp +++ b/src/CalcViewModel/ApplicationViewModel.cpp @@ -37,446 +37,6 @@ namespace { StringReference CategoriesPropertyName(L"Categories"); StringReference ClearMemoryVisibilityPropertyName(L"ClearMemoryVisibility"); - - struct SnapshotHelper - { - static constexpr int SnapshotVersion = 0; - - static Windows::Data::Json::JsonObject ^ SaveSnapshotToJson(const ApplicationSnapshot& value) - { - auto jsonObject = ref new Windows::Data::Json::JsonObject(); - jsonObject->SetNamedValue(L"SnapshotVersion", Windows::Data::Json::JsonValue::CreateNumberValue(value.SnapshotVersion)); - jsonObject->SetNamedValue(L"Mode", Windows::Data::Json::JsonValue::CreateNumberValue(value.Mode)); - if (value.StandardCalc.has_value()) - { - jsonObject->SetNamedValue(L"StandardCalculatorSnapshot", SaveSnapshotToJson(*value.StandardCalc)); - } - return jsonObject; - } - - static Windows::Data::Json::JsonObject ^ SaveSnapshotToJson(const StandardCalculatorSnapshot& value) - { - auto jsonObject = ref new Windows::Data::Json::JsonObject(); - jsonObject->SetNamedValue(L"CalculatorManagerSnapshot", SaveSnapshotToJson(value.CalcManager)); - jsonObject->SetNamedValue(L"PrimaryDisplay", SaveSnapshotToJson(value.PrimaryDisplay)); - if (value.ExpressionDisplay.has_value()) - { - jsonObject->SetNamedValue(L"ExpressionDisplay", SaveSnapshotToJson(*value.ExpressionDisplay)); - } - auto commandsJsonArray = ref new Windows::Data::Json::JsonArray(); - for (const auto& command : value.DisplayCommands) - { - commandsJsonArray->Append(SaveSnapshotToJson(command)); - } - jsonObject->SetNamedValue(L"DisplayCommands", commandsJsonArray); - return jsonObject; - } - - static Windows::Data::Json::JsonObject ^ SaveSnapshotToJson(const PrimaryDisplaySnapshot& value) - { - auto jsonObject = ref new Windows::Data::Json::JsonObject(); - jsonObject->SetNamedValue(L"DisplayValue", Windows::Data::Json::JsonValue::CreateStringValue(value.DisplayValue)); - jsonObject->SetNamedValue(L"IsError", Windows::Data::Json::JsonValue::CreateBooleanValue(value.IsError)); - return jsonObject; - } - - static Windows::Data::Json::JsonObject ^ SaveSnapshotToJson(const ExpressionDisplaySnapshot& value) - { - auto jsonObject = ref new Windows::Data::Json::JsonObject(); - auto tokensJsonArray = ref new Windows::Data::Json::JsonArray(); - for (const auto& token : value.Tokens) - { - auto tokenJsonArray = ref new Windows::Data::Json::JsonArray(); - tokenJsonArray->Append(Windows::Data::Json::JsonValue::CreateStringValue(ref new Platform::String(token.first.c_str()))); - tokenJsonArray->Append(Windows::Data::Json::JsonValue::CreateNumberValue(token.second)); - tokensJsonArray->Append(tokenJsonArray); - } - jsonObject->SetNamedValue(L"Tokens", tokensJsonArray); - - auto commandsJsonArray = ref new Windows::Data::Json::JsonArray(); - for (const auto& command : value.Commands) - { - commandsJsonArray->Append(SaveSnapshotToJson(command)); - } - jsonObject->SetNamedValue(L"Commands", commandsJsonArray); - - return jsonObject; - } - - static Windows::Data::Json::JsonObject ^ SaveSnapshotToJson(const CalculatorManagerSnapshot& value) - { - auto jsonObject = ref new Windows::Data::Json::JsonObject(); - if (value.HistoryItems.has_value()) - { - auto historyJsonArray = ref new Windows::Data::Json::JsonArray(); - for (const auto& item : *value.HistoryItems) - { - historyJsonArray->Append(SaveSnapshotToJson(*item)); - } - jsonObject->SetNamedValue(L"HistoryItems", historyJsonArray); - } - return jsonObject; - } - - static Windows::Data::Json::JsonObject ^ SaveSnapshotToJson(const CalculationManager::HISTORYITEM& value) - { - auto jsonObject = ref new Windows::Data::Json::JsonObject(); - jsonObject->SetNamedValue(L"Expression", Windows::Data::Json::JsonValue::CreateStringValue(ref new Platform::String(value.historyItemVector.expression.c_str()))); - jsonObject->SetNamedValue(L"Result", Windows::Data::Json::JsonValue::CreateStringValue(ref new Platform::String(value.historyItemVector.result.c_str()))); - - auto tokensJsonArray = ref new Windows::Data::Json::JsonArray(); - for (const auto& token : *value.historyItemVector.spTokens) - { - auto tokenJsonArray = ref new Windows::Data::Json::JsonArray(); - tokenJsonArray->Append(Windows::Data::Json::JsonValue::CreateStringValue(ref new Platform::String(token.first.c_str()))); - tokenJsonArray->Append(Windows::Data::Json::JsonValue::CreateNumberValue(token.second)); - tokensJsonArray->Append(tokenJsonArray); - } - jsonObject->SetNamedValue(L"Tokens", tokensJsonArray); - - auto commandsJsonArray = ref new Windows::Data::Json::JsonArray(); - for (const auto& command : *value.historyItemVector.spCommands) - { - commandsJsonArray->Append(SaveSnapshotToJson(command)); - } - jsonObject->SetNamedValue(L"Commands", commandsJsonArray); - - return jsonObject; - } - - static Windows::Data::Json::JsonObject ^ SaveSnapshotToJson(const std::shared_ptr& value) - { - auto jsonObject = ref new Windows::Data::Json::JsonObject(); - auto opndCommand = dynamic_cast(value.get()); - if (opndCommand != nullptr) - { - jsonObject = SaveSnapshotToJson(*opndCommand); - } - auto unaryCommand = dynamic_cast(value.get()); - if (unaryCommand != nullptr) - { - jsonObject = SaveSnapshotToJson(*unaryCommand); - } - auto binaryCommand = dynamic_cast(value.get()); - if (binaryCommand != nullptr) - { - jsonObject = SaveSnapshotToJson(*binaryCommand); - } - auto parenthesesCommand = dynamic_cast(value.get()); - if (parenthesesCommand != nullptr) - { - jsonObject = SaveSnapshotToJson(*parenthesesCommand); - } - return jsonObject; - } - - static Windows::Data::Json::JsonObject ^ SaveSnapshotToJson(const COpndCommand& value) - { - auto jsonObject = ref new Windows::Data::Json::JsonObject(); - jsonObject->SetNamedValue(L"CommandType", Windows::Data::Json::JsonValue::CreateNumberValue(static_cast(value.GetCommandType()))); - jsonObject->SetNamedValue(L"IsNegative", Windows::Data::Json::JsonValue::CreateBooleanValue(value.IsNegative())); - jsonObject->SetNamedValue(L"IsDecimalPresent", Windows::Data::Json::JsonValue::CreateBooleanValue(value.IsDecimalPresent())); - jsonObject->SetNamedValue(L"IsSciFmt", Windows::Data::Json::JsonValue::CreateBooleanValue(value.IsSciFmt())); - auto commandsJsonArray = ref new Windows::Data::Json::JsonArray(); - for (const auto& command : *value.GetCommands()) - { - commandsJsonArray->Append(Windows::Data::Json::JsonValue::CreateNumberValue(command)); - } - jsonObject->SetNamedValue(L"Commands", commandsJsonArray); - return jsonObject; - } - - static Windows::Data::Json::JsonObject ^ SaveSnapshotToJson(const CUnaryCommand& value) - { - auto jsonObject = ref new Windows::Data::Json::JsonObject(); - jsonObject->SetNamedValue(L"CommandType", Windows::Data::Json::JsonValue::CreateNumberValue(static_cast(value.GetCommandType()))); - auto commandsJsonArray = ref new Windows::Data::Json::JsonArray(); - for (const auto& command : *value.GetCommands()) - { - commandsJsonArray->Append(Windows::Data::Json::JsonValue::CreateNumberValue(command)); - } - jsonObject->SetNamedValue(L"Commands", commandsJsonArray); - return jsonObject; - } - - static Windows::Data::Json::JsonObject ^ SaveSnapshotToJson(const CBinaryCommand& value) - { - auto jsonObject = ref new Windows::Data::Json::JsonObject(); - jsonObject->SetNamedValue(L"CommandType", Windows::Data::Json::JsonValue::CreateNumberValue(static_cast(value.GetCommandType()))); - jsonObject->SetNamedValue(L"Command", Windows::Data::Json::JsonValue::CreateNumberValue(value.GetCommand())); - return jsonObject; - } - - static Windows::Data::Json::JsonObject ^ SaveSnapshotToJson(const CParentheses& value) - { - auto jsonObject = ref new Windows::Data::Json::JsonObject(); - jsonObject->SetNamedValue(L"CommandType", Windows::Data::Json::JsonValue::CreateNumberValue(static_cast(value.GetCommandType()))); - jsonObject->SetNamedValue(L"Command", Windows::Data::Json::JsonValue::CreateNumberValue(value.GetCommand())); - return jsonObject; - } - - static void RestoreJsonToSnapshot(Windows::Data::Json::JsonObject ^ jsonObject, std::optional& value) - { - ApplicationSnapshot applicationSnapshot; - applicationSnapshot.SnapshotVersion = static_cast(jsonObject->GetNamedNumber(L"SnapshotVersion")); - if (applicationSnapshot.SnapshotVersion > SnapshotVersion) - { - return; - } - applicationSnapshot.Mode = static_cast(jsonObject->GetNamedNumber(L"Mode")); - if (jsonObject->HasKey(L"StandardCalculatorSnapshot")) - { - std::optional standardCalculatorSnapshot; - RestoreJsonToSnapshot(jsonObject->GetNamedObject(L"StandardCalculatorSnapshot"), standardCalculatorSnapshot); - if (standardCalculatorSnapshot.has_value()) - { - applicationSnapshot.StandardCalc = std::move(*standardCalculatorSnapshot); - } - else - { - return; - } - } - value = std::move(applicationSnapshot); - } - - static void RestoreJsonToSnapshot(Windows::Data::Json::JsonObject ^ jsonObject, std::optional& value) - { - StandardCalculatorSnapshot standardCalculatorSnapshot; - std::optional calcManagerSnapshot; - RestoreJsonToSnapshot(jsonObject->GetNamedObject(L"CalculatorManagerSnapshot"), calcManagerSnapshot); - if (calcManagerSnapshot.has_value()) - { - standardCalculatorSnapshot.CalcManager = std::move(*calcManagerSnapshot); - } - else - { - return; - } - std::optional primaryDisplaySnapshot; - RestoreJsonToSnapshot(jsonObject->GetNamedObject(L"PrimaryDisplay"), primaryDisplaySnapshot); - if (primaryDisplaySnapshot.has_value()) - { - standardCalculatorSnapshot.PrimaryDisplay = std::move(*primaryDisplaySnapshot); - } - else - { - return; - } - if (jsonObject->HasKey(L"ExpressionDisplay")) - { - std::optional expressionDisplaySnapshot; - RestoreJsonToSnapshot(jsonObject->GetNamedObject(L"ExpressionDisplay"), expressionDisplaySnapshot); - if (expressionDisplaySnapshot.has_value()) - { - standardCalculatorSnapshot.ExpressionDisplay = std::move(*expressionDisplaySnapshot); - } - else - { - return; - } - } - standardCalculatorSnapshot.DisplayCommands = RestoreExpressionCommandsFromJsonArray(jsonObject->GetNamedArray(L"DisplayCommands")); - value = std::move(standardCalculatorSnapshot); - } - - static void RestoreJsonToSnapshot(Windows::Data::Json::JsonObject ^ jsonObject, std::optional& value) - { - value = PrimaryDisplaySnapshot{ jsonObject->GetNamedString(L"DisplayValue"), jsonObject->GetNamedBoolean(L"IsError") }; - } - - static void RestoreJsonToSnapshot(Windows::Data::Json::JsonObject ^ jsonObject, std::optional& value) - { - ExpressionDisplaySnapshot expressionDisplaySnapshot; - expressionDisplaySnapshot.Tokens = RestoreExpressionTokensFromJsonArray(jsonObject->GetNamedArray(L"Tokens")); - if (expressionDisplaySnapshot.Tokens.empty()) - { - return; - } - expressionDisplaySnapshot.Commands = RestoreExpressionCommandsFromJsonArray(jsonObject->GetNamedArray(L"Commands")); - if (expressionDisplaySnapshot.Commands.empty()) - { - return; - } - value = std::move(expressionDisplaySnapshot); - } - - static void RestoreJsonToSnapshot(Windows::Data::Json::JsonObject ^ jsonObject, std::optional& value) - { - CalculatorManagerSnapshot calcManagerSnapshot; - if (jsonObject->HasKey(L"HistoryItems")) - { - std::vector> historyItems; - auto historyJsonArray = jsonObject->GetNamedArray(L"HistoryItems"); - for (uint32_t i = 0; i < historyJsonArray->Size; ++i) - { - std::optional historyItem; - RestoreJsonToSnapshot(historyJsonArray->GetObjectAt(i), historyItem); - if (historyItem.has_value()) - { - historyItems.push_back(std::make_shared(*historyItem)); - } - else - { - return; - } - } - calcManagerSnapshot.HistoryItems = std::move(historyItems); - } - value = std::move(calcManagerSnapshot); - } - - static void RestoreJsonToSnapshot(Windows::Data::Json::JsonObject ^ jsonObject, std::optional& value) - { - CalculationManager::HISTORYITEM historyItem; - historyItem.historyItemVector.expression = std::wstring(jsonObject->GetNamedString(L"Expression")->Data()); - historyItem.historyItemVector.result = std::wstring(jsonObject->GetNamedString(L"Result")->Data()); - historyItem.historyItemVector.spTokens = - std::make_shared>>(RestoreExpressionTokensFromJsonArray(jsonObject->GetNamedArray(L"Tokens"))); - if (historyItem.historyItemVector.spTokens->empty()) - { - return; - } - historyItem.historyItemVector.spCommands = std::make_shared>>( - RestoreExpressionCommandsFromJsonArray(jsonObject->GetNamedArray(L"Commands"))); - if (historyItem.historyItemVector.spCommands->empty()) - { - return; - } - value = std::move(historyItem); - } - - static void RestoreJsonToSnapshot(Windows::Data::Json::JsonObject ^ jsonObject, std::optional>& value) - { - auto commandType = static_cast(jsonObject->GetNamedNumber(L"CommandType")); - switch (commandType) - { - case CalculationManager::CommandType::OperandCommand: - { - std::optional opndCommand; - RestoreJsonToSnapshot(jsonObject, opndCommand); - if (opndCommand.has_value()) - { - value = std::make_shared(*opndCommand); - } - break; - } - case CalculationManager::CommandType::UnaryCommand: - { - std::optional unaryCommand; - RestoreJsonToSnapshot(jsonObject, unaryCommand); - if (unaryCommand.has_value()) - { - value = std::make_shared(*unaryCommand); - } - break; - } - case CalculationManager::CommandType::BinaryCommand: - { - std::optional binaryCommand; - RestoreJsonToSnapshot(jsonObject, binaryCommand); - if (binaryCommand.has_value()) - { - value = std::make_shared(*binaryCommand); - } - break; - } - case CalculationManager::CommandType::Parentheses: - { - std::optional parenthesesCommand; - RestoreJsonToSnapshot(jsonObject, parenthesesCommand); - if (parenthesesCommand.has_value()) - { - value = std::make_shared(*parenthesesCommand); - } - break; - } - default: - throw std::logic_error{ "c8cba597-dfec-447a-bd1c-e78a9ffaad95" }; - } - } - - static void RestoreJsonToSnapshot(Windows::Data::Json::JsonObject ^ jsonObject, std::optional& value) - { - auto isNegative = jsonObject->GetNamedBoolean(L"IsNegative"); - auto isDecimalPresent = jsonObject->GetNamedBoolean(L"IsDecimalPresent"); - auto isSciFmt = jsonObject->GetNamedBoolean(L"IsSciFmt"); - std::vector commands; - auto commandsJsonArray = jsonObject->GetNamedArray(L"Commands"); - for (uint32_t i = 0; i < commandsJsonArray->Size; ++i) - { - commands.push_back(static_cast(commandsJsonArray->GetNumberAt(i))); - } - value = COpndCommand(std::make_shared>(std::move(commands)), isNegative, isDecimalPresent, isSciFmt); - } - - static void RestoreJsonToSnapshot(Windows::Data::Json::JsonObject ^ jsonObject, std::optional& value) - { - std::vector commands; - auto commandsJsonArray = jsonObject->GetNamedArray(L"Commands"); - if (commandsJsonArray->Size == 1) - { - value = CUnaryCommand(static_cast(commandsJsonArray->GetNumberAt(0))); - } - else if (commandsJsonArray->Size == 2) - { - value = CUnaryCommand(static_cast(commandsJsonArray->GetNumberAt(0)), static_cast(commandsJsonArray->GetNumberAt(1))); - } - } - - static void RestoreJsonToSnapshot(Windows::Data::Json::JsonObject ^ jsonObject, std::optional& value) - { - value = CBinaryCommand(static_cast(jsonObject->GetNamedNumber(L"Command"))); - } - - static void RestoreJsonToSnapshot(Windows::Data::Json::JsonObject ^ jsonObject, std::optional& value) - { - value = CParentheses(static_cast(jsonObject->GetNamedNumber(L"Command"))); - } - - static std::vector> RestoreExpressionTokensFromJsonArray(Windows::Data::Json::JsonArray ^ jsonArray) - { - std::vector> tokens; - for (uint32_t i = 0; i < jsonArray->Size; ++i) - { - auto tokenJsonArray = jsonArray->GetArrayAt(i); - if (tokenJsonArray->Size == 2 && tokenJsonArray->GetAt(0)->ValueType == Windows::Data::Json::JsonValueType::String - && tokenJsonArray->GetAt(1)->ValueType == Windows::Data::Json::JsonValueType::Number) - { - tokens.emplace_back(std::wstring(tokenJsonArray->GetAt(0)->GetString()->Data()), static_cast(tokenJsonArray->GetAt(1)->GetNumber())); - } - else - { - return {}; - } - } - return tokens; - } - - static std::vector> RestoreExpressionCommandsFromJsonArray(Windows::Data::Json::JsonArray ^ jsonArray) - { - std::vector> commands; - for (uint32_t i = 0; i < jsonArray->Size; ++i) - { - std::optional> command; - RestoreJsonToSnapshot(jsonArray->GetObjectAt(i), command); - if (command.has_value()) - { - commands.push_back(*command); - } - else - { - return {}; - } - } - return commands; - } - - static bool IsJsonParsingException(Platform::COMException ^ e) - { - return e->HResult == WEB_E_JSON_VALUE_NOT_FOUND || e->HResult == E_ILLEGAL_METHOD_CALL; - } - }; } ApplicationViewModel::ApplicationViewModel() @@ -512,6 +72,17 @@ void ApplicationViewModel::Categories::set(IObservableVector } } +CalculatorApp::ViewModel::Snapshot::ApplicationSnapshot ^ ApplicationViewModel::Snapshot::get() +{ + auto snapshot = ref new CalculatorApp::ViewModel::Snapshot::ApplicationSnapshot(); + snapshot->Mode = static_cast(Mode); + if (m_CalculatorViewModel != nullptr && m_mode == ViewMode::Standard) + { + snapshot->StandardCalculator = m_CalculatorViewModel->Snapshot; + } + return snapshot; +} + void ApplicationViewModel::Initialize(ViewMode mode) { if (!NavCategoryStates::IsValidViewMode(mode) || !NavCategoryStates::IsViewModeEnabled(mode)) @@ -545,6 +116,15 @@ void ApplicationViewModel::Initialize(ViewMode mode) } } +void ApplicationViewModel::RestoreFromSnapshot(CalculatorApp::ViewModel::Snapshot::ApplicationSnapshot ^ snapshot) +{ + Mode = static_cast(snapshot->Mode); + if (snapshot->StandardCalculator != nullptr) + { + m_CalculatorViewModel->Snapshot = snapshot->StandardCalculator; + } +} + bool ApplicationViewModel::TryRecoverFromNavigationModeFailure() { // Here we are simply trying to recover from being unable to navigate to a mode. @@ -711,48 +291,3 @@ void ApplicationViewModel::SetDisplayNormalAlwaysOnTopOption() DisplayNormalAlwaysOnTopOption = m_mode == ViewMode::Standard && ApplicationView::GetForCurrentView()->IsViewModeSupported(ApplicationViewMode::CompactOverlay) && !IsAlwaysOnTop; } - -Windows::Data::Json::JsonObject ^ ApplicationViewModel::SaveApplicationSnapshot() -{ - ApplicationSnapshot applicationSnapshot; - applicationSnapshot.SnapshotVersion = SnapshotHelper::SnapshotVersion; - applicationSnapshot.Mode = static_cast(Mode); - if (m_CalculatorViewModel != nullptr && m_mode == ViewMode::Standard) - { - // Standard calculator is the only supported mode so far. - applicationSnapshot.StandardCalc = m_CalculatorViewModel->GetStandardCalculatorSnapshot(); - } - return SnapshotHelper::SaveSnapshotToJson(applicationSnapshot); -} - -bool ApplicationViewModel::TryRestoreFromSnapshot(Windows::Data::Json::JsonObject ^ jsonObject) -{ - std::optional applicationSnapshot; - try - { - SnapshotHelper::RestoreJsonToSnapshot(jsonObject, applicationSnapshot); - } - catch (Platform::COMException ^ e) - { - if (SnapshotHelper::IsJsonParsingException(e)) - { - return false; - } - throw; - } - - if (applicationSnapshot.has_value()) - { - Mode = static_cast(applicationSnapshot->Mode); - if (applicationSnapshot->StandardCalc.has_value()) - { - if (m_CalculatorViewModel == nullptr) - { - m_CalculatorViewModel = ref new StandardCalculatorViewModel(); - } - m_CalculatorViewModel->SetStandardCalculatorSnapshot(applicationSnapshot->StandardCalc.value()); - } - return true; - } - return false; -} diff --git a/src/CalcViewModel/ApplicationViewModel.h b/src/CalcViewModel/ApplicationViewModel.h index 542144fa..afb67eb6 100644 --- a/src/CalcViewModel/ApplicationViewModel.h +++ b/src/CalcViewModel/ApplicationViewModel.h @@ -3,6 +3,7 @@ #pragma once +#include "Snapshots.h" #include "StandardCalculatorViewModel.h" #include "DateCalculatorViewModel.h" #include "GraphingCalculator/GraphingCalculatorViewModel.h" @@ -12,19 +13,13 @@ namespace CalculatorApp { namespace ViewModel { - struct ApplicationSnapshot - { - int SnapshotVersion; - int Mode; - std::optional StandardCalc; - }; - [Windows::UI::Xaml::Data::Bindable] public ref class ApplicationViewModel sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged { public: ApplicationViewModel(); void Initialize(CalculatorApp::ViewModel::Common::ViewMode mode); // Use for first init, use deserialize for rehydration + void RestoreFromSnapshot(CalculatorApp::ViewModel::Snapshot::ApplicationSnapshot^ snapshot); OBSERVABLE_OBJECT(); OBSERVABLE_PROPERTY_RW(StandardCalculatorViewModel ^, CalculatorViewModel); @@ -77,6 +72,11 @@ namespace CalculatorApp } } + property CalculatorApp::ViewModel::Snapshot::ApplicationSnapshot ^ Snapshot + { + CalculatorApp::ViewModel::Snapshot::ApplicationSnapshot ^ get(); + } + static property Platform::String ^ LaunchedLocalSettings { Platform::String ^ get() @@ -103,9 +103,6 @@ namespace CalculatorApp void ToggleAlwaysOnTop(float width, float height); - Windows::Data::Json::JsonObject ^ SaveApplicationSnapshot(); - bool TryRestoreFromSnapshot(Windows::Data::Json::JsonObject ^ jsonObject); - private: bool TryRecoverFromNavigationModeFailure(); diff --git a/src/CalcViewModel/CalcViewModel.vcxproj b/src/CalcViewModel/CalcViewModel.vcxproj index b8869d4b..aedd8d8f 100644 --- a/src/CalcViewModel/CalcViewModel.vcxproj +++ b/src/CalcViewModel/CalcViewModel.vcxproj @@ -338,6 +338,7 @@ + @@ -380,6 +381,7 @@ Create Create + diff --git a/src/CalcViewModel/CalcViewModel.vcxproj.filters b/src/CalcViewModel/CalcViewModel.vcxproj.filters index 46ee7fd0..9de55c39 100644 --- a/src/CalcViewModel/CalcViewModel.vcxproj.filters +++ b/src/CalcViewModel/CalcViewModel.vcxproj.filters @@ -89,6 +89,7 @@ + @@ -203,6 +204,7 @@ + diff --git a/src/CalcViewModel/Common/TraceLogger.cpp b/src/CalcViewModel/Common/TraceLogger.cpp index 9d4bac78..b1017e6f 100644 --- a/src/CalcViewModel/Common/TraceLogger.cpp +++ b/src/CalcViewModel/Common/TraceLogger.cpp @@ -345,10 +345,9 @@ namespace CalculatorApp TraceLoggingCommon::GetInstance()->LogLevel2Event(StringReference(EVENT_NAME_RECALL_RESTORE), fields); } - void TraceLogger::LogRecallError(CalculatorApp::ViewModel::Common::ViewMode mode, Platform::String^ message) + void TraceLogger::LogRecallError(Platform::String^ message) { auto fields = ref new LoggingFields(); - fields->AddString(StringReference(CALC_MODE), NavCategoryStates::GetFriendlyName(mode)); fields->AddString(StringReference(L"FunctionName"), L"Recall"); fields->AddString(StringReference(L"Message"), message); TraceLoggingCommon::GetInstance()->LogLevel2Event(StringReference(EVENT_NAME_EXCEPTION), fields); diff --git a/src/CalcViewModel/Common/TraceLogger.h b/src/CalcViewModel/Common/TraceLogger.h index 25e57eea..0c70b0a9 100644 --- a/src/CalcViewModel/Common/TraceLogger.h +++ b/src/CalcViewModel/Common/TraceLogger.h @@ -87,7 +87,7 @@ namespace CalculatorApp::ViewModel::Common void LogPlatformExceptionInfo(CalculatorApp::ViewModel::Common::ViewMode mode, Platform::String ^ functionName, Platform::String ^ message, int hresult); void LogRecallSnapshot(CalculatorApp::ViewModel::Common::ViewMode mode); void LogRecallRestore(CalculatorApp::ViewModel::Common::ViewMode mode); - void LogRecallError(CalculatorApp::ViewModel::Common::ViewMode mode, Platform::String ^ message); + void LogRecallError(Platform::String ^ message); internal: void LogPlatformException(CalculatorApp::ViewModel::Common::ViewMode mode, Platform::String ^ functionName, Platform::Exception ^ e); diff --git a/src/CalcViewModel/Snapshots.cpp b/src/CalcViewModel/Snapshots.cpp new file mode 100644 index 00000000..0389afc5 --- /dev/null +++ b/src/CalcViewModel/Snapshots.cpp @@ -0,0 +1,281 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "pch.h" +#include +#include +#include + +#include "CalcManager/ExpressionCommand.h" +#include "Snapshots.h" + +namespace CalculatorApp::ViewModel::Snapshot +{ + UnaryCommand::UnaryCommand() + { + } + + UnaryCommand::UnaryCommand(std::vector cmds) + : m_cmds(std::move(cmds)) + { + } + + Windows::Foundation::Collections::IVectorView ^ UnaryCommand::Commands::get() + { + return ref new Platform::Collections::VectorView(m_cmds); + } + + void UnaryCommand::Commands::set(Windows::Foundation::Collections::IVectorView ^ commands) + { + m_cmds.clear(); + for (auto cmd : commands) + { + m_cmds.push_back(cmd); + } + } + + BinaryCommand::BinaryCommand() + { + Command = 0; + } + + BinaryCommand::BinaryCommand(int cmd) + { + Command = cmd; + } + + OperandCommand::OperandCommand() + { + IsNegative = false; + IsDecimalPresent = false; + IsSciFmt = false; + } + + OperandCommand::OperandCommand(bool isNegative, bool isDecimal, bool isSciFmt, std::vector cmds) + { + IsNegative = isNegative; + IsDecimalPresent = isDecimal; + IsSciFmt = isSciFmt; + m_cmds = std::move(cmds); + } + + Windows::Foundation::Collections::IVectorView ^ OperandCommand::Commands::get() + { + return ref new Platform::Collections::VectorView(m_cmds); + } + + void OperandCommand::Commands::set(Windows::Foundation::Collections::IVectorView ^ commands) + { + m_cmds.clear(); + for (auto cmd : commands) + { + m_cmds.push_back(cmd); + } + } + + Parentheses::Parentheses() + { + Command = 0; + } + + Parentheses::Parentheses(int cmd) + { + Command = cmd; + } + + ICalcManagerIExprCommand ^ CreateExprCommand(const IExpressionCommand* exprCmd) { + switch (exprCmd->GetCommandType()) + { + case CalculationManager::CommandType::UnaryCommand: + { + auto cmd = static_cast(exprCmd); + std::vector cmdlist; + for (auto& subcmd : *cmd->GetCommands()) + { + cmdlist.push_back(subcmd); + } + return ref new UnaryCommand(std::move(cmdlist)); + } + case CalculationManager::CommandType::BinaryCommand: + { + auto cmd = static_cast(exprCmd); + return ref new BinaryCommand(cmd->GetCommand()); + } + case CalculationManager::CommandType::OperandCommand: + { + auto cmd = static_cast(exprCmd); + std::vector cmdlist; + for (auto& subcmd : *cmd->GetCommands()) + { + cmdlist.push_back(subcmd); + } + return ref new OperandCommand(cmd->IsNegative(), cmd->IsDecimalPresent(), cmd->IsSciFmt(), std::move(cmdlist)); + } + case CalculationManager::CommandType::Parentheses: + { + auto cmd = static_cast(exprCmd); + return ref new Parentheses(cmd->GetCommand()); + } + default: + throw std::logic_error{ "unhandled command type." }; + } + } + + CalcManagerToken::CalcManagerToken() + { + OpCodeName = ref new Platform::String(); + CommandIndex = 0; + } + + CalcManagerToken::CalcManagerToken(Platform::String ^ opCodeName, int cmdIndex) + { + assert(opCodeName != nullptr && "opCodeName is mandatory."); + OpCodeName = opCodeName; + CommandIndex = cmdIndex; + } + + CalcManagerHistoryItem::CalcManagerHistoryItem() + { + Tokens = ref new Platform::Collections::Vector(); + Commands = ref new Platform::Collections::Vector(); + Expression = ref new Platform::String(); + Result = ref new Platform::String(); + } + + CalcManagerHistoryItem::CalcManagerHistoryItem(const CalculationManager::HISTORYITEM& item) + { + Tokens = ref new Platform::Collections::Vector(); + assert(item.historyItemVector.spTokens != nullptr && "spTokens shall not be null."); + for (auto& [opCode, cmdIdx] : *item.historyItemVector.spTokens) + { + Tokens->Append(ref new CalcManagerToken(ref new Platform::String(opCode.c_str()), cmdIdx)); + } + Commands = ref new Platform::Collections::Vector(); + assert(item.historyItemVector.spCommands != nullptr && "spCommands shall not be null."); + for (auto& cmd : *item.historyItemVector.spCommands) + { + Commands->Append(CreateExprCommand(cmd.get())); + } + Expression = ref new Platform::String(item.historyItemVector.expression.c_str()); + Result = ref new Platform::String(item.historyItemVector.result.c_str()); + } + + CalcManagerSnapshot::CalcManagerSnapshot() + { + HistoryItems = nullptr; + } + + CalcManagerSnapshot::CalcManagerSnapshot(const CalculationManager::CalculatorManager& calcMgr) + { + auto& items = calcMgr.GetHistoryItems(); + if (!items.empty()) + { + HistoryItems = ref new Platform::Collections::Vector(); + for (auto& item : items) + { + HistoryItems->Append(ref new CalcManagerHistoryItem(*item)); + } + } + } + + PrimaryDisplaySnapshot::PrimaryDisplaySnapshot() + { + DisplayValue = ref new Platform::String(); + IsError = false; + } + + PrimaryDisplaySnapshot::PrimaryDisplaySnapshot(Platform::String ^ display, bool isError) + { + assert(display != nullptr && "display is mandatory"); + DisplayValue = display; + IsError = isError; + } + + ExpressionDisplaySnapshot::ExpressionDisplaySnapshot() + { + Tokens = ref new Platform::Collections::Vector(); + Commands = ref new Platform::Collections::Vector(); + } + + ExpressionDisplaySnapshot::ExpressionDisplaySnapshot( + const std::vector& tokens, + const std::vector>& commands) + { + Tokens = ref new Platform::Collections::Vector(); + for (auto& [opCode, cmdIdx] : tokens) + { + Tokens->Append(ref new CalcManagerToken(ref new Platform::String(opCode.c_str()), cmdIdx)); + } + + Commands = ref new Platform::Collections::Vector(); + for (auto& cmd : commands) + { + Commands->Append(CreateExprCommand(cmd.get())); + } + } + + StandardCalculatorSnapshot::StandardCalculatorSnapshot() + { + CalcManager = ref new CalcManagerSnapshot(); + PrimaryDisplay = ref new PrimaryDisplaySnapshot(); + ExpressionDisplay = nullptr; + DisplayCommands = ref new Platform::Collections::Vector(); + } + + std::vector> ToUnderlying(Windows::Foundation::Collections::IVector ^ items) + { + std::vector> result; + for (CalcManagerHistoryItem ^ item : items) + { + CalculationManager::HISTORYITEMVECTOR nativeItem; + nativeItem.spTokens = std::make_shared>>(); + for (CalcManagerToken ^ token : item->Tokens) + { + nativeItem.spTokens->push_back(std::make_pair(token->OpCodeName->Data(), token->CommandIndex)); + } + nativeItem.spCommands = std::make_shared>>(ToUnderlying(item->Commands)); + nativeItem.expression = item->Expression->Data(); + nativeItem.result = item->Result->Data(); + auto spItem = std::make_shared(CalculationManager::HISTORYITEM{ std::move(nativeItem) }); + result.push_back(std::move(std::move(spItem))); + } + return result; + } + + std::vector> ToUnderlying(Windows::Foundation::Collections::IVector ^ commands) + { + std::vector> result; + for (ICalcManagerIExprCommand ^ cmdEntry : commands) + { + if (auto unary = dynamic_cast(cmdEntry); unary != nullptr) + { + if (unary->m_cmds.size() == 1) + { + result.push_back(std::make_shared(unary->m_cmds[0])); + } + else if (unary->m_cmds.size() == 2) + { + result.push_back(std::make_shared(unary->m_cmds[0], unary->m_cmds[1])); + } + else + { + throw std::logic_error{ "ill-formed command." }; + } + } + else if (auto binary = dynamic_cast(cmdEntry); binary != nullptr) + { + result.push_back(std::make_shared(binary->Command)); + } + else if (auto paren = dynamic_cast(cmdEntry); paren != nullptr) + { + result.push_back(std::make_shared(paren->Command)); + } + else if (auto operand = dynamic_cast(cmdEntry); operand != nullptr) + { + auto subcmds = std::make_shared>(operand->m_cmds); + result.push_back(std::make_shared(std::move(subcmds), operand->IsNegative, operand->IsDecimalPresent, operand->IsSciFmt)); + } + } + return result; + } +} // namespace CalculatorApp::ViewModel diff --git a/src/CalcViewModel/Snapshots.h b/src/CalcViewModel/Snapshots.h new file mode 100644 index 00000000..65afd4e4 --- /dev/null +++ b/src/CalcViewModel/Snapshots.h @@ -0,0 +1,156 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#pragma once +#include +#include + +#include "CalcManager/CalculatorManager.h" + +namespace CalculatorApp::ViewModel::Snapshot +{ +public + interface struct ICalcManagerIExprCommand + { + }; + +public + ref struct UnaryCommand sealed : public ICalcManagerIExprCommand + { + property Windows::Foundation::Collections::IVectorView ^ Commands { + Windows::Foundation::Collections::IVectorView ^ get(); + void set(Windows::Foundation::Collections::IVectorView ^ commands); + }; + + UnaryCommand(); + + internal :; + explicit UnaryCommand(std::vector cmds); + std::vector m_cmds; + }; + +public + ref struct BinaryCommand sealed : public ICalcManagerIExprCommand + { + property int Command; + + BinaryCommand(); + + internal :; + explicit BinaryCommand(int cmd); + }; + +public + ref struct OperandCommand sealed : public ICalcManagerIExprCommand + { + property bool IsNegative; + property bool IsDecimalPresent; + property bool IsSciFmt; + property Windows::Foundation::Collections::IVectorView ^ Commands { + Windows::Foundation::Collections::IVectorView ^ get(); + void set(Windows::Foundation::Collections::IVectorView ^ commands); + }; + + OperandCommand(); + + internal :; + explicit OperandCommand(bool isNegative, bool isDecimal, bool isSciFmt, std::vector cmds); + std::vector m_cmds; + }; + +public + ref struct Parentheses sealed : public ICalcManagerIExprCommand + { + property int Command; + + Parentheses(); + + internal :; + explicit Parentheses(int cmd); + }; + +public + ref struct CalcManagerToken sealed + { + property Platform::String ^ OpCodeName; // mandatory + property int CommandIndex; + + CalcManagerToken(); + + internal :; + explicit CalcManagerToken(Platform::String ^ opCodeName, int cmdIndex); + }; + +public + ref struct CalcManagerHistoryItem sealed + { + property Windows::Foundation::Collections::IVector ^ Tokens; // mandatory + property Windows::Foundation::Collections::IVector ^ Commands; // mandatory + property Platform::String ^ Expression; // mandatory + property Platform::String ^ Result; // mandatory + + CalcManagerHistoryItem(); + + internal :; + explicit CalcManagerHistoryItem(const CalculationManager::HISTORYITEM& item); + }; + +public + ref struct CalcManagerSnapshot sealed + { + property Windows::Foundation::Collections::IVector ^ HistoryItems; // optional + + CalcManagerSnapshot(); + + internal :; + explicit CalcManagerSnapshot(const CalculationManager::CalculatorManager& calcMgr); + }; + +public + ref struct PrimaryDisplaySnapshot sealed + { + property Platform::String ^ DisplayValue; // mandatory + property bool IsError; + + PrimaryDisplaySnapshot(); + + internal :; + explicit PrimaryDisplaySnapshot(Platform::String ^ display, bool isError); + }; + +public + ref struct ExpressionDisplaySnapshot sealed + { + property Windows::Foundation::Collections::IVector ^ Tokens; + property Windows::Foundation::Collections::IVector ^ Commands; + + ExpressionDisplaySnapshot(); + + internal :; + using CalcHistoryToken = std::pair; + explicit ExpressionDisplaySnapshot(const std::vector& tokens, const std::vector>& commands); + }; + +public + ref struct StandardCalculatorSnapshot sealed + { + property CalcManagerSnapshot ^ CalcManager; // mandatory + property PrimaryDisplaySnapshot ^ PrimaryDisplay; // mandatory + property ExpressionDisplaySnapshot ^ ExpressionDisplay; // optional + property Windows::Foundation::Collections::IVector ^ DisplayCommands; // mandatory + + StandardCalculatorSnapshot(); + }; + +public + ref struct ApplicationSnapshot sealed + { + property int Mode; + property StandardCalculatorSnapshot ^ StandardCalculator; // optional + }; + + ICalcManagerIExprCommand ^ CreateExprCommand(const IExpressionCommand* exprCmd); + std::vector> ToUnderlying(Windows::Foundation::Collections::IVector ^ commands); + std::vector> ToUnderlying(Windows::Foundation::Collections::IVector ^ items); + +} // namespace CalculatorApp::ViewModel diff --git a/src/CalcViewModel/StandardCalculatorViewModel.cpp b/src/CalcViewModel/StandardCalculatorViewModel.cpp index 04735680..ef440992 100644 --- a/src/CalcViewModel/StandardCalculatorViewModel.cpp +++ b/src/CalcViewModel/StandardCalculatorViewModel.cpp @@ -227,7 +227,7 @@ String ^ StandardCalculatorViewModel::CalculateNarratorDisplayValue(_In_ wstring String ^ StandardCalculatorViewModel::GetNarratorStringReadRawNumbers(_In_ String ^ localizedDisplayValue) { wstring ws; - LocalizationSettings^ locSettings = LocalizationSettings::GetInstance(); + LocalizationSettings ^ locSettings = LocalizationSettings::GetInstance(); // Insert a space after each digit in the string, to force Narrator to read them as separate numbers. for (const wchar_t& c : localizedDisplayValue) @@ -386,7 +386,7 @@ void StandardCalculatorViewModel::SetTokens(_Inout_ shared_ptr& newMemorizedNumbers) { - LocalizationSettings^ localizer = LocalizationSettings::GetInstance(); + LocalizationSettings ^ localizer = LocalizationSettings::GetInstance(); if (newMemorizedNumbers.size() == 0) // Memory has been cleared { MemorizedNumbers->Clear(); @@ -1060,8 +1060,8 @@ ButtonInfo StandardCalculatorViewModel::MapCharacterToButtonId(char16 ch) { if (LocalizationSettings::GetInstance()->IsLocalizedDigit(ch)) { - result.buttonId = - NumbersAndOperatorsEnum::Zero + static_cast(ch - LocalizationSettings::GetInstance()->GetDigitSymbolFromEnUsDigit('0')); + result.buttonId = NumbersAndOperatorsEnum::Zero + + static_cast(ch - LocalizationSettings::GetInstance()->GetDigitSymbolFromEnUsDigit('0')); result.canSendNegate = true; } } @@ -1588,7 +1588,7 @@ void StandardCalculatorViewModel::UpdateProgrammerPanelDisplay() binaryDisplayString = m_standardCalculatorManager.GetResultForRadix(2, precision, true); } } - LocalizationSettings^ localizer = LocalizationSettings::GetInstance(); + LocalizationSettings ^ localizer = LocalizationSettings::GetInstance(); binaryDisplayString = AddPadding(binaryDisplayString); localizer->LocalizeDisplayValue(&hexDisplayString); @@ -1787,49 +1787,56 @@ void StandardCalculatorViewModel::SetBitshiftRadioButtonCheckedAnnouncement(Plat Announcement = CalculatorAnnouncement::GetBitShiftRadioButtonCheckedAnnouncement(announcement); } -StandardCalculatorSnapshot StandardCalculatorViewModel::GetStandardCalculatorSnapshot() const +CalculatorApp::ViewModel::Snapshot::StandardCalculatorSnapshot ^ StandardCalculatorViewModel::Snapshot::get() { - StandardCalculatorSnapshot snapshot; - auto& historyItems = m_standardCalculatorManager.GetHistoryItems(); - if (!historyItems.empty()) - { - snapshot.CalcManager.HistoryItems = std::move(historyItems); - } - snapshot.PrimaryDisplay = PrimaryDisplaySnapshot{ m_DisplayValue, m_IsInError }; + auto result = ref new CalculatorApp::ViewModel::Snapshot::StandardCalculatorSnapshot(); + result->CalcManager = ref new CalculatorApp::ViewModel::Snapshot::CalcManagerSnapshot(m_standardCalculatorManager); + result->PrimaryDisplay = ref new CalculatorApp::ViewModel::Snapshot::PrimaryDisplaySnapshot(m_DisplayValue, m_IsInError); if (!m_tokens->empty() && !m_commands->empty()) { - snapshot.ExpressionDisplay = { *m_tokens, *m_commands }; + result->ExpressionDisplay = ref new CalculatorApp::ViewModel::Snapshot::ExpressionDisplaySnapshot(*m_tokens, *m_commands); } - snapshot.DisplayCommands = m_standardCalculatorManager.GetDisplayCommandsSnapshot(); - return snapshot; + result->DisplayCommands = ref new Platform::Collections::Vector(); + for (auto cmd : m_standardCalculatorManager.GetDisplayCommandsSnapshot()) + { + result->DisplayCommands->Append(CalculatorApp::ViewModel::Snapshot::CreateExprCommand(cmd.get())); + } + return result; } -void StandardCalculatorViewModel::SetStandardCalculatorSnapshot(const StandardCalculatorSnapshot& snapshot) +void CalculatorApp::ViewModel::StandardCalculatorViewModel::Snapshot::set(CalculatorApp::ViewModel::Snapshot::StandardCalculatorSnapshot ^ snapshot) { - if (snapshot.CalcManager.HistoryItems.has_value()) + assert(snapshot != nullptr); + m_standardCalculatorManager.Reset(); + if (snapshot->CalcManager->HistoryItems != nullptr) { - m_standardCalculatorManager.SetHistoryItems(snapshot.CalcManager.HistoryItems.value()); + m_standardCalculatorManager.SetHistoryItems(ToUnderlying(snapshot->CalcManager->HistoryItems)); } std::vector commands; - if (snapshot.ExpressionDisplay.has_value() && snapshot.ExpressionDisplay->Tokens.back().first == L"=") + if (snapshot->ExpressionDisplay != nullptr && snapshot->ExpressionDisplay->Tokens->GetAt(snapshot->ExpressionDisplay->Tokens->Size - 1)->OpCodeName == L"=") { - commands = GetCommandsFromExpressionCommands(snapshot.ExpressionDisplay->Commands); + commands = GetCommandsFromExpressionCommands(ToUnderlying(snapshot->ExpressionDisplay->Commands)); } - if (commands.empty() && !snapshot.DisplayCommands.empty()) + if (commands.empty() && snapshot->DisplayCommands->Size > 0) { - commands = GetCommandsFromExpressionCommands(snapshot.DisplayCommands); + commands = GetCommandsFromExpressionCommands(ToUnderlying(snapshot->DisplayCommands)); } - for (const auto& command : commands) + for (auto cmd : commands) { - m_standardCalculatorManager.SendCommand(static_cast(command)); + m_standardCalculatorManager.SendCommand(static_cast(cmd)); } - - if (snapshot.ExpressionDisplay.has_value()) + if (snapshot->ExpressionDisplay != nullptr) { + using RawTokenCollection = std::vector>; + RawTokenCollection rawTokens; + for (CalculatorApp::ViewModel::Snapshot::CalcManagerToken ^ token : snapshot->ExpressionDisplay->Tokens) + { + rawTokens.push_back(std::pair{ token->OpCodeName->Data(), token->CommandIndex }); + } SetExpressionDisplay( - std::make_shared>>(snapshot.ExpressionDisplay->Tokens), - std::make_shared>>(snapshot.ExpressionDisplay->Commands)); + std::make_shared(rawTokens), + std::make_shared>>(ToUnderlying(snapshot->ExpressionDisplay->Commands))); } - SetPrimaryDisplay(snapshot.PrimaryDisplay.DisplayValue, snapshot.PrimaryDisplay.IsError); + SetPrimaryDisplay(snapshot->PrimaryDisplay->DisplayValue, snapshot->PrimaryDisplay->IsError); } diff --git a/src/CalcViewModel/StandardCalculatorViewModel.h b/src/CalcViewModel/StandardCalculatorViewModel.h index 160a7fe8..65cf7eb6 100644 --- a/src/CalcViewModel/StandardCalculatorViewModel.h +++ b/src/CalcViewModel/StandardCalculatorViewModel.h @@ -7,11 +7,13 @@ #include "Common/CalculatorDisplay.h" #include "Common/EngineResourceProvider.h" #include "Common/CalculatorButtonUser.h" -#include "HistoryViewModel.h" -#include "MemoryItemViewModel.h" #include "Common/BitLength.h" #include "Common/NumberBase.h" +#include "HistoryViewModel.h" +#include "MemoryItemViewModel.h" +#include "Snapshots.h" + namespace CalculatorUnitTests { class MultiWindowUnitTests; @@ -25,43 +27,19 @@ namespace CalculatorApp namespace ViewModel { #define ASCII_0 48 - public delegate void HideMemoryClickedHandler(); + public + delegate void HideMemoryClickedHandler(); - public value struct ButtonInfo + public + value struct ButtonInfo { CalculatorApp::ViewModel::Common::NumbersAndOperatorsEnum buttonId; bool canSendNegate; }; - struct CalculatorManagerSnapshot - { - std::optional>> HistoryItems; - }; - - struct PrimaryDisplaySnapshot - { - Platform::String ^ DisplayValue; - bool IsError = false; - }; - - struct ExpressionDisplaySnapshot - { - std::vector> Tokens; - std::vector> Commands; - }; - - struct StandardCalculatorSnapshot - { - CalculatorManagerSnapshot CalcManager; - PrimaryDisplaySnapshot PrimaryDisplay; - std::optional ExpressionDisplay; - std::vector> DisplayCommands; - }; - [Windows::UI::Xaml::Data::Bindable] public ref class StandardCalculatorViewModel sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged { public: - StandardCalculatorViewModel(); void UpdateOperand(int pos, Platform::String ^ text); OBSERVABLE_OBJECT_CALLBACK(OnPropertyChanged); @@ -136,7 +114,7 @@ namespace CalculatorApp static property Platform::String ^ IsBitFlipCheckedPropertyName { Platform::String ^ get() { return Platform::StringReference(L"IsBitFlipChecked"); } } - property CalculatorApp::ViewModel::Common::BitLength ValueBitLength + property CalculatorApp::ViewModel::Common::BitLength ValueBitLength { CalculatorApp::ViewModel::Common::BitLength get() { @@ -216,7 +194,7 @@ namespace CalculatorApp static property Platform::String ^ IsProgrammerPropertyName { Platform::String ^ get() { return Platform::StringReference(L"IsProgrammer"); } } - property bool IsEditingEnabled + property bool IsEditingEnabled { bool get() { @@ -265,6 +243,11 @@ namespace CalculatorApp } } + property CalculatorApp::ViewModel::Snapshot::StandardCalculatorSnapshot ^ Snapshot { + CalculatorApp::ViewModel::Snapshot::StandardCalculatorSnapshot ^ get(); + void set(CalculatorApp::ViewModel::Snapshot::StandardCalculatorSnapshot ^ snapshot); + }; + // Used by unit tests void ResetCalcManager(bool clearMemory); void SendCommandToCalcManager(int command); @@ -284,8 +267,7 @@ namespace CalculatorApp void SwitchAngleType(CalculatorApp::ViewModel::Common::NumbersAndOperatorsEnum num); void FtoEButtonToggled(); - internal: - void OnPaste(Platform::String ^ pastedString); + internal : void OnPaste(Platform::String ^ pastedString); void OnCopyCommand(Platform::Object ^ parameter); void OnPasteCommand(Platform::Object ^ parameter); @@ -307,9 +289,9 @@ namespace CalculatorApp Platform::String ^ GetRawDisplayValue(); void Recalculate(bool fromHistory = false); bool IsOperator(CalculationManager::Command cmdenum); - void SetMemorizedNumbersString(); + void SetMemorizedNumbersString(); void ResetRadixAndUpdateMemory(bool resetRadix); - + void SetPrecision(int32_t precision); void UpdateMaxIntDigits() { @@ -320,9 +302,9 @@ namespace CalculatorApp return m_CurrentAngleType; } - StandardCalculatorSnapshot GetStandardCalculatorSnapshot() const; - void SetStandardCalculatorSnapshot(const StandardCalculatorSnapshot& state); - + internal :; + explicit StandardCalculatorViewModel(); + private: void SetMemorizedNumbers(const std::vector& memorizedNumbers); void UpdateProgrammerPanelDisplay(); @@ -372,8 +354,7 @@ namespace CalculatorApp Common::DisplayExpressionToken ^ m_selectedExpressionToken; Platform::String ^ LocalizeDisplayValue(_In_ std::wstring const& displayValue); - Platform::String - ^ CalculateNarratorDisplayValue(_In_ std::wstring const& displayValue, _In_ Platform::String ^ localizedDisplayValue); + Platform::String ^ CalculateNarratorDisplayValue(_In_ std::wstring const& displayValue, _In_ Platform::String ^ localizedDisplayValue); CalculatorApp::ViewModel::Common::Automation::NarratorAnnouncement ^ GetDisplayUpdatedNarratorAnnouncement(); Platform::String ^ GetCalculatorExpressionAutomationName(); Platform::String ^ GetNarratorStringReadRawNumbers(_In_ Platform::String ^ localizedDisplayValue); diff --git a/src/CalcViewModelCopyForUT/CalcViewModelCopyForUT.vcxproj b/src/CalcViewModelCopyForUT/CalcViewModelCopyForUT.vcxproj index 7aef42f5..ed1a9fb1 100644 --- a/src/CalcViewModelCopyForUT/CalcViewModelCopyForUT.vcxproj +++ b/src/CalcViewModelCopyForUT/CalcViewModelCopyForUT.vcxproj @@ -333,6 +333,7 @@ + @@ -375,6 +376,7 @@ Create Create + @@ -392,6 +394,14 @@ /DUSE_MOCK_DATA %(AdditionalOptions) + stdcpp17 + stdcpp17 + stdcpp17 + stdcpp17 + stdcpp17 + stdcpp17 + stdcpp17 + stdcpp17 diff --git a/src/CalcViewModelCopyForUT/CalcViewModelCopyForUT.vcxproj.filters b/src/CalcViewModelCopyForUT/CalcViewModelCopyForUT.vcxproj.filters index 19ca2cee..2691d534 100644 --- a/src/CalcViewModelCopyForUT/CalcViewModelCopyForUT.vcxproj.filters +++ b/src/CalcViewModelCopyForUT/CalcViewModelCopyForUT.vcxproj.filters @@ -87,6 +87,7 @@ GraphingCalculator + @@ -204,6 +205,7 @@ Common + diff --git a/src/Calculator/App.xaml.cs b/src/Calculator/App.xaml.cs index 342e6851..13fe4a6f 100644 --- a/src/Calculator/App.xaml.cs +++ b/src/Calculator/App.xaml.cs @@ -73,24 +73,19 @@ namespace CalculatorApp protected override void OnActivated(IActivatedEventArgs args) { - if (args.Kind != ActivationKind.Protocol) return; - - if (args.IsSnapshotProtocol()) + if (args.Kind != ActivationKind.Protocol) { - var protoArgs = (IProtocolActivatedEventArgs)args; - OnAppLaunch(args, - new SnapshotLaunchArguments - { - ActivityId = protoArgs.Uri.GetActivityId(), - LaunchUri = protoArgs.Uri - }, - false); + return; + } + else if (args.TryGetSnapshotProtocol(out var protoArgs)) + { + OnAppLaunch(args, protoArgs.GetSnapshotLaunchArgs(), false); } else { // handle any unknown protocol launch as a normal app launch. OnAppLaunch(args, null, false); - } + } } private void OnAppLaunch(IActivatedEventArgs args, object arguments, bool isPreLaunch) diff --git a/src/Calculator/Calculator.csproj b/src/Calculator/Calculator.csproj index 6c73ba68..00bdc954 100644 --- a/src/Calculator/Calculator.csproj +++ b/src/Calculator/Calculator.csproj @@ -165,8 +165,10 @@ + + EquationStylePanelControl.xaml @@ -799,6 +801,9 @@ 6.2.14 + + 8.0.5 + diff --git a/src/Calculator/Common/LaunchArguments.cs b/src/Calculator/Common/LaunchArguments.cs index 8a1be58d..27c8853d 100644 --- a/src/Calculator/Common/LaunchArguments.cs +++ b/src/Calculator/Common/LaunchArguments.cs @@ -1,48 +1,53 @@ using System; - +using System.Linq; +using System.Text.Json; using Windows.ApplicationModel.Activation; -using Windows.ApplicationModel.UserActivities; + +using CalculatorApp.ViewModel.Snapshot; +using CalculatorApp.JsonUtils; +using CalculatorApp.ViewModel.Common; namespace CalculatorApp { internal class SnapshotLaunchArguments { - public string ActivityId { get; set; } - public Uri LaunchUri { get; set; } + public bool HasError { get; set; } + public ApplicationSnapshot Snapshot { get; set; } } internal static class LaunchExtensions { - public static bool IsSnapshotProtocol(this IActivatedEventArgs args) => - args is IProtocolActivatedEventArgs protoArgs && - protoArgs.Uri != null && - protoArgs.Uri.Segments != null && - protoArgs.Uri.Segments.Length == 2 && - protoArgs.Uri.Segments[0] == "snapshots/"; - - /// - /// GetActivityId() requires the parameter `launchUri` to be a well-formed - /// snapshot URI. - /// - /// the Uri to launch with a snapshot context. - /// Activity ID - public static string GetActivityId(this Uri launchUri) + public static bool TryGetSnapshotProtocol(this IActivatedEventArgs args, out IProtocolActivatedEventArgs result) { - return launchUri.Segments[1].Trim(); - } - - public static bool VerifyIncomingActivity(this SnapshotLaunchArguments launchArgs, UserActivity activity) - { - if (activity.State != UserActivityState.Published || - string.IsNullOrEmpty(activity.ActivityId) || - activity.ActivationUri == null || - activity.ActivationUri.Segments == null || - activity.ActivationUri.Segments.Length != 2 || - activity.ActivationUri.Segments[0] != "snapshots/") + result = null; + var protoArgs = args as IProtocolActivatedEventArgs; + if (protoArgs == null || + protoArgs.Uri == null || + protoArgs.Uri.Segments == null || + protoArgs.Uri.Segments.Length < 2 || + protoArgs.Uri.Segments[0] != "snapshot/") { return false; } - return activity.ActivityId == GetActivityId(launchArgs.LaunchUri); + result = protoArgs; + return true; + } + + public static SnapshotLaunchArguments GetSnapshotLaunchArgs(this IProtocolActivatedEventArgs args) + { + try + { + var rawbase64 = args.Uri.Segments.Skip(1).Aggregate((folded, x) => folded += x); + var compressed = Convert.FromBase64String(rawbase64); + var jsonStr = DeflateUtils.Decompress(compressed); + var snapshot = JsonSerializer.Deserialize(jsonStr); + return new SnapshotLaunchArguments { HasError = false, Snapshot = snapshot.Value }; + } + catch (Exception ex) + { + TraceLogger.GetInstance().LogRecallError($"Error occurs during the deserialization of Snapshot. Exception: {ex}"); + return new SnapshotLaunchArguments { HasError = true }; + } } } } diff --git a/src/Calculator/Utils/DeflateUtils.cs b/src/Calculator/Utils/DeflateUtils.cs new file mode 100644 index 00000000..b5a511d1 --- /dev/null +++ b/src/Calculator/Utils/DeflateUtils.cs @@ -0,0 +1,35 @@ +using System; +using System.IO; +using System.IO.Compression; +using System.Text; + +namespace CalculatorApp +{ + internal static class DeflateUtils + { + public 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(); + } + } + + public static string Decompress(byte[] data) + { + using (var srcStream = new MemoryStream(data)) + using (var inflater = new DeflateStream(srcStream, CompressionMode.Decompress)) + using (var resultStream = new MemoryStream()) + { + inflater.CopyTo(resultStream); + byte[] decompressed = resultStream.ToArray(); + return Encoding.UTF8.GetString(decompressed); + } + } + } +} diff --git a/src/Calculator/Utils/JsonUtils.cs b/src/Calculator/Utils/JsonUtils.cs new file mode 100644 index 00000000..1e86f94a --- /dev/null +++ b/src/Calculator/Utils/JsonUtils.cs @@ -0,0 +1,330 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.Json.Serialization; +using CalculatorApp.ViewModel.Snapshot; +using Windows.ApplicationModel; + +namespace CalculatorApp.JsonUtils +{ + internal class CalcManagerTokenAlias + { + [JsonIgnore] + public CalcManagerToken Value; + + [JsonPropertyName("t")] + public string OpCodeName + { + get => Value.OpCodeName; + set => Value.OpCodeName = value; + } + [JsonPropertyName("c")] + public int CommandIndex + { + get => Value.CommandIndex; + set => Value.CommandIndex = value; + } + + public CalcManagerTokenAlias() => Value = new CalcManagerToken(); + public CalcManagerTokenAlias(CalcManagerToken value) => Value = value; + } + + [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 + { + } + + internal class UnaryCommandAlias : ICalcManagerIExprCommandAlias + { + [JsonIgnore] + public UnaryCommand Value; + + [JsonPropertyName("c")] + public IReadOnlyList Commands + { + get => Value.Commands; + set => Value.Commands = value; + } + + public UnaryCommandAlias() => Value = new UnaryCommand(); + public UnaryCommandAlias(UnaryCommand value) => Value = value; + } + + internal class BinaryCommandAlias : ICalcManagerIExprCommandAlias + { + [JsonIgnore] + public BinaryCommand Value; + + [JsonPropertyName("c")] + public int Command + { + get => Value.Command; + set => Value.Command = value; + } + + public BinaryCommandAlias() => Value = new BinaryCommand(); + public BinaryCommandAlias(BinaryCommand value) => Value = value; + } + + internal class OperandCommandAlias : ICalcManagerIExprCommandAlias + { + [JsonIgnore] + public OperandCommand Value; + + [JsonPropertyName("n")] + public bool IsNegative + { + get => Value.IsNegative; + set => Value.IsNegative = value; + } + [JsonPropertyName("d")] + public bool IsDecimalPresent + { + get => Value.IsDecimalPresent; + set => Value.IsDecimalPresent = value; + } + [JsonPropertyName("s")] + public bool IsSciFmt + { + get => Value.IsSciFmt; + set => Value.IsSciFmt = value; + } + [JsonPropertyName("c")] + public IReadOnlyList Commands + { + get => Value.Commands; + set => Value.Commands = value; + } + + public OperandCommandAlias() => Value = new OperandCommand(); + public OperandCommandAlias(OperandCommand value) => Value = value; + } + + internal class ParenthesesAlias : ICalcManagerIExprCommandAlias + { + [JsonIgnore] + public Parentheses Value; + + [JsonPropertyName("c")] + public int Command + { + get => Value.Command; + set => Value.Command = value; + } + + public ParenthesesAlias() => Value = new Parentheses(); + public ParenthesesAlias(Parentheses value) => Value = value; + } + + internal class CalcManagerHistoryItemAlias + { + [JsonIgnore] + public CalcManagerHistoryItem Value; + + [JsonPropertyName("t")] + public IEnumerable Tokens + { + get => Value.Tokens.Select(x => new CalcManagerTokenAlias(x)); + set => Value.Tokens = value.Select(Helpers.MapToken).ToList(); + } + [JsonPropertyName("c")] + public IEnumerable Commands + { + get => Value.Commands.Select(Helpers.MapCommandAlias); + set => Value.Commands = value.Select(Helpers.MapCommandAlias).ToList(); + } + [JsonPropertyName("e")] + public string Expression + { + get => Value.Expression; + set => Value.Expression = value; + } + [JsonPropertyName("r")] + public string Result + { + get => Value.Result; + set => Value.Result = value; + } + + public CalcManagerHistoryItemAlias() => Value = new CalcManagerHistoryItem(); + public CalcManagerHistoryItemAlias(CalcManagerHistoryItem value) => Value = value; + } + + internal class CalcManagerSnapshotAlias + { + [JsonIgnore] + public CalcManagerSnapshot Value; + + [JsonPropertyName("h")] + public IEnumerable HistoryItems // optional + { + get => Value.HistoryItems?.Select(x => new CalcManagerHistoryItemAlias { Value = x }); + set => Value.HistoryItems = value?.Select(x => new CalcManagerHistoryItem + { + Tokens = x.Tokens.Select(Helpers.MapToken).ToList(), + Commands = x.Commands.Select(Helpers.MapCommandAlias).ToList(), + Expression = x.Expression, + Result = x.Result + }).ToList(); + } + + public CalcManagerSnapshotAlias() => Value = new CalcManagerSnapshot(); + public CalcManagerSnapshotAlias(CalcManagerSnapshot value) => Value = value; + } + + internal class PrimaryDisplaySnapshotAlias + { + [JsonIgnore] + public PrimaryDisplaySnapshot Value; + + [JsonPropertyName("d")] + public string DisplayValue + { + get => Value.DisplayValue; + set => Value.DisplayValue = value; + } + [JsonPropertyName("e")] + public bool IsError + { + get => Value.IsError; + set => Value.IsError = value; + } + + public PrimaryDisplaySnapshotAlias() => Value = new PrimaryDisplaySnapshot(); + public PrimaryDisplaySnapshotAlias(PrimaryDisplaySnapshot value) => Value = value; + } + + internal class ExpressionDisplaySnapshotAlias + { + [JsonIgnore] + public ExpressionDisplaySnapshot Value; + + [JsonPropertyName("t")] + public IEnumerable Tokens + { + get => Value.Tokens.Select(x => new CalcManagerTokenAlias(x)); + set => Value.Tokens = value.Select(Helpers.MapToken).ToList(); + } + [JsonPropertyName("c")] + public IEnumerable Commands + { + get => Value.Commands.Select(Helpers.MapCommandAlias); + set => Value.Commands = value.Select(Helpers.MapCommandAlias).ToList(); + } + + public ExpressionDisplaySnapshotAlias() => Value = new ExpressionDisplaySnapshot(); + public ExpressionDisplaySnapshotAlias(ExpressionDisplaySnapshot value) => Value = value; + } + + internal class StandardCalculatorSnapshotAlias + { + [JsonIgnore] + public StandardCalculatorSnapshot Value; + + [JsonPropertyName("m")] + public CalcManagerSnapshotAlias CalcManager + { + get => new CalcManagerSnapshotAlias(Value.CalcManager); + set => Value.CalcManager = value.Value; + } + [JsonPropertyName("p")] + public PrimaryDisplaySnapshotAlias PrimaryDisplay + { + get => new PrimaryDisplaySnapshotAlias(Value.PrimaryDisplay); + set => Value.PrimaryDisplay = value.Value; + } + [JsonPropertyName("e")] + public ExpressionDisplaySnapshotAlias ExpressionDisplay // optional + { + get => Value.ExpressionDisplay != null ? new ExpressionDisplaySnapshotAlias(Value.ExpressionDisplay) : null; + set => Value.ExpressionDisplay = value?.Value; + } + [JsonPropertyName("c")] + public IEnumerable Commands + { + get => Value.DisplayCommands.Select(Helpers.MapCommandAlias); + set => Value.DisplayCommands = value.Select(Helpers.MapCommandAlias).ToList(); + } + + public StandardCalculatorSnapshotAlias() => Value = new StandardCalculatorSnapshot(); + public StandardCalculatorSnapshotAlias(StandardCalculatorSnapshot value) => Value = value; + } + + internal class ApplicationSnapshotAlias + { + [JsonIgnore] + public ApplicationSnapshot Value; + + [JsonPropertyName("m")] + public int Mode { get => Value.Mode; set => Value.Mode = value; } + [JsonPropertyName("s")] + public StandardCalculatorSnapshotAlias StandardCalculatorSnapshot // optional + { + get => Value.StandardCalculator != null ? new StandardCalculatorSnapshotAlias(Value.StandardCalculator) : null; + set => Value.StandardCalculator = value?.Value; + } + + public ApplicationSnapshotAlias() => Value = new ApplicationSnapshot(); + public ApplicationSnapshotAlias(ApplicationSnapshot value) => Value = value; + } + + internal static class Helpers + { + public static CalcManagerToken MapToken(CalcManagerTokenAlias token) + { + return new CalcManagerToken { OpCodeName = token.OpCodeName, CommandIndex = token.CommandIndex }; + } + + public static ICalcManagerIExprCommandAlias MapCommandAlias(ICalcManagerIExprCommand exprCmd) + { + if (exprCmd is UnaryCommand unary) + { + return new UnaryCommandAlias(unary); + } + else if (exprCmd is BinaryCommand binary) + { + return new BinaryCommandAlias(binary); + } + else if (exprCmd is OperandCommand operand) + { + return new OperandCommandAlias(operand); + } + else if (exprCmd is Parentheses paren) + { + return new ParenthesesAlias(paren); + } + throw new NotImplementedException("unhandled command type."); + } + + public static ICalcManagerIExprCommand MapCommandAlias(ICalcManagerIExprCommandAlias exprCmd) + { + if (exprCmd is UnaryCommandAlias unary) + { + return new UnaryCommand { Commands = unary.Commands }; + } + else if (exprCmd is BinaryCommandAlias binary) + { + return new BinaryCommand { Command = binary.Command }; + } + else if (exprCmd is OperandCommandAlias operand) + { + return new OperandCommand + { + IsNegative = operand.IsNegative, + IsDecimalPresent = operand.IsDecimalPresent, + IsSciFmt = operand.IsSciFmt, + Commands = operand.Commands + }; + } + else if (exprCmd is ParenthesesAlias paren) + { + return new Parentheses { Command = paren.Command }; + } + throw new NotImplementedException("unhandled command type."); + } + } +} diff --git a/src/Calculator/Views/Calculator.xaml.cs b/src/Calculator/Views/Calculator.xaml.cs index ccd42637..7249e8a7 100644 --- a/src/Calculator/Views/Calculator.xaml.cs +++ b/src/Calculator/Views/Calculator.xaml.cs @@ -245,6 +245,7 @@ namespace CalculatorApp HistoryFlyout.FlyoutPresenterStyle.Setters.Add(new Setter(AutomationProperties.NameProperty, historyPaneName)); string memoryPaneName = AppResourceProvider.GetInstance().GetResourceString("MemoryPane"); MemoryFlyout.FlyoutPresenterStyle.Setters.Add(new Setter(AutomationProperties.NameProperty, memoryPaneName)); + OnIsInErrorPropertyChanged(); // Delay load things later when we get a chance. WeakReference weakThis = new WeakReference(this); diff --git a/src/Calculator/Views/MainPage.xaml.cs b/src/Calculator/Views/MainPage.xaml.cs index ff6fbe6c..32bfdb26 100644 --- a/src/Calculator/Views/MainPage.xaml.cs +++ b/src/Calculator/Views/MainPage.xaml.cs @@ -1,10 +1,10 @@ using System; using System.Collections.Generic; using System.ComponentModel; +using System.Text.Json; using System.Threading.Tasks; using Windows.ApplicationModel.UserActivities; -using Windows.Data.Json; using Windows.Foundation; using Windows.Graphics.Display; using Windows.Storage; @@ -18,6 +18,7 @@ using Microsoft.UI.Xaml.Controls; using CalculatorApp.Common; using CalculatorApp.Converters; +using CalculatorApp.JsonUtils; using CalculatorApp.ViewModel; using CalculatorApp.ViewModel.Common; using CalculatorApp.ViewModel.Common.Automation; @@ -61,24 +62,38 @@ 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) + { + // FIXME: https://microsoft.visualstudio.com/DefaultCollection/OS/_workitems/edit/47775705/ + TraceLogger.GetInstance().LogRecallError("55e29ba5-6097-40ec-8960-458750be3039"); + return; + } + var channel = UserActivityChannel.GetDefault(); + var activity = await channel.GetOrCreateUserActivityAsync($"{Guid.NewGuid()}"); + string embeddedData; + try + { + var json = JsonSerializer.Serialize(new ApplicationSnapshotAlias(Model.Snapshot)); + embeddedData = Convert.ToBase64String(DeflateUtils.Compress(json)); + } + catch (Exception ex) + { + TraceLogger.GetInstance().LogRecallError($"Error occurs during the serialization of Snapshot. Exception: {ex}"); + 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()}"); - activity.ActivationUri = new Uri($"ms-calculator:snapshots/{activity.ActivityId}"); - activity.ContentInfo = UserActivityContentInfo.FromJson(Model.SaveApplicationSnapshot().Stringify()); - 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); }; } @@ -165,22 +180,18 @@ namespace CalculatorApp } else if (e.Parameter is SnapshotLaunchArguments snapshotArgs) { - _ = Window.Current.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () => - { - var channel = UserActivityChannel.GetDefault(); - var activity = await channel.GetOrCreateUserActivityAsync(snapshotArgs.ActivityId); - - if (TryRestoreFromActivity(snapshotArgs, activity, out var errorMessage)) - { - TraceLogger.GetInstance().LogRecallRestore(Model.Mode); - SelectNavigationItemByModel(); - } - else - { - TraceLogger.GetInstance().LogRecallError(Model.Mode, errorMessage); - } - }); Model.Initialize(initialMode); + if (!snapshotArgs.HasError) + { + Model.RestoreFromSnapshot(snapshotArgs.Snapshot); + TraceLogger.GetInstance().LogRecallRestore((ViewMode)snapshotArgs.Snapshot.Mode); + } + else + { + _ = Window.Current.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, + async () => await ShowSnapshotLaunchErrorAsync()); + TraceLogger.GetInstance().LogRecallError("OnNavigatedTo:Found errors."); + } } else { @@ -188,38 +199,6 @@ namespace CalculatorApp } } - private bool TryRestoreFromActivity(SnapshotLaunchArguments snapshotArgs, UserActivity activity, out string errorMessage) - { - if (!snapshotArgs.VerifyIncomingActivity(activity)) - { - errorMessage = "IncomingActivityFailed"; - return false; - } - - // Work around for bug https://microsoft.visualstudio.com/DefaultCollection/OS/_workitems/edit/48931227 - // where ContentInfo can't be directly accessed. - if (!JsonObject.TryParse(activity.ToJson(), out var activityJson)) - { - errorMessage = "ParseJsonError"; - return false; - } - - if (!activityJson.ContainsKey("contentInfo")) - { - errorMessage = "ContentInfoNotExist"; - return false; - } - - if (!Model.TryRestoreFromSnapshot(activityJson.GetNamedObject("contentInfo"))) - { - errorMessage = "RestoreFromSnapshotFailed"; - return false; - } - - errorMessage = string.Empty; - return true; - } - private void InitializeNavViewCategoriesSource() { NavViewCategoriesSource = ExpandNavViewCategoryGroups(Model.Categories);