diff --git a/source/uwp/Renderer/AdaptiveCardRenderer.vcxproj b/source/uwp/Renderer/AdaptiveCardRenderer.vcxproj index f23198e9fb..361c13fcf9 100644 --- a/source/uwp/Renderer/AdaptiveCardRenderer.vcxproj +++ b/source/uwp/Renderer/AdaptiveCardRenderer.vcxproj @@ -127,6 +127,7 @@ + @@ -176,6 +177,7 @@ + diff --git a/source/uwp/Renderer/AdaptiveCardRenderer.vcxproj.filters b/source/uwp/Renderer/AdaptiveCardRenderer.vcxproj.filters index 1e49cabe39..8ceff937a5 100644 --- a/source/uwp/Renderer/AdaptiveCardRenderer.vcxproj.filters +++ b/source/uwp/Renderer/AdaptiveCardRenderer.vcxproj.filters @@ -111,6 +111,7 @@ + @@ -229,6 +230,7 @@ + diff --git a/source/uwp/Renderer/idl/AdaptiveCards.Rendering.Uwp.idl b/source/uwp/Renderer/idl/AdaptiveCards.Rendering.Uwp.idl index 1958334c43..86c213eaf2 100644 --- a/source/uwp/Renderer/idl/AdaptiveCards.Rendering.Uwp.idl +++ b/source/uwp/Renderer/idl/AdaptiveCards.Rendering.Uwp.idl @@ -412,6 +412,7 @@ AdaptiveNamespaceStart interface Windows.Foundation.Collections.IVector; interface Windows.Foundation.Collections.IVector; interface Windows.Foundation.Collections.IVector; + interface Windows.Foundation.Collections.IVector; } [ @@ -434,8 +435,8 @@ AdaptiveNamespaceStart FallbackType FallbackType; IAdaptiveCardElement FallbackContent; Windows.Data.Json.JsonObject AdditionalProperties; + Windows.Foundation.Collections.IVector Requirements { get; }; - Boolean MeetsRequirements(AdaptiveFeatureRegistration featureRegistration); Windows.Data.Json.JsonObject ToJson(); }; @@ -1593,6 +1594,21 @@ AdaptiveNamespaceStart Windows.Foundation.Collections.ValueSet AsValueSet(); }; + [ +#ifdef ADAPTIVE_CARDS_WINDOWS + interface_name("Windows.Internal.AdaptiveCards.Rendering.Uwp.IAdaptiveRequirement", 45B7321F-BB3C-48B8-B98E-6483927C47D7), + contract(InternalContract, 1), + internal +#endif + ] + runtimeclass AdaptiveRequirement + { + AdaptiveRequirement(String requirementName, String requirementversion); + + String Name; + String Version; + } + [ #ifdef ADAPTIVE_CARDS_WINDOWS interface_name("Windows.Internal.AdaptiveCards.Rendering.Uwp.IAdaptiveFeatureRegistration", b2a455fd-385c-45eb-89ce-007b7ed0a815), diff --git a/source/uwp/Renderer/lib/AdaptiveActionSet.h b/source/uwp/Renderer/lib/AdaptiveActionSet.h index 4daeeaa0b6..c56d53d3fe 100644 --- a/source/uwp/Renderer/lib/AdaptiveActionSet.h +++ b/source/uwp/Renderer/lib/AdaptiveActionSet.h @@ -102,12 +102,13 @@ namespace AdaptiveNamespace return AdaptiveCardElementBase::put_IsVisible(isVisible); } - IFACEMETHODIMP MeetsRequirements(_In_ ABI::AdaptiveNamespace::IAdaptiveFeatureRegistration* featureRegistration, - _Out_ boolean* value) + IFACEMETHODIMP get_Requirements( + _COM_Outptr_ ABI::Windows::Foundation::Collections::IVector** requirements) { - return AdaptiveCardElementBase::MeetsRequirements(featureRegistration, value); + return AdaptiveCardElementBase::get_Requirements(requirements); } + IFACEMETHODIMP ToJson(_Out_ ABI::Windows::Data::Json::IJsonObject** result) { return AdaptiveCardElementBase::ToJson(result); diff --git a/source/uwp/Renderer/lib/AdaptiveCardElement.cpp b/source/uwp/Renderer/lib/AdaptiveCardElement.cpp index fdbb64cd43..a6a9dd50fb 100644 --- a/source/uwp/Renderer/lib/AdaptiveCardElement.cpp +++ b/source/uwp/Renderer/lib/AdaptiveCardElement.cpp @@ -5,6 +5,7 @@ #include "AdaptiveFeatureRegistration.h" #include "SemanticVersion.h" #include "Util.h" +#include "Vector.h" using namespace Microsoft::WRL; using namespace Microsoft::WRL::Wrappers; @@ -35,9 +36,8 @@ namespace AdaptiveNamespace RETURN_IF_FAILED(GenerateElementProjection(fallbackObject, m_fallbackContent.GetAddressOf())); } } - - m_requires = sharedModel->GetRequirements(); - + m_requirements = Microsoft::WRL::Make>(); + GenerateRequirementsProjection(sharedModel->GetRequirements(), m_requirements.Get()); return S_OK; } @@ -102,6 +102,16 @@ namespace AdaptiveNamespace IFACEMETHODIMP AdaptiveCardElementBase::put_FallbackContent(_In_ ABI::AdaptiveNamespace::IAdaptiveCardElement* content) { m_fallbackContent = content; + + if (content == nullptr && m_fallbackType == ABI::AdaptiveNamespace::FallbackType::Content) + { + m_fallbackType = ABI::AdaptiveNamespace::FallbackType::None; + } + else if (content != nullptr) + { + m_fallbackType = ABI::AdaptiveNamespace::FallbackType::Content; + } + return S_OK; } @@ -137,22 +147,11 @@ namespace AdaptiveNamespace return S_OK; } - IFACEMETHODIMP AdaptiveCardElementBase::MeetsRequirements(_In_ ABI::AdaptiveNamespace::IAdaptiveFeatureRegistration* featureRegistration, - _Out_ boolean* value) try + IFACEMETHODIMP AdaptiveCardElementBase::get_Requirements( + ABI::Windows::Foundation::Collections::IVector** requirements) { - *value = true; - - std::shared_ptr sharedModel; - RETURN_IF_FAILED(GetSharedModel(sharedModel)); - - ComPtr featureRegistrationImpl = PeekInnards(featureRegistration); - std::shared_ptr sharedFeatureRegistration = - featureRegistrationImpl->GetSharedFeatureRegistration(); - - *value = sharedModel->MeetsRequirements(*sharedFeatureRegistration); - return S_OK; + return m_requirements.CopyTo(requirements); } - CATCH_RETURN; IFACEMETHODIMP AdaptiveCardElementBase::ToJson(_COM_Outptr_ ABI::Windows::Data::Json::IJsonObject** result) { @@ -171,14 +170,7 @@ namespace AdaptiveNamespace sharedCardElement->SetHeight(static_cast(m_height)); sharedCardElement->SetFallbackType(MapUwpFallbackTypeToShared(m_fallbackType)); - if (!m_requires->empty()) - { - auto requirements = sharedCardElement->GetRequirements(); - for (const auto& requirement : *m_requires) - { - requirements->emplace(requirement); - } - } + RETURN_IF_FAILED(GenerateSharedRequirements(m_requirements.Get(), sharedCardElement->GetRequirements())); if (m_fallbackType == ABI::AdaptiveNamespace::FallbackType::Content) { diff --git a/source/uwp/Renderer/lib/AdaptiveCardElement.h b/source/uwp/Renderer/lib/AdaptiveCardElement.h index 489ae6a1d6..7a96538b8d 100644 --- a/source/uwp/Renderer/lib/AdaptiveCardElement.h +++ b/source/uwp/Renderer/lib/AdaptiveCardElement.h @@ -41,8 +41,7 @@ namespace AdaptiveNamespace IFACEMETHODIMP get_Height(_Out_ ABI::AdaptiveNamespace::HeightType* height); IFACEMETHODIMP put_Height(ABI::AdaptiveNamespace::HeightType height); - IFACEMETHODIMP MeetsRequirements(_In_ ABI::AdaptiveNamespace::IAdaptiveFeatureRegistration* featureRegistration, - _Out_ boolean* value); + IFACEMETHODIMP get_Requirements(_COM_Outptr_ ABI::Windows::Foundation::Collections::IVector** requirements); IFACEMETHODIMP ToJson(_COM_Outptr_ ABI::Windows::Data::Json::IJsonObject** result); @@ -61,6 +60,6 @@ namespace AdaptiveNamespace InternalId m_internalId; ABI::AdaptiveNamespace::FallbackType m_fallbackType; Microsoft::WRL::ComPtr m_fallbackContent; - std::shared_ptr> m_requires; + Microsoft::WRL::ComPtr> m_requirements; }; } diff --git a/source/uwp/Renderer/lib/AdaptiveChoiceSetInput.h b/source/uwp/Renderer/lib/AdaptiveChoiceSetInput.h index f1011bb02f..38220c38d0 100644 --- a/source/uwp/Renderer/lib/AdaptiveChoiceSetInput.h +++ b/source/uwp/Renderer/lib/AdaptiveChoiceSetInput.h @@ -131,10 +131,10 @@ namespace AdaptiveNamespace return AdaptiveCardElementBase::put_AdditionalProperties(value); } - IFACEMETHODIMP MeetsRequirements(_In_ ABI::AdaptiveNamespace::IAdaptiveFeatureRegistration* featureRegistration, - _Out_ boolean* value) + IFACEMETHODIMP get_Requirements( + _COM_Outptr_ ABI::Windows::Foundation::Collections::IVector** requirements) { - return AdaptiveCardElementBase::MeetsRequirements(featureRegistration, value); + return AdaptiveCardElementBase::get_Requirements(requirements); } IFACEMETHODIMP ToJson(_COM_Outptr_ ABI::Windows::Data::Json::IJsonObject** result) diff --git a/source/uwp/Renderer/lib/AdaptiveColumn.h b/source/uwp/Renderer/lib/AdaptiveColumn.h index 539d2469ce..e120f97bc3 100644 --- a/source/uwp/Renderer/lib/AdaptiveColumn.h +++ b/source/uwp/Renderer/lib/AdaptiveColumn.h @@ -117,10 +117,10 @@ namespace AdaptiveNamespace return AdaptiveCardElementBase::put_AdditionalProperties(value); } - IFACEMETHODIMP MeetsRequirements(_In_ ABI::AdaptiveNamespace::IAdaptiveFeatureRegistration* featureRegistration, - _Out_ boolean* value) + IFACEMETHODIMP get_Requirements( + _COM_Outptr_ ABI::Windows::Foundation::Collections::IVector** requirements) { - return AdaptiveCardElementBase::MeetsRequirements(featureRegistration, value); + return AdaptiveCardElementBase::get_Requirements(requirements); } IFACEMETHODIMP ToJson(_COM_Outptr_ ABI::Windows::Data::Json::IJsonObject** result) diff --git a/source/uwp/Renderer/lib/AdaptiveColumnSet.h b/source/uwp/Renderer/lib/AdaptiveColumnSet.h index db90676cd2..b0b0616af5 100644 --- a/source/uwp/Renderer/lib/AdaptiveColumnSet.h +++ b/source/uwp/Renderer/lib/AdaptiveColumnSet.h @@ -103,10 +103,10 @@ namespace AdaptiveNamespace return AdaptiveCardElementBase::put_AdditionalProperties(value); } - IFACEMETHODIMP MeetsRequirements(_In_ ABI::AdaptiveNamespace::IAdaptiveFeatureRegistration* featureRegistration, - _Out_ boolean* value) + IFACEMETHODIMP get_Requirements( + _COM_Outptr_ ABI::Windows::Foundation::Collections::IVector** requirements) { - return AdaptiveCardElementBase::MeetsRequirements(featureRegistration, value); + return AdaptiveCardElementBase::get_Requirements(requirements); } IFACEMETHODIMP ToJson(_COM_Outptr_ ABI::Windows::Data::Json::IJsonObject** result) diff --git a/source/uwp/Renderer/lib/AdaptiveContainer.h b/source/uwp/Renderer/lib/AdaptiveContainer.h index 5384da6c08..206847d190 100644 --- a/source/uwp/Renderer/lib/AdaptiveContainer.h +++ b/source/uwp/Renderer/lib/AdaptiveContainer.h @@ -111,10 +111,10 @@ namespace AdaptiveNamespace return AdaptiveCardElementBase::put_AdditionalProperties(value); } - IFACEMETHODIMP MeetsRequirements(_In_ ABI::AdaptiveNamespace::IAdaptiveFeatureRegistration* featureRegistration, - _Out_ boolean* value) + IFACEMETHODIMP get_Requirements( + _COM_Outptr_ ABI::Windows::Foundation::Collections::IVector** requirements) { - return AdaptiveCardElementBase::MeetsRequirements(featureRegistration, value); + return AdaptiveCardElementBase::get_Requirements(requirements); } IFACEMETHODIMP ToJson(_COM_Outptr_ ABI::Windows::Data::Json::IJsonObject** result) diff --git a/source/uwp/Renderer/lib/AdaptiveDateInput.h b/source/uwp/Renderer/lib/AdaptiveDateInput.h index c1a5df028b..95f82cecc8 100644 --- a/source/uwp/Renderer/lib/AdaptiveDateInput.h +++ b/source/uwp/Renderer/lib/AdaptiveDateInput.h @@ -115,10 +115,10 @@ namespace AdaptiveNamespace return AdaptiveCardElementBase::put_AdditionalProperties(value); } - IFACEMETHODIMP MeetsRequirements(_In_ ABI::AdaptiveNamespace::IAdaptiveFeatureRegistration* featureRegistration, - _Out_ boolean* value) + IFACEMETHODIMP get_Requirements( + _COM_Outptr_ ABI::Windows::Foundation::Collections::IVector** requirements) { - return AdaptiveCardElementBase::MeetsRequirements(featureRegistration, value); + return AdaptiveCardElementBase::get_Requirements(requirements); } IFACEMETHODIMP ToJson(_COM_Outptr_ ABI::Windows::Data::Json::IJsonObject** result) diff --git a/source/uwp/Renderer/lib/AdaptiveFactSet.h b/source/uwp/Renderer/lib/AdaptiveFactSet.h index a47e41cb10..161a6e73d2 100644 --- a/source/uwp/Renderer/lib/AdaptiveFactSet.h +++ b/source/uwp/Renderer/lib/AdaptiveFactSet.h @@ -97,10 +97,10 @@ namespace AdaptiveNamespace return AdaptiveCardElementBase::put_AdditionalProperties(value); } - IFACEMETHODIMP MeetsRequirements(_In_ ABI::AdaptiveNamespace::IAdaptiveFeatureRegistration* featureRegistration, - _Out_ boolean* value) + IFACEMETHODIMP get_Requirements( + _COM_Outptr_ ABI::Windows::Foundation::Collections::IVector** requirements) { - return AdaptiveCardElementBase::MeetsRequirements(featureRegistration, value); + return AdaptiveCardElementBase::get_Requirements(requirements); } IFACEMETHODIMP ToJson(_COM_Outptr_ ABI::Windows::Data::Json::IJsonObject** result) diff --git a/source/uwp/Renderer/lib/AdaptiveImage.h b/source/uwp/Renderer/lib/AdaptiveImage.h index afcb59ef18..254facfbd7 100644 --- a/source/uwp/Renderer/lib/AdaptiveImage.h +++ b/source/uwp/Renderer/lib/AdaptiveImage.h @@ -112,10 +112,10 @@ namespace AdaptiveNamespace return AdaptiveCardElementBase::put_AdditionalProperties(value); } - IFACEMETHODIMP MeetsRequirements(_In_ ABI::AdaptiveNamespace::IAdaptiveFeatureRegistration* featureRegistration, - _Out_ boolean* value) + IFACEMETHODIMP get_Requirements( + _COM_Outptr_ ABI::Windows::Foundation::Collections::IVector** requirements) { - return AdaptiveCardElementBase::MeetsRequirements(featureRegistration, value); + return AdaptiveCardElementBase::get_Requirements(requirements); } IFACEMETHODIMP ToJson(_COM_Outptr_ ABI::Windows::Data::Json::IJsonObject** result) diff --git a/source/uwp/Renderer/lib/AdaptiveImageSet.h b/source/uwp/Renderer/lib/AdaptiveImageSet.h index db2e039cfc..f59618c5dc 100644 --- a/source/uwp/Renderer/lib/AdaptiveImageSet.h +++ b/source/uwp/Renderer/lib/AdaptiveImageSet.h @@ -100,10 +100,10 @@ namespace AdaptiveNamespace return AdaptiveCardElementBase::put_AdditionalProperties(value); } - IFACEMETHODIMP MeetsRequirements(_In_ ABI::AdaptiveNamespace::IAdaptiveFeatureRegistration* featureRegistration, - _Out_ boolean* value) + IFACEMETHODIMP get_Requirements( + _COM_Outptr_ ABI::Windows::Foundation::Collections::IVector** requirements) { - return AdaptiveCardElementBase::MeetsRequirements(featureRegistration, value); + return AdaptiveCardElementBase::get_Requirements(requirements); } IFACEMETHODIMP ToJson(_COM_Outptr_ ABI::Windows::Data::Json::IJsonObject** result) diff --git a/source/uwp/Renderer/lib/AdaptiveMedia.h b/source/uwp/Renderer/lib/AdaptiveMedia.h index e0368e04fd..e93addbd5f 100644 --- a/source/uwp/Renderer/lib/AdaptiveMedia.h +++ b/source/uwp/Renderer/lib/AdaptiveMedia.h @@ -86,10 +86,10 @@ namespace AdaptiveNamespace return AdaptiveCardElementBase::put_Height(height); } - IFACEMETHODIMP MeetsRequirements(_In_ ABI::AdaptiveNamespace::IAdaptiveFeatureRegistration* featureRegistration, - _Out_ boolean* value) + IFACEMETHODIMP get_Requirements( + _COM_Outptr_ ABI::Windows::Foundation::Collections::IVector** requirements) { - return AdaptiveCardElementBase::MeetsRequirements(featureRegistration, value); + return AdaptiveCardElementBase::get_Requirements(requirements); } IFACEMETHODIMP ToJson(_COM_Outptr_ ABI::Windows::Data::Json::IJsonObject** result) diff --git a/source/uwp/Renderer/lib/AdaptiveNumberInput.h b/source/uwp/Renderer/lib/AdaptiveNumberInput.h index 7076b7fc8a..81e3528fe2 100644 --- a/source/uwp/Renderer/lib/AdaptiveNumberInput.h +++ b/source/uwp/Renderer/lib/AdaptiveNumberInput.h @@ -115,10 +115,10 @@ namespace AdaptiveNamespace return AdaptiveCardElementBase::put_AdditionalProperties(value); } - IFACEMETHODIMP MeetsRequirements(_In_ ABI::AdaptiveNamespace::IAdaptiveFeatureRegistration* featureRegistration, - _Out_ boolean* value) + IFACEMETHODIMP get_Requirements( + _COM_Outptr_ ABI::Windows::Foundation::Collections::IVector** requirements) { - return AdaptiveCardElementBase::MeetsRequirements(featureRegistration, value); + return AdaptiveCardElementBase::get_Requirements(requirements); } IFACEMETHODIMP ToJson(_COM_Outptr_ ABI::Windows::Data::Json::IJsonObject** result) diff --git a/source/uwp/Renderer/lib/AdaptiveRequirement.cpp b/source/uwp/Renderer/lib/AdaptiveRequirement.cpp new file mode 100644 index 0000000000..dd4b76278a --- /dev/null +++ b/source/uwp/Renderer/lib/AdaptiveRequirement.cpp @@ -0,0 +1,35 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +#include "pch.h" +#include "AdaptiveRequirement.h" + +using namespace Microsoft::WRL; +using namespace Microsoft::WRL::Wrappers; +using namespace ABI::AdaptiveNamespace; + +namespace AdaptiveNamespace +{ + HRESULT AdaptiveRequirement::RuntimeClassInitialize() { return S_OK; } + + HRESULT AdaptiveRequirement::RuntimeClassInitialize(_In_ HSTRING name, _In_ HSTRING version) + { + m_name.Set(name); + m_version.Set(version); + return S_OK; + } + + HRESULT AdaptiveRequirement::RuntimeClassInitialize(const std::pair& sharedRequirement) noexcept + { + RETURN_IF_FAILED(UTF8ToHString(sharedRequirement.first, m_name.GetAddressOf())); + RETURN_IF_FAILED(UTF8ToHString((std::string)sharedRequirement.second, m_version.GetAddressOf())); + return S_OK; + } + + HRESULT AdaptiveRequirement::get_Name(_Outptr_ HSTRING* value) { return m_name.CopyTo(value); } + + HRESULT AdaptiveRequirement::put_Name(_In_ HSTRING value) { return m_name.Set(value); } + + HRESULT AdaptiveRequirement::get_Version(_Outptr_ HSTRING* value) { return m_version.CopyTo(value); } + + HRESULT AdaptiveRequirement::put_Version(_In_ HSTRING value) { return m_version.Set(value); } +} diff --git a/source/uwp/Renderer/lib/AdaptiveRequirement.h b/source/uwp/Renderer/lib/AdaptiveRequirement.h new file mode 100644 index 0000000000..0e4e4839f3 --- /dev/null +++ b/source/uwp/Renderer/lib/AdaptiveRequirement.h @@ -0,0 +1,44 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +#pragma once + +#include "AdaptiveCards.Rendering.Uwp.h" + +namespace AdaptiveNamespace +{ + class AdaptiveRequirement + : public Microsoft::WRL::RuntimeClass, + Microsoft::WRL::Implements, + Microsoft::WRL::FtmBase> + { + AdaptiveRuntime(AdaptiveRequirement); + + public: + HRESULT RuntimeClassInitialize(); + HRESULT RuntimeClassInitialize(_In_ HSTRING name, _In_ HSTRING version); + HRESULT RuntimeClassInitialize(const std::pair& sharedRequirement) noexcept; + + // IAdaptiveRequirement + IFACEMETHODIMP put_Name(_In_ HSTRING value); + IFACEMETHODIMP get_Name(_Outptr_ HSTRING* value); + + IFACEMETHODIMP put_Version(_In_ HSTRING value); + IFACEMETHODIMP get_Version(_Outptr_ HSTRING* value); + + private: + Microsoft::WRL::Wrappers::HString m_name; + Microsoft::WRL::Wrappers::HString m_version; + }; + + class AdaptiveRequirementFactory : public Microsoft::WRL::AgileActivationFactory + { + IFACEMETHODIMP CreateInstance(_In_ HSTRING name, + _In_ HSTRING version, + _COM_Outptr_ ABI::AdaptiveCards::Rendering::Uwp::IAdaptiveRequirement** result) override + { + return Microsoft::WRL::Details::MakeAndInitialize(result, name, version); + } + }; + + ActivatableClassWithFactory(AdaptiveRequirement, AdaptiveRequirementFactory); +} diff --git a/source/uwp/Renderer/lib/AdaptiveRichTextBlock.h b/source/uwp/Renderer/lib/AdaptiveRichTextBlock.h index ea147a026f..f204d0fa95 100644 --- a/source/uwp/Renderer/lib/AdaptiveRichTextBlock.h +++ b/source/uwp/Renderer/lib/AdaptiveRichTextBlock.h @@ -89,10 +89,10 @@ namespace AdaptiveNamespace return AdaptiveCardElementBase::put_FallbackContent(content); } - IFACEMETHODIMP MeetsRequirements(_In_ ABI::AdaptiveNamespace::IAdaptiveFeatureRegistration* featureRegistration, - _Out_ boolean* value) + IFACEMETHODIMP get_Requirements( + _COM_Outptr_ ABI::Windows::Foundation::Collections::IVector** requirements) { - return AdaptiveCardElementBase::MeetsRequirements(featureRegistration, value); + return AdaptiveCardElementBase::get_Requirements(requirements); } IFACEMETHODIMP ToJson(_COM_Outptr_ ABI::Windows::Data::Json::IJsonObject** result) diff --git a/source/uwp/Renderer/lib/AdaptiveTextBlock.h b/source/uwp/Renderer/lib/AdaptiveTextBlock.h index 5388b705ae..6ed5d5a53d 100644 --- a/source/uwp/Renderer/lib/AdaptiveTextBlock.h +++ b/source/uwp/Renderer/lib/AdaptiveTextBlock.h @@ -141,10 +141,10 @@ namespace AdaptiveNamespace return AdaptiveCardElementBase::put_AdditionalProperties(value); } - IFACEMETHODIMP MeetsRequirements(_In_ ABI::AdaptiveNamespace::IAdaptiveFeatureRegistration* featureRegistration, - _Out_ boolean* value) + IFACEMETHODIMP get_Requirements( + _COM_Outptr_ ABI::Windows::Foundation::Collections::IVector** requirements) { - return AdaptiveCardElementBase::MeetsRequirements(featureRegistration, value); + return AdaptiveCardElementBase::get_Requirements(requirements); } IFACEMETHODIMP ToJson(_COM_Outptr_ ABI::Windows::Data::Json::IJsonObject** result) diff --git a/source/uwp/Renderer/lib/AdaptiveTextInput.h b/source/uwp/Renderer/lib/AdaptiveTextInput.h index 381b008ef5..94043b088a 100644 --- a/source/uwp/Renderer/lib/AdaptiveTextInput.h +++ b/source/uwp/Renderer/lib/AdaptiveTextInput.h @@ -124,10 +124,10 @@ namespace AdaptiveNamespace return AdaptiveCardElementBase::put_AdditionalProperties(value); } - IFACEMETHODIMP MeetsRequirements(_In_ ABI::AdaptiveNamespace::IAdaptiveFeatureRegistration* featureRegistration, - _Out_ boolean* value) + IFACEMETHODIMP get_Requirements( + _COM_Outptr_ ABI::Windows::Foundation::Collections::IVector** requirements) { - return AdaptiveCardElementBase::MeetsRequirements(featureRegistration, value); + return AdaptiveCardElementBase::get_Requirements(requirements); } IFACEMETHODIMP ToJson(_COM_Outptr_ ABI::Windows::Data::Json::IJsonObject** result) diff --git a/source/uwp/Renderer/lib/AdaptiveTimeInput.h b/source/uwp/Renderer/lib/AdaptiveTimeInput.h index df6b845b3c..6d575d6d20 100644 --- a/source/uwp/Renderer/lib/AdaptiveTimeInput.h +++ b/source/uwp/Renderer/lib/AdaptiveTimeInput.h @@ -115,10 +115,10 @@ namespace AdaptiveNamespace return AdaptiveCardElementBase::put_AdditionalProperties(value); } - IFACEMETHODIMP MeetsRequirements(_In_ ABI::AdaptiveNamespace::IAdaptiveFeatureRegistration* featureRegistration, - _Out_ boolean* value) + IFACEMETHODIMP get_Requirements( + _COM_Outptr_ ABI::Windows::Foundation::Collections::IVector** requirements) { - return AdaptiveCardElementBase::MeetsRequirements(featureRegistration, value); + return AdaptiveCardElementBase::get_Requirements(requirements); } IFACEMETHODIMP ToJson(_COM_Outptr_ ABI::Windows::Data::Json::IJsonObject** result) diff --git a/source/uwp/Renderer/lib/AdaptiveToggleInput.h b/source/uwp/Renderer/lib/AdaptiveToggleInput.h index 75c1c25b84..bccda9595d 100644 --- a/source/uwp/Renderer/lib/AdaptiveToggleInput.h +++ b/source/uwp/Renderer/lib/AdaptiveToggleInput.h @@ -118,10 +118,10 @@ namespace AdaptiveNamespace return AdaptiveCardElementBase::put_AdditionalProperties(value); } - IFACEMETHODIMP MeetsRequirements(_In_ ABI::AdaptiveNamespace::IAdaptiveFeatureRegistration* featureRegistration, - _Out_ boolean* value) + IFACEMETHODIMP get_Requirements( + _COM_Outptr_ ABI::Windows::Foundation::Collections::IVector** requirements) { - return AdaptiveCardElementBase::MeetsRequirements(featureRegistration, value); + return AdaptiveCardElementBase::get_Requirements(requirements); } IFACEMETHODIMP ToJson(_COM_Outptr_ ABI::Windows::Data::Json::IJsonObject** result) diff --git a/source/uwp/Renderer/lib/AdaptiveUnsupportedElement.h b/source/uwp/Renderer/lib/AdaptiveUnsupportedElement.h index 904825ed48..a753576e7c 100644 --- a/source/uwp/Renderer/lib/AdaptiveUnsupportedElement.h +++ b/source/uwp/Renderer/lib/AdaptiveUnsupportedElement.h @@ -83,10 +83,10 @@ namespace AdaptiveNamespace return AdaptiveCardElementBase::put_AdditionalProperties(value); } - IFACEMETHODIMP MeetsRequirements(_In_ ABI::AdaptiveNamespace::IAdaptiveFeatureRegistration* featureRegistration, - _Out_ boolean* value) + IFACEMETHODIMP get_Requirements( + _COM_Outptr_ ABI::Windows::Foundation::Collections::IVector** requirements) { - return AdaptiveCardElementBase::MeetsRequirements(featureRegistration, value); + return AdaptiveCardElementBase::get_Requirements(requirements); } IFACEMETHODIMP ToJson(_COM_Outptr_ ABI::Windows::Data::Json::IJsonObject** result) diff --git a/source/uwp/Renderer/lib/Util.cpp b/source/uwp/Renderer/lib/Util.cpp index 1a572aca69..2ce839234a 100644 --- a/source/uwp/Renderer/lib/Util.cpp +++ b/source/uwp/Renderer/lib/Util.cpp @@ -13,12 +13,14 @@ #include "AdaptiveDateInput.h" #include "AdaptiveFact.h" #include "AdaptiveFactSet.h" +#include "AdaptiveFeatureRegistration.h" #include "AdaptiveImage.h" #include "AdaptiveImageSet.h" #include "AdaptiveMedia.h" #include "AdaptiveMediaSource.h" #include "AdaptiveNumberInput.h" #include "AdaptiveOpenUrlAction.h" +#include "AdaptiveRequirement.h" #include "AdaptiveRichTextBlock.h" #include "AdaptiveSeparator.h" #include "AdaptiveShowCardAction.h" @@ -91,8 +93,7 @@ std::wstring StringToWstring(const std::string_view& in) if (!in.empty()) { // TODO: safer casts - const size_t requiredSize = - MultiByteToWideChar(CP_UTF8, 0 /*dwFlags*/, &in[0], (int)in.length(), (LPWSTR) nullptr, 0); + const size_t requiredSize = MultiByteToWideChar(CP_UTF8, 0 /*dwFlags*/, &in[0], (int)in.length(), (LPWSTR) nullptr, 0); std::wstring wide(requiredSize, 0); if (MultiByteToWideChar(CP_UTF8, 0 /*dwFlags*/, &in[0], (int)in.length(), &wide[0], (int)requiredSize) == 0) @@ -332,6 +333,42 @@ HRESULT GenerateSharedActions(_In_ ABI::Windows::Foundation::Collections::IVecto return S_OK; } +HRESULT GenerateSharedRequirements( + _In_ ABI::Windows::Foundation::Collections::IVector* adaptiveRequirements, + std::shared_ptr> sharedRequirements) noexcept try +{ + sharedRequirements->clear(); + + XamlHelpers::IterateOverVector( + adaptiveRequirements, [&](ABI::AdaptiveNamespace::IAdaptiveRequirement* requirement) { + HString nameHString; + RETURN_IF_FAILED(requirement->get_Name(nameHString.GetAddressOf())); + + HString versionHString; + RETURN_IF_FAILED(requirement->get_Version(versionHString.GetAddressOf())); + + std::string nameString; + RETURN_IF_FAILED(HStringToUTF8(nameHString.Get(), nameString)); + + std::string versionString; + RETURN_IF_FAILED(HStringToUTF8(versionHString.Get(), versionString)); + + if (versionString == "*") + { + sharedRequirements->emplace(nameString, "0"); + } + else + { + sharedRequirements->emplace(nameString, versionString); + } + + return S_OK; + }); + + return S_OK; +} +CATCH_RETURN; + HRESULT GenerateSharedImages(_In_ ABI::Windows::Foundation::Collections::IVector* images, std::vector>& containedElements) { @@ -707,6 +744,20 @@ HRESULT GenerateInlinesProjection(const std::vector>& sharedRequirements, + _In_ ABI::Windows::Foundation::Collections::IVector* projectedRequirementVector) noexcept try +{ + for (auto& sharedRequirement : *sharedRequirements) + { + ComPtr projectedRequirement; + RETURN_IF_FAILED(MakeAndInitialize<::AdaptiveNamespace::AdaptiveRequirement>(&projectedRequirement, sharedRequirement)); + RETURN_IF_FAILED(projectedRequirementVector->Append(projectedRequirement.Detach())); + } + return S_OK; +} +CATCH_RETURN; + HRESULT GenerateImagesProjection(const std::vector>& containedElements, _In_ ABI::Windows::Foundation::Collections::IVector* projectedParentContainer) noexcept try { @@ -1359,6 +1410,21 @@ HRESULT ProjectedElementTypeToHString(ABI::AdaptiveNamespace::ElementType projec return UTF8ToHString(CardElementTypeToString(sharedElementType), result); } +HRESULT MeetsRequirements(_In_ ABI::AdaptiveNamespace::IAdaptiveCardElement* cardElement, + _In_ ABI::AdaptiveNamespace::IAdaptiveFeatureRegistration* featureRegistration, + _Out_ bool* meetsRequirements) +{ + std::shared_ptr sharedElement; + RETURN_IF_FAILED(GenerateSharedElement(cardElement, sharedElement)); + + ComPtr featureRegistrationImpl = PeekInnards(featureRegistration); + std::shared_ptr sharedFeatureRegistration = + featureRegistrationImpl->GetSharedFeatureRegistration(); + + *meetsRequirements = sharedElement->MeetsRequirements(*sharedFeatureRegistration); + return S_OK; +} + HRESULT IsBackgroundImageValid(_In_ ABI::AdaptiveNamespace::IAdaptiveBackgroundImage* backgroundImageElement, _Out_ BOOL* isValid) { *isValid = FALSE; diff --git a/source/uwp/Renderer/lib/Util.h b/source/uwp/Renderer/lib/Util.h index 48f460fec2..79cf437a8b 100644 --- a/source/uwp/Renderer/lib/Util.h +++ b/source/uwp/Renderer/lib/Util.h @@ -122,6 +122,10 @@ HRESULT GenerateSharedAction(_In_ ABI::AdaptiveNamespace::IAdaptiveActionElement HRESULT GenerateSharedActions(_In_ ABI::Windows::Foundation::Collections::IVector* items, std::vector>& containedElements); +HRESULT GenerateSharedRequirements( + _In_ ABI::Windows::Foundation::Collections::IVector* adaptiveRequirements, + std::shared_ptr> sharedRequirements) noexcept; + HRESULT GenerateSharedImages(_In_ ABI::Windows::Foundation::Collections::IVector* items, std::vector>& containedElements); @@ -164,6 +168,10 @@ HRESULT GenerateFactsProjection(const std::vector>& containedElements, _In_ ABI::Windows::Foundation::Collections::IVector* projectedParentContainer) noexcept; +HRESULT GenerateRequirementsProjection( + const std::shared_ptr>& sharedRequirements, + _In_ ABI::Windows::Foundation::Collections::IVector* projectedRequirementVector) noexcept; + HRESULT GenerateImagesProjection(const std::vector>& containedElements, _In_ ABI::Windows::Foundation::Collections::IVector* projectedParentContainer) noexcept; @@ -201,6 +209,10 @@ HRESULT JsonObjectToJsonCpp(_In_ ABI::Windows::Data::Json::IJsonObject* jsonObje HRESULT ProjectedActionTypeToHString(ABI::AdaptiveNamespace::ActionType projectedActionType, _Outptr_ HSTRING* result); HRESULT ProjectedElementTypeToHString(ABI::AdaptiveNamespace::ElementType projectedElementType, _Outptr_ HSTRING* result); +HRESULT MeetsRequirements(_In_ ABI::AdaptiveNamespace::IAdaptiveCardElement* cardElement, + _In_ ABI::AdaptiveNamespace::IAdaptiveFeatureRegistration* featureRegistration, + _Out_ bool* meetsRequirements); + HRESULT IsBackgroundImageValid(_In_ ABI::AdaptiveNamespace::IAdaptiveBackgroundImage* backgroundImageElement, _Out_ BOOL* isValid); typedef Microsoft::WRL::EventSource> ActionEventSource; diff --git a/source/uwp/Renderer/lib/XamlBuilder.cpp b/source/uwp/Renderer/lib/XamlBuilder.cpp index 80b3c7197a..4eecd31191 100644 --- a/source/uwp/Renderer/lib/XamlBuilder.cpp +++ b/source/uwp/Renderer/lib/XamlBuilder.cpp @@ -1129,8 +1129,8 @@ namespace AdaptiveNamespace RETURN_IF_FAILED(renderArgs->put_AncestorHasFallback(elementHasFallback || ancestorHasFallback)); // Check to see if element's requirements are being met - boolean requirementsMet; - RETURN_IF_FAILED(element->MeetsRequirements(featureRegistration.Get(), &requirementsMet)); + bool requirementsMet; + RETURN_IF_FAILED(MeetsRequirements(element, featureRegistration.Get(), &requirementsMet)); hr = requirementsMet ? S_OK : E_PERFORM_FALLBACK; // Get element renderer diff --git a/source/uwp/UWPUnitTests/FallbackTests.cs b/source/uwp/UWPUnitTests/FallbackTests.cs index c66b0e6019..b5fb33f0c2 100644 --- a/source/uwp/UWPUnitTests/FallbackTests.cs +++ b/source/uwp/UWPUnitTests/FallbackTests.cs @@ -66,5 +66,209 @@ await dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () => throw testException; } } + + [TestMethod] + public async Task RequiresAdaptiveCardsTest() + { + AdaptiveCard card = new AdaptiveCard(); + AdaptiveTextBlock textBlock1 = new AdaptiveTextBlock + { + Text = "Text Block 1 requires adaptive cards 5 and has fallback content." + }; + + AdaptiveTextBlock fallbackTextBlock = new AdaptiveTextBlock + { + Text = "Text Block 1 falls back to this." + }; + + textBlock1.FallbackContent = fallbackTextBlock; + textBlock1.Requirements.Add(new AdaptiveRequirement("adaptiveCards", "5.0")); + + Assert.AreEqual(FallbackType.Content, textBlock1.FallbackType); + + card.Body.Add(textBlock1); + + AdaptiveTextBlock textBlock2 = new AdaptiveTextBlock + { + Text = "Text Block 2 requires adaptive cards 5 and is dropped." + }; + + textBlock2.FallbackType = FallbackType.Drop; + textBlock2.Requirements.Add(new AdaptiveRequirement("adaptiveCards", "5.0")); + + Assert.AreEqual(FallbackType.Drop, textBlock2.FallbackType); + + card.Body.Add(textBlock2); + + AdaptiveTextBlock textBlock3 = new AdaptiveTextBlock + { + Text = "Text Block 3 requires adaptive cards 5 and has no specified fallback." + }; + + textBlock3.Requirements.Add(new AdaptiveRequirement("adaptiveCards", "5.0")); + + Assert.AreEqual(FallbackType.None, textBlock3.FallbackType); + + card.Body.Add(textBlock3); + + AdaptiveTextBlock textBlock4 = new AdaptiveTextBlock + { + Text = "Text Block 4 requires adaptive cards 1." + }; + + textBlock4.Requirements.Add(new AdaptiveRequirement("adaptiveCards", "1.0")); + + Assert.AreEqual(FallbackType.None, textBlock4.FallbackType); + + card.Body.Add(textBlock4); + + AdaptiveTextBlock textBlock5 = new AdaptiveTextBlock + { + Text = "Text Block 5 requires any version of adaptive cards." + }; + + textBlock5.Requirements.Add(new AdaptiveRequirement("adaptiveCards", "*")); + + Assert.AreEqual(FallbackType.None, textBlock5.FallbackType); + + card.Body.Add(textBlock5); + + var dispatcher = CoreApplication.MainView.CoreWindow.Dispatcher; + + Exception testException = null; + + // Need to move the test to the UI Thread + await dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () => + { + try + { + AdaptiveCardRenderer renderer = new AdaptiveCardRenderer(); + RenderedAdaptiveCard renderedCard = renderer.RenderAdaptiveCard(card); + + Assert.AreEqual(3, renderedCard.Warnings.Count); + Assert.AreEqual("Performing fallback for element of type \"TextBlock\" (fallback element type \"TextBlock\")", renderedCard.Warnings[0].Message); + Assert.AreEqual("Dropping element of type \"TextBlock\" for fallback", renderedCard.Warnings[1].Message); + Assert.AreEqual("No Renderer found for type: TextBlock", renderedCard.Warnings[2].Message); //Issue #3418 + } + catch (Exception thrown) + { + testException = thrown; + } + }); + + if (testException != null) + { + throw testException; + } + } + + [TestMethod] + public async Task RequiresCustomFeatureTest() + { + AdaptiveCard card = new AdaptiveCard(); + AdaptiveTextBlock textBlock1 = new AdaptiveTextBlock + { + Text = "Text Block 1 requires testFeature 5.0 and has fallback content." + }; + + AdaptiveTextBlock fallbackTextBlock = new AdaptiveTextBlock + { + Text = "Text Block 1 falls back to this." + }; + + textBlock1.FallbackContent = fallbackTextBlock; + textBlock1.Requirements.Add(new AdaptiveRequirement("testFeature", "5.0")); + + Assert.AreEqual(FallbackType.Content, textBlock1.FallbackType); + + card.Body.Add(textBlock1); + + AdaptiveTextBlock textBlock2 = new AdaptiveTextBlock + { + Text = "Text Block 2 requires testFeature 5.0 and is dropped." + }; + + textBlock2.FallbackType = FallbackType.Drop; + textBlock2.Requirements.Add(new AdaptiveRequirement("testFeature", "5.0")); + + Assert.AreEqual(FallbackType.Drop, textBlock2.FallbackType); + + card.Body.Add(textBlock2); + + AdaptiveTextBlock textBlock3 = new AdaptiveTextBlock + { + Text = "Text Block 3 requires testFeature 5 and has no specified fallback." + }; + + textBlock3.Requirements.Add(new AdaptiveRequirement("testFeature", "5.0")); + + Assert.AreEqual(FallbackType.None, textBlock3.FallbackType); + + card.Body.Add(textBlock3); + + AdaptiveTextBlock textBlock4 = new AdaptiveTextBlock + { + Text = "Text Block 4 requires testFeature 1." + }; + + textBlock4.Requirements.Add(new AdaptiveRequirement("adaptiveCards", "1.0")); + + Assert.AreEqual(FallbackType.None, textBlock4.FallbackType); + + card.Body.Add(textBlock4); + + AdaptiveTextBlock textBlock5 = new AdaptiveTextBlock + { + Text = "Text Block 5 requires any version of testFeature." + }; + + textBlock5.Requirements.Add(new AdaptiveRequirement("adaptiveCards", "*")); + + Assert.AreEqual(FallbackType.None, textBlock5.FallbackType); + + card.Body.Add(textBlock5); + + AdaptiveTextBlock textBlock6 = new AdaptiveTextBlock + { + Text = "Text Block 6 requires bogusFeature." + }; + + textBlock6.Requirements.Add(new AdaptiveRequirement("bogusFeature", "*")); + + Assert.AreEqual(FallbackType.None, textBlock6.FallbackType); + + card.Body.Add(textBlock6); + + var dispatcher = CoreApplication.MainView.CoreWindow.Dispatcher; + + Exception testException = null; + + // Need to move the test to the UI Thread + await dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () => + { + try + { + AdaptiveCardRenderer renderer = new AdaptiveCardRenderer(); + renderer.FeatureRegistration.Set("testFeature", "1.0"); + + RenderedAdaptiveCard renderedCard = renderer.RenderAdaptiveCard(card); + + Assert.AreEqual(4, renderedCard.Warnings.Count); + Assert.AreEqual("Performing fallback for element of type \"TextBlock\" (fallback element type \"TextBlock\")", renderedCard.Warnings[0].Message); + Assert.AreEqual("Dropping element of type \"TextBlock\" for fallback", renderedCard.Warnings[1].Message); + Assert.AreEqual("No Renderer found for type: TextBlock", renderedCard.Warnings[2].Message); //Issue #3418 + Assert.AreEqual("No Renderer found for type: TextBlock", renderedCard.Warnings[3].Message); //Issue #3418 + } + catch (Exception thrown) + { + testException = thrown; + } + }); + + if (testException != null) + { + throw testException; + } + } } } diff --git a/source/uwp/UWPUnitTests/ObjectModelTests.cs b/source/uwp/UWPUnitTests/ObjectModelTests.cs index 0c42da04b2..0c88fe5389 100644 --- a/source/uwp/UWPUnitTests/ObjectModelTests.cs +++ b/source/uwp/UWPUnitTests/ObjectModelTests.cs @@ -850,5 +850,35 @@ public void RichTextBlock() var jsonString = richTextBlock.ToJson().ToString(); Assert.AreEqual("{\"height\":\"Stretch\",\"horizontalAlignment\":\"center\",\"id\":\"RichTextBlockId\",\"inlines\":[{\"color\":\"Accent\",\"fontType\":\"Monospace\",\"highlight\":true,\"isSubtle\":true,\"italic\":true,\"selectAction\":{\"title\":\"Select Action\",\"type\":\"Action.Submit\"},\"size\":\"Large\",\"strikethrough\":true,\"text\":\"This is text run number 1\",\"type\":\"TextRun\",\"underline\":true,\"weight\":\"Bolder\"},{\"text\":\"This is text run number 2\",\"type\":\"TextRun\"},{\"text\":\"This is text run number 3\",\"type\":\"TextRun\"}],\"isVisible\":false,\"separator\":true,\"spacing\":\"large\",\"type\":\"RichTextBlock\"}", jsonString); } + + [TestMethod] + public void Fallback() + { + AdaptiveTextBlock textBlockDrop = new AdaptiveTextBlock + { + Text = "This text block has fallback type Drop", + FallbackType = FallbackType.Drop + }; + + textBlockDrop.Requirements.Add(new AdaptiveRequirement("foo", "1.2.3.4")); + + Assert.AreEqual(FallbackType.Drop, textBlockDrop.FallbackType); + + var jsonString = textBlockDrop.ToJson().ToString(); + Assert.AreEqual("{\"fallback\":\"drop\",\"requires\":{\"foo\":\"1.2.3.4\"},\"text\":\"This text block has fallback type Drop\",\"type\":\"TextBlock\"}", jsonString); + + AdaptiveTextBlock textBlockNone = new AdaptiveTextBlock + { + Text = "This text block has fallback explicitly set to None", + FallbackType = FallbackType.None + }; + + textBlockNone.Requirements.Add(new AdaptiveRequirement("foo", "*")); + + Assert.AreEqual(FallbackType.None, textBlockNone.FallbackType); + + jsonString = textBlockNone.ToJson().ToString(); + Assert.AreEqual("{\"requires\":{\"foo\":\"0.0.0.0\"},\"text\":\"This text block has fallback explicitly set to None\",\"type\":\"TextBlock\"}", jsonString); + } } } diff --git a/source/uwp/UWPUnitTests/ParserRegistration.cs b/source/uwp/UWPUnitTests/ParserRegistration.cs index 594ad9e7c2..52064e1fd4 100644 --- a/source/uwp/UWPUnitTests/ParserRegistration.cs +++ b/source/uwp/UWPUnitTests/ParserRegistration.cs @@ -91,11 +91,7 @@ public JsonObject ToJson() public UInt32 MinHeight { get; set; } IAdaptiveCardElement IAdaptiveCardElement.FallbackContent { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } FallbackType IAdaptiveCardElement.FallbackType { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - - public bool MeetsRequirements(AdaptiveFeatureRegistration featureRegistration) - { - return true; - } + public IList Requirements { get; set; } }; class TestElementParser : IAdaptiveElementParser {