From d8afee61994cbe870feec85dbc4225132f7b852a Mon Sep 17 00:00:00 2001 From: Ignacio Villanueva Date: Wed, 18 Jul 2018 08:56:16 +0200 Subject: [PATCH] Resolved Microsoft/Master into fix/develop merge conflicts (#49) * Add MSC directive to prevent cppcorecheck build failures on non-VS builds (#1552) * cleaned up exception handling in loading image (#1526) * Enable member init checks and fix errors (#1566) * A class with only static methods and no state should just be a namespace. (#1567) * Support OnParseAction Handler (#1570) * Honour preexpandSingleShowCardAction and weight of fact title from HostConfig * Addressed comments * Support OnParseAction * resolved minor comments * Jwoo/ios render sync (#1547) * make rendering to be syncronoush by default * updated custom textblock renderer * changes before CR * CR update * CR update * [.NET] Prevent async exceptions from tearing down the process (#1561) * Prevent unobserved exceptions from taking down the process. If rendering fails, report the error but do not throw on a background thread. * Add configuration to sign nuget packages * fixed dll/project names in comments * Fix actions parent not being set before actions are parsed (#1573) * Replace ContainerStyle string enum with class + static fields * Update default card * Improve hover behavior for Image with selectAction * Finalize support for width/height in Image and Column * Add support for icons on action buttons * Cleanup * Fix handling of buttons without title * Fix broken preExpandSingleShowCardAction handling * Fix actions parent not being set before actions are parsed * Enable const cppcore checks and fix fallout (#1575) * Add Events for media play and end (#1546) * [Android] Add vertical stretch property (#1525) * Add vertical stretch to android * Remove non related test file * Fix comments in FactSetRendered * Add tests as linked tests * Update tests results from TestApp with new tests * Jwoo/ios makrdown optimized (#1554) * make rendering to be syncronoush by default * updated custom textblock renderer * optimized use of html rendering * fixed factset issue * fixed build compatability issues below iOS 11.3 * fixes before CR * CR comments fix * Added additional headers to public lis in framework. (#1562) * make rendering to be syncronoush by default * updated custom textblock renderer * optimized use of html rendering * fixed factset issue * made headers public for customizing buttons * added ACRUILable.h * reverted wrong merge * Enable CPPCORECHECK_DECLARATION_WARNINGS and fix errors (#1579) * Enable Declaration warnings and first round of fixes * Clean up remaining unnamed objects warnings and disable C26426 in enums.cpp * added ability to scroll when there are more buttons than viewing area (#1581) * make rendering to be syncronoush by default * updated custom textblock renderer * optimized use of html rendering * fixed factset issue * fixed build compatability issues below iOS 11.3 * fixes before CR * added UIScrollView to ActionSet * edits before CR * Added sample json and updates b4 CR * Enable "rule of five" checker, fix errors (#1585) * Add Teams containers (#1469) Add support for more properties in property sheet Add initial support for snippets * [iOS] Add vertical stretch support (#1584) * Add vertical stretch to android * make rendering to be syncronoush by default * updated custom textblock renderer * optimized use of html rendering * fixed factset issue * Remove non related test file * Fix comments in FactSetRendered * Add tests as linked tests * Update tests results from TestApp with new tests * Add vertical stretch for items * Remove 'changes' from merge * Add vertical stretch tests to solution * Fix issue for image in columnsets rendering * Fix issue for images being shorter than other columns in columnset * Keep alignment leading for cards with no stretchable items * Add new test for auto size image * Enable all cppcorecheck class warnings and fix errors (#1601) * Enable override checker and fix errors * Enable function hiding warning and fix errors * Enable all class warnings and fix errors * [iOS] Add missing test for Vertical Stretch (#1603) * Add missing test * Fix spaces in json * Update GetResource method to include type and to support media (#1583) * Update get resource method to include type and to support media * Handle opening media via resource resolver when play is pressed (#1586) Handle opening media via resource resolver when play is pressed * Enable unit test code coverage in Debug Win32 builds (#1611) * Test source file updates (#1551) * Test file updates * Jwoo/ios custom styling (#1474) * XIB first commit * fixes merge error * updated InputView with XIB updated the style of UIButtons added UITableViewCell xib for compact mode removed comments * code clean up * added ACRbutton.xib * Added Xib for CompactCell * added ACRButton.xib * allowed column with numeric width value to stretch (#1595) * allowed column with numeric width value to stretch * fix merge and remove spacing for the first item * [UWP][Android][iOS] Add aesthetic fix Icons in Actions (#1612) * Add aesthetic fix to UWP * Aesthetic fix for android * Aesthetic fix for iOS * Add json test files to project * Fix formatting * updated showcard rendering position (#1600) removed comments * compact style choice set "make choice" row will reflect user choice (#1596) * in progress * when user chooses, user choice will be shown * updated default message * value is used instead of "Make Choice" * handles cases where compact style lacking defaults (#1627) * fixed separator issue (#1629) * Remove Stretch from vertical content alignment (#1630) * Jwoo/explicit image size ios (#1374) * explicit image changes * Added 'Explicit' enum value * uwp changes * uwp changes * added explicit image sizing option for ios * removed division * fixes extra spacing issue in UICollectionView (#1628) * fixes extra spacing issue in UICollectionView * refined intrinsic content size for collection view * fixed precision error * fixes url loading sync issue for actions (#1641) * fixes url loading sync issue for actions removed hard coded image icon placement * fixed indentation and changed variable name * Fix media controls (#1614) * [TS] Fixed Input.Text style and horizontalAlignment roundtrip; added test (#1636) * Input.Text roundtrip, added test and fix * feedback -vertical spaces * Change private for protected in all Renderers constructors (#1649) * Add vertical scroll support (#1650) * Jwoo/ios custom background image loader (#1642) * fixes url loading sync issue for actions removed hard coded image icon placement * allows custom BG IMG renderer * Added CustomImageRenderer to sample * removed CoreGraphics framework as it's not used * added a test to ensure Unicode/Emoji support (#1647) :shipit: * multiline input and input limit for textview and textfield (#1643) * initial commit for multiline input and input limit implmt plc hld txt; rmved max len chg when not set reverted json files * Refactoring and code clean-up * updated whitespaces * Fixed newline char * [Android] Refocus when a show card changes visibility (#1651) * Refocus on show card * Set focus for cards instead of input fields * Revert "[Android] Refocus when a show card changes visibility (#1651)" (#1653) This reverts commit f2a899a17eb686a42debb39dd1ac876ab8a3b629. * changed toggle view to UISwitch (#1631) * changed toggle view to UISwitch * dev complete * streamlined selection process * updated project file & code clean-up * added sanity check;chd borderstyle;corner radius * [Android] Refocus when a show card changes visibility (#1654) * Refocus on show card * Set focus for cards instead of input fields * Fix activity retrieval * Fix formatting and null check * [Android] Add custom renderer for actions and actionlayouts (#1655) * Add functions to register action renderer * Add IActionLayoutRenderer to support full actionLayout modification * Fix PR commentsa * 52619-to-much-padding-in-tree-view * 52614-tree-view-is-very-messy-as-it-is-resized-to-limited-width * [Android] Custom image downloading fix (#1666) * Add overrideable method loadOnlineImage to allow image loading modification * Fix merge errors * Fix styling for images * bitCode added (#1674) * priority update for factset title (#1670) * added explicit column width for ios (#1672) * Reverted to Fabric UI instead of SVGs (#33) * Reverted to Fabric UI instead of SVGs * Removed unnecesary comments * Removed unnecesary CSS and duplicated one (#35) * Fix bug in XceedNumberInput.Render (#1679) XceedNumberInput.Render was setting numberPicker.Minimum to input.Max. This fixes it to set numberPicker.Maximum instead. * Add a GitHub issue template (#1678) * Add a GitHub issue template * Add some more questions to issue template * Updates to CONTRIBUTING.md - Don't tell people to use the Schema label since non-contributors can't use labels - Eliminate some early preview wording - Fix some links * Direct questions to StackOverflow * Fix/use hostconfig to add container styles (#36) * background fix * Added background color via CSS again * Fixed some colours * Fixed class names in CSS and HTML * Moved interfaz to separate component * Fixed action and add column styles * Reverted removed commented code * Removed CSS properties panel color modification * Changed querySelector by getElementById * Removed commented code * [Android] Fix for vertical scroll in input text (#1689) * Add touchlistener for vertical scroll in input text * Fix styling * Jwoo/variablenumoflines choiceset (#1687) * updated content sz calcuation logic for inputview * Updated Toggle Switch Style * inputView refinements * added ACRDateTextField * disable custom background image loading * fixes line clipping issue of toggle switch * costmetic changes before CR * CR changes * Fix/hostconfig styles (#37) * Changed SVGs to FabriUI Font * Changed SVGs to FabriUI Font * Changed aside-panel icons when folded * Added cursor pointer to panel headers and avoided user-select it's text * Added cursor pointer to properties/treewview panels when folded * Added treeview icon fonts from FabricUI instead of SVGs * Fixed treeview icons positon * Fix/treeview implementation (#38) * Half of treeview refactor * Finished treeview * Removed unnecesary btn class and fixed treeview icons position * Removed unnecesary treeview component * Removed outline as David Claux did to match he's style * Mantained folded state when needed * Removed unnecesary console.log * Removed unnecesary icons (#39) * Increase the information in the version error message (#1697) Increase the information in the error message when version fails to parse, and mark version as required in order to get a more specific error message * Fix/minor microsoft pr changes (#40) * Removed unnecesary icons * Removed blank line * Fix/phase 0 (#41) * Changed aside panel background's color * Maded JSON togglable * Refactored CSS * Fixed PR comments * Reverse backwards if to allow image tracking (#1700) * Fix/phase 0 (#43) * Changed aside panel background's color * Maded JSON togglable * Refactored CSS * Fixed PR comments * Removed unnecesary code * Fix/json overflow (#46) * Fixed JSON title width * Moved to HTML monaco-editor's nav-title * Fixed toggle class implementation when updatingLayout in the treeview * Fixed aside scrollbar * Fixed properties panel label's width * Fixed JSON panel height when toggled * Fixed properties panel paddings and FactSet padding-right and padding-left * Fixed treeview background-color * [CPP] Fix/cpp small fixes (#1681) * this was breaking the build on case-sensitive FS * lesser evil (no autosave); better to ignore the file, methinks * removed unused * reordered * Revert "removed unused" since ES.84 This reverts commit c403517575dd7c1b26e26ff4b8ff4af695988497. * reordered * Set AutomationProperties.Name to AltText on Images (#1701) * added resource resolver (#1702) * added resource resolver * Added resource resolver class * Added interface file * chged NSString to NSURL as arg to the resolver IF * Media event changes (#1712) * Fixes UI Issues (#1709) * Minor UI fixes * chececk pointer against boolVal message * removed extra method call --- CONTRIBUTING.md => .github/CONTRIBUTING.md | 14 +- .github/ISSUE_TEMPLATE.md | 24 ++++ .vscode/settings.json | 6 +- .../renderer/ActionLayoutRenderer.java | 125 ++++++++++++++++++ .../renderer/AdaptiveCardRenderer.java | 98 +++----------- .../renderer/GenericImageLoaderAsync.java | 32 ++--- .../renderer/IActionLayoutRenderer.java | 13 ++ .../renderer/IOnlineImageLoader.java | 12 ++ .../renderer/OnlineImageLoader.java | 33 +++++ .../action/ActionElementRenderer.java | 27 ++++ .../renderer/input/TextInputRenderer.java | 36 ++++- .../renderer/readonly/ImageRenderer.java | 9 ++ .../CardRendererRegistration.java | 42 ++++++ .../XceedNumberInput.cs | 4 +- .../project.pbxproj | 10 ++ .../xcschemes/ADCIOSVisualizer.xcscheme | 2 +- .../ADCIOSVisualizer/ADCResolver.h | 14 ++ .../ADCIOSVisualizer/ADCResolver.m | 20 +++ .../ADCIOSVisualizer/ViewController.m | 13 +- .../ADCIOSVisualizer/resources/sample.json | 1 + .../AdaptiveCards.xcodeproj/project.pbxproj | 38 ++++-- .../AdaptiveCards-Universal.xcscheme | 2 +- .../xcschemes/AdaptiveCards.xcscheme | 2 +- .../AdaptiveCards/AdaptiveCards/ACFramework.h | 4 + .../AdaptiveCards/ACOHostConfig.h | 7 + .../AdaptiveCards/ACOHostConfig.mm | 34 +++-- .../AdaptiveCards/ACOIResourceResolver.h | 14 ++ .../AdaptiveCards/ACOResourceResolvers.h | 18 +++ .../AdaptiveCards/ACOResourceResolvers.mm | 34 +++++ .../AdaptiveCards/ACRActionOpenURLRenderer.mm | 3 +- .../ACRActionShowCardRenderer.mm | 3 +- .../AdaptiveCards/ACRActionSubmitRenderer.mm | 4 +- .../AdaptiveCards/ACRAggregateTarget.mm | 1 - .../AdaptiveCards/AdaptiveCards/ACRButton.h | 1 - .../AdaptiveCards/AdaptiveCards/ACRButton.mm | 3 +- .../AdaptiveCards/ACRCellForCompactMode.xib | 5 +- .../ACRChoiceSetViewDataSource.mm | 52 ++++---- .../ACRChoiceSetViewDataSourceCompactStyle.mm | 37 ++++-- .../AdaptiveCards/ACRColumnRenderer.mm | 5 +- .../AdaptiveCards/ACRColumnSetRenderer.mm | 10 +- .../AdaptiveCards/ACRColumnView.h | 1 + .../AdaptiveCards/ACRContainerRenderer.mm | 1 + .../AdaptiveCards/ACRContentStackView.mm | 10 +- .../AdaptiveCards/ACRDateTextField.mm | 3 +- .../AdaptiveCards/ACRFactSetRenderer.mm | 8 +- .../AdaptiveCards/ACRImageRenderer.mm | 3 +- .../ACRImageSetUICollectionView.mm | 2 +- .../ACRInputChoiceSetRenderer.mm | 19 ++- .../AdaptiveCards/ACRInputNumberRenderer.mm | 2 +- .../AdaptiveCards/ACRInputTableView.mm | 8 +- .../AdaptiveCards/ACRInputToggleRenderer.mm | 25 ++-- .../AdaptiveCards/ACRRenderer.mm | 7 +- .../AdaptiveCards/ACRTextView.mm | 1 + .../AdaptiveCards/ACRToggleInputDataSource.h | 4 +- .../AdaptiveCards/ACRToggleInputDataSource.mm | 98 +++++++------- .../AdaptiveCards/AdaptiveCards/ACRView.mm | 80 +++++------ .../AdaptiveCards/ACRViewPrivate.h | 5 +- .../AdaptiveCards/CMakeLists.txt | 2 +- .../ObjectModel/DateTimePreparsedToken.cpp | 6 +- source/shared/cpp/ObjectModel/MediaSource.h | 2 +- .../cpp/ObjectModel/SharedAdaptiveCard.cpp | 20 ++- .../idl/AdaptiveCards.Rendering.Uwp.idl | 10 +- .../lib/AdaptiveMediaEventInvoker.cpp | 10 +- .../Renderer/lib/AdaptiveMediaEventInvoker.h | 3 +- source/uwp/Renderer/lib/MediaHelpers.cpp | 7 +- .../uwp/Renderer/lib/RenderedAdaptiveCard.cpp | 37 +----- .../uwp/Renderer/lib/RenderedAdaptiveCard.h | 17 +-- source/uwp/Renderer/lib/XamlBuilder.cpp | 23 ++-- .../Visualizer/ViewModel/DocumentViewModel.cs | 21 +-- 69 files changed, 812 insertions(+), 435 deletions(-) rename CONTRIBUTING.md => .github/CONTRIBUTING.md (80%) create mode 100644 .github/ISSUE_TEMPLATE.md create mode 100644 source/android/adaptivecards/src/main/java/io/adaptivecards/renderer/ActionLayoutRenderer.java create mode 100644 source/android/adaptivecards/src/main/java/io/adaptivecards/renderer/IActionLayoutRenderer.java create mode 100644 source/android/adaptivecards/src/main/java/io/adaptivecards/renderer/IOnlineImageLoader.java create mode 100644 source/android/adaptivecards/src/main/java/io/adaptivecards/renderer/OnlineImageLoader.java create mode 100644 source/ios/AdaptiveCards/ADCIOSVisualizer/ADCIOSVisualizer/ADCResolver.h create mode 100644 source/ios/AdaptiveCards/ADCIOSVisualizer/ADCIOSVisualizer/ADCResolver.m create mode 100644 source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOIResourceResolver.h create mode 100644 source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOResourceResolvers.h create mode 100644 source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOResourceResolvers.mm diff --git a/CONTRIBUTING.md b/.github/CONTRIBUTING.md similarity index 80% rename from CONTRIBUTING.md rename to .github/CONTRIBUTING.md index e90771e7d0..b11375833f 100644 --- a/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -7,21 +7,23 @@ ## 2. Do you have a question? Please use the issue tracker for bugs and suggestions. -If you have a *question*, please use [Stack Overflow](http://stackoverflow.com/questions/tagged/adaptive-cards) + +If you have a *question*, please use [Stack Overflow](https://stackoverflow.com/questions/tagged/adaptive-cards) ## 3. Did you find a bug? -I'm not surprised, we're still in early preview so there are plenty of them right now :) +I'm not surprised, we're still in the early stages, so there are plenty of them right now :) When logging a bug, please be sure to include the following: * The platform you were using * If at all possible, an *isolated* way to reproduce the behavior * The behavior you expect to see, and the actual behavior + ## 4. Do you have a suggestion? We also accept suggestions in the issue tracker. -Please take a look at our [Core Design Principles](http://adaptivecards.io/documentation/#about-overview) to make sure the suggestion is aligned with the project goals. +Please take a look at our [Core Design Principles](https://docs.microsoft.com/en-us/adaptive-cards/#core-design-principles) to make sure the suggestion is aligned with the project goals. In general, things we find useful when reviewing suggestions are: * A description of the problem you're trying to solve @@ -32,13 +34,15 @@ In general, things we find useful when reviewing suggestions are: ## Contributing bug fixes -Adaptive Cards are current in early preview so there's a lot of churn in the code. We're still accepting contributions in the form of bug fixes. +Our local dev team is in active development, but we are still accepting external contributions in the form of bug fixes. + A bug must have an issue tracking it in the issue tracker that has been approved by the Adaptive Cards team. Your pull request should include a link to the bug that you are fixing. If you've submitted a PR for a bug, please post a comment in the bug to avoid duplication of effort. ## Contributing features We're happy to discuss schema proposals as long as they align with our [Core Design Principles](http://adaptivecards.io/documentation/#about-overview). -Please open an issue with the `Schema` label to get a discussion started. + +Please open an issue with your schema proposal to get a discussion started. ## Legal diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 0000000000..a7ac9a897a --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,24 @@ +# Platform + +What platform is your issue or question related to? (Delete other platforms). + +* .NET HTML +* .NET WPF +* Android +* iOS +* JavaScript +* UWP + +# Author or host + +Are you an author (like sending something to Outlook), or a host that is rendering your own cards? + +If you're an author, who are you sending cards to? + +# Version of SDK + +What version are you using? Ex: NuGet 1.0.2, or latest master, etc... + +# Issue + +Explain your issue. If you just have a question, please post [on StackOverflow instead](https://stackoverflow.com/questions/tagged/adaptive-cards). diff --git a/.vscode/settings.json b/.vscode/settings.json index 4a4d37e4ed..e0d94afb4b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,6 @@ { - "editor.insertSpaces": false, - "editor.formatOnSave": true, // only if you want auto fomattting on saving the file + "editor.insertSpaces": false, + "editor.formatOnSave": false, // only if you want auto fomattting on saving the file "editor.detectIndentation": false, - + "C_Cpp.default.cppStandard": "c++17" } \ No newline at end of file diff --git a/source/android/adaptivecards/src/main/java/io/adaptivecards/renderer/ActionLayoutRenderer.java b/source/android/adaptivecards/src/main/java/io/adaptivecards/renderer/ActionLayoutRenderer.java new file mode 100644 index 0000000000..2e5595bdbf --- /dev/null +++ b/source/android/adaptivecards/src/main/java/io/adaptivecards/renderer/ActionLayoutRenderer.java @@ -0,0 +1,125 @@ +package io.adaptivecards.renderer; + +import android.content.Context; +import android.support.v4.app.FragmentManager; +import android.view.Gravity; +import android.view.ViewGroup; +import android.widget.HorizontalScrollView; +import android.widget.LinearLayout; + +import io.adaptivecards.objectmodel.ActionAlignment; +import io.adaptivecards.objectmodel.ActionsOrientation; +import io.adaptivecards.objectmodel.BaseActionElement; +import io.adaptivecards.objectmodel.BaseActionElementVector; +import io.adaptivecards.objectmodel.HostConfig; +import io.adaptivecards.objectmodel.IconPlacement; +import io.adaptivecards.objectmodel.Spacing; +import io.adaptivecards.renderer.AdaptiveWarning; +import io.adaptivecards.renderer.BaseCardElementRenderer; +import io.adaptivecards.renderer.IActionLayoutRenderer; +import io.adaptivecards.renderer.IBaseActionElementRenderer; +import io.adaptivecards.renderer.RenderedAdaptiveCard; +import io.adaptivecards.renderer.actionhandler.ICardActionHandler; +import io.adaptivecards.renderer.registration.CardRendererRegistration; + +public class ActionLayoutRenderer implements IActionLayoutRenderer { + + protected ActionLayoutRenderer() + { + } + + public static ActionLayoutRenderer getInstance() + { + if (s_instance == null) + { + s_instance = new ActionLayoutRenderer(); + } + + return s_instance; + } + + public void renderActions(RenderedAdaptiveCard renderedCard, Context context, FragmentManager fragmentManager, ViewGroup viewGroup, BaseActionElementVector baseActionElementList, ICardActionHandler cardActionHandler, HostConfig hostConfig) { + long size; + if (baseActionElementList == null || (size = baseActionElementList.size()) <= 0) + { + return; + } + + LinearLayout actionButtonsLayout = new LinearLayout(context); + actionButtonsLayout.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); + int alignment = hostConfig.getActions().getActionAlignment().swigValue(); + if (alignment == ActionAlignment.Right.swigValue()) + { + actionButtonsLayout.setGravity(Gravity.RIGHT); + } + else if (alignment == ActionAlignment.Center.swigValue()) + { + actionButtonsLayout.setGravity(Gravity.CENTER_HORIZONTAL); + } + + int actionButtonsLayoutOrientation = hostConfig.getActions().getActionsOrientation().swigValue(); + if (actionButtonsLayoutOrientation == ActionsOrientation.Vertical.swigValue()) + { + actionButtonsLayout.setOrientation(LinearLayout.VERTICAL); + } + else + { + actionButtonsLayout.setOrientation(LinearLayout.HORIZONTAL); + } + + Spacing spacing = hostConfig.getActions().getSpacing(); + /* Passing false for separator since we do not have any configuration for separator in actionsConfig */ + BaseCardElementRenderer.setSpacingAndSeparator(context, viewGroup, spacing, false, hostConfig, true /* Horizontal Line */); + + if (viewGroup != null) + { + if(actionButtonsLayoutOrientation == ActionsOrientation.Horizontal.swigValue()) + { + HorizontalScrollView actionButtonsContainer = new HorizontalScrollView(context); + actionButtonsContainer.setHorizontalScrollBarEnabled(false); + actionButtonsContainer.addView(actionButtonsLayout); + viewGroup.addView(actionButtonsContainer); + } + else + { + viewGroup.addView(actionButtonsLayout); + } + } + + int i = 0; + long maxActions = hostConfig.getActions().getMaxActions(); + + boolean allActionsHaveIcons = true; + for(; i < size && i < maxActions; ++i) + { + BaseActionElement actionElement = baseActionElementList.get(i); + if(actionElement.GetIconUrl().isEmpty()) + { + allActionsHaveIcons = false; + break; + } + } + + for (i = 0; i < size && i < maxActions; i++) + { + BaseActionElement actionElement = baseActionElementList.get(i); + + IconPlacement originalIconPlacement = hostConfig.getActions().getIconPlacement(); + if(!allActionsHaveIcons) + { + hostConfig.getActions().setIconPlacement(IconPlacement.LeftOfTitle); + } + + IBaseActionElementRenderer actionRenderer = CardRendererRegistration.getInstance().getActionRenderer(); + actionRenderer.render(renderedCard, context, fragmentManager, actionButtonsLayout, actionElement, cardActionHandler, hostConfig); + hostConfig.getActions().setIconPlacement(originalIconPlacement); + } + + if (i >= maxActions && size != maxActions) + { + renderedCard.addWarning(new AdaptiveWarning(AdaptiveWarning.MAX_ACTIONS_EXCEEDED, "A maximum of " + maxActions + " actions are allowed")); + } + } + + private static ActionLayoutRenderer s_instance = null; +} diff --git a/source/android/adaptivecards/src/main/java/io/adaptivecards/renderer/AdaptiveCardRenderer.java b/source/android/adaptivecards/src/main/java/io/adaptivecards/renderer/AdaptiveCardRenderer.java index d220c341f4..34b4488300 100644 --- a/source/android/adaptivecards/src/main/java/io/adaptivecards/renderer/AdaptiveCardRenderer.java +++ b/source/android/adaptivecards/src/main/java/io/adaptivecards/renderer/AdaptiveCardRenderer.java @@ -116,6 +116,8 @@ public View internalRender(RenderedAdaptiveCard renderedCard, LinearLayout rootLayout = new LinearLayout(context); rootLayout.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); rootLayout.setOrientation(LinearLayout.VERTICAL); + rootLayout.setFocusable(true); + rootLayout.setFocusableInTouchMode(true); LinearLayout layout = new LinearLayout(context); layout.setTag(adaptiveCard); @@ -178,7 +180,10 @@ public View internalRender(RenderedAdaptiveCard renderedCard, showCardsLayout.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); rootLayout.addView(showCardsLayout); - renderActions(renderedCard, context, fragmentManager, layout, baseActionElementList, cardActionHandler, hostConfig); + IActionLayoutRenderer actionLayoutRenderer = CardRendererRegistration.getInstance().getActionLayoutRenderer(); + if(actionLayoutRenderer != null) { + actionLayoutRenderer.renderActions(renderedCard, context, fragmentManager, layout, baseActionElementList, cardActionHandler, hostConfig); + } } } else @@ -190,6 +195,13 @@ public View internalRender(RenderedAdaptiveCard renderedCard, if (!imageUrl.isEmpty()) { BackgroundImageLoaderAsync loaderAsync = new BackgroundImageLoaderAsync(renderedCard, context, layout, hostConfig.getImageBaseUrl()); + + IOnlineImageLoader onlineImageLoader = CardRendererRegistration.getInstance().getOnlineImageLoader(); + if(onlineImageLoader != null) + { + loaderAsync.registerCustomOnlineImageLoader(onlineImageLoader); + } + loaderAsync.execute(imageUrl); } @@ -203,89 +215,9 @@ public View internalRender(RenderedAdaptiveCard renderedCard, return rootLayout; } - private void renderActions(RenderedAdaptiveCard renderedCard, Context context, FragmentManager fragmentManager, ViewGroup viewGroup, BaseActionElementVector baseActionElementList, ICardActionHandler cardActionHandler, HostConfig hostConfig) { - long size; - if (baseActionElementList == null || (size = baseActionElementList.size()) <= 0) - { - return; - } - - LinearLayout actionButtonsLayout = new LinearLayout(context); - actionButtonsLayout.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); - int alignment = hostConfig.getActions().getActionAlignment().swigValue(); - if (alignment == ActionAlignment.Right.swigValue()) - { - actionButtonsLayout.setGravity(Gravity.RIGHT); - } - else if (alignment == ActionAlignment.Center.swigValue()) - { - actionButtonsLayout.setGravity(Gravity.CENTER_HORIZONTAL); - } - - int actionButtonsLayoutOrientation = hostConfig.getActions().getActionsOrientation().swigValue(); - if (actionButtonsLayoutOrientation == ActionsOrientation.Vertical.swigValue()) - { - actionButtonsLayout.setOrientation(LinearLayout.VERTICAL); - } - else - { - actionButtonsLayout.setOrientation(LinearLayout.HORIZONTAL); - } - - - Spacing spacing = hostConfig.getActions().getSpacing(); - /* Passing false for seperator since we do not have any configuration for seperator in actionsConfig */ - BaseCardElementRenderer.setSpacingAndSeparator(context, viewGroup, spacing, false, hostConfig, true /* Horizontal Line */); - - if (viewGroup != null) - { - if(actionButtonsLayoutOrientation == ActionsOrientation.Horizontal.swigValue()) - { - HorizontalScrollView actionButtonsContainer = new HorizontalScrollView(context); - actionButtonsContainer.setHorizontalScrollBarEnabled(false); - actionButtonsContainer.addView(actionButtonsLayout); - viewGroup.addView(actionButtonsContainer); - } - else - { - viewGroup.addView(actionButtonsLayout); - } - } - - int i = 0; - long maxActions = hostConfig.getActions().getMaxActions(); - - boolean allActionsHaveIcons = true; - for(; i < size && i < maxActions; ++i) - { - BaseActionElement actionElement = baseActionElementList.get(i); - if(actionElement.GetIconUrl().isEmpty()) - { - allActionsHaveIcons = false; - break; - } - } - - for (i = 0; i < size && i < maxActions; i++) - { - BaseActionElement actionElement = baseActionElementList.get(i); - - IconPlacement originalIconPlacement = hostConfig.getActions().getIconPlacement(); - if(!allActionsHaveIcons) - { - hostConfig.getActions().setIconPlacement(IconPlacement.LeftOfTitle); - } - ActionElementRenderer.getInstance().render(renderedCard, context, fragmentManager, actionButtonsLayout, actionElement, cardActionHandler, hostConfig); - hostConfig.getActions().setIconPlacement(originalIconPlacement); - } - - if (i >= maxActions && size != maxActions) - { - renderedCard.addWarning(new AdaptiveWarning(AdaptiveWarning.MAX_ACTIONS_EXCEEDED, "A maximum of " + maxActions + " actions are allowed")); - } - } - private static AdaptiveCardRenderer s_instance = null; + private IOnlineImageLoader m_onlineImageLoader = null; + private HostConfig defaultHostConfig = new HostConfig(); } diff --git a/source/android/adaptivecards/src/main/java/io/adaptivecards/renderer/GenericImageLoaderAsync.java b/source/android/adaptivecards/src/main/java/io/adaptivecards/renderer/GenericImageLoaderAsync.java index 100905f3e2..a099a889d0 100644 --- a/source/android/adaptivecards/src/main/java/io/adaptivecards/renderer/GenericImageLoaderAsync.java +++ b/source/android/adaptivecards/src/main/java/io/adaptivecards/renderer/GenericImageLoaderAsync.java @@ -20,11 +20,13 @@ public abstract class GenericImageLoaderAsync extends AsyncTask loadImage(String path, Context context) // Try loading online using only the path first try { - return loadOnlineImage(path); + return m_onlineImageLoader.loadOnlineImage(path, this); } catch (MalformedURLException e1) { // Then try using image base URL to load online @@ -50,7 +52,8 @@ HttpRequestResult loadImage(String path, Context context) URL urlContext = new URL(m_imageBaseUrl); URL url = new URL(urlContext, path); - return loadOnlineImage(url.toString()); + return m_onlineImageLoader.loadOnlineImage(url.toString(), this); + // return loadOnlineImage(url.toString()); } catch (MalformedURLException e2) { @@ -65,26 +68,6 @@ HttpRequestResult loadImage(String path, Context context) } } - // Helper function to load online image URL - private HttpRequestResult loadOnlineImage(String url) throws IOException, URISyntaxException - { - byte[] bytes = HttpRequestHelper.get(url); - if (bytes == null) - { - throw new IOException("Failed to retrieve content from " + url); - } - - Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length); - bitmap = styleBitmap(bitmap); - - if (bitmap == null) - { - throw new IOException("Failed to convert content to bitmap: " + new String(bytes)); - } - - return new HttpRequestResult<>(bitmap); - } - // Helper function to load local image URL from res/ private HttpRequestResult loadLocalImage(String imageBaseUrl, Context context, String url) throws IOException { @@ -127,5 +110,10 @@ protected void onPostExecute(HttpRequestResult result) } } + public void registerCustomOnlineImageLoader(IOnlineImageLoader onlineImageLoader) + { + m_onlineImageLoader = onlineImageLoader; + } + abstract void onSuccessfulPostExecute(Bitmap result); } diff --git a/source/android/adaptivecards/src/main/java/io/adaptivecards/renderer/IActionLayoutRenderer.java b/source/android/adaptivecards/src/main/java/io/adaptivecards/renderer/IActionLayoutRenderer.java new file mode 100644 index 0000000000..13acb6f944 --- /dev/null +++ b/source/android/adaptivecards/src/main/java/io/adaptivecards/renderer/IActionLayoutRenderer.java @@ -0,0 +1,13 @@ +package io.adaptivecards.renderer; + +import android.content.Context; +import android.support.v4.app.FragmentManager; +import android.view.ViewGroup; + +import io.adaptivecards.objectmodel.BaseActionElementVector; +import io.adaptivecards.objectmodel.HostConfig; +import io.adaptivecards.renderer.actionhandler.ICardActionHandler; + +public interface IActionLayoutRenderer { + public void renderActions(RenderedAdaptiveCard renderedCard, Context context, FragmentManager fragmentManager, ViewGroup viewGroup, BaseActionElementVector baseActionElementList, ICardActionHandler cardActionHandler, HostConfig hostConfig); +} diff --git a/source/android/adaptivecards/src/main/java/io/adaptivecards/renderer/IOnlineImageLoader.java b/source/android/adaptivecards/src/main/java/io/adaptivecards/renderer/IOnlineImageLoader.java new file mode 100644 index 0000000000..9b4a4df997 --- /dev/null +++ b/source/android/adaptivecards/src/main/java/io/adaptivecards/renderer/IOnlineImageLoader.java @@ -0,0 +1,12 @@ +package io.adaptivecards.renderer; + +import android.graphics.Bitmap; + +import java.io.IOException; +import java.net.URISyntaxException; + +import io.adaptivecards.renderer.http.HttpRequestResult; + +public interface IOnlineImageLoader { + HttpRequestResult loadOnlineImage(String url, GenericImageLoaderAsync loader) throws IOException, URISyntaxException; +} diff --git a/source/android/adaptivecards/src/main/java/io/adaptivecards/renderer/OnlineImageLoader.java b/source/android/adaptivecards/src/main/java/io/adaptivecards/renderer/OnlineImageLoader.java new file mode 100644 index 0000000000..bde5e3ff54 --- /dev/null +++ b/source/android/adaptivecards/src/main/java/io/adaptivecards/renderer/OnlineImageLoader.java @@ -0,0 +1,33 @@ +package io.adaptivecards.renderer; + +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; + +import java.io.IOException; +import java.net.URISyntaxException; + +import io.adaptivecards.renderer.http.HttpRequestHelper; +import io.adaptivecards.renderer.http.HttpRequestResult; + +public class OnlineImageLoader implements IOnlineImageLoader +{ + @Override + public HttpRequestResult loadOnlineImage(String url, GenericImageLoaderAsync loader) throws IOException, URISyntaxException + { + byte[] bytes = HttpRequestHelper.get(url); + if (bytes == null) + { + throw new IOException("Failed to retrieve content from " + url); + } + + Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length); + bitmap = loader.styleBitmap(bitmap); + + if (bitmap == null) + { + throw new IOException("Failed to convert content to bitmap: " + new String(bytes)); + } + + return new HttpRequestResult<>(bitmap); + } +} diff --git a/source/android/adaptivecards/src/main/java/io/adaptivecards/renderer/action/ActionElementRenderer.java b/source/android/adaptivecards/src/main/java/io/adaptivecards/renderer/action/ActionElementRenderer.java index 2f192c6216..d27292c0e3 100644 --- a/source/android/adaptivecards/src/main/java/io/adaptivecards/renderer/action/ActionElementRenderer.java +++ b/source/android/adaptivecards/src/main/java/io/adaptivecards/renderer/action/ActionElementRenderer.java @@ -1,12 +1,15 @@ package io.adaptivecards.renderer.action; +import android.app.Activity; import android.content.Context; +import android.content.ContextWrapper; import android.graphics.Bitmap; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.support.v4.app.FragmentManager; +import android.view.ContextThemeWrapper; import android.view.View; import android.view.ViewGroup; import android.widget.Button; @@ -108,9 +111,32 @@ public ShowCardInlineClickListener(View invisibleCard, ViewGroup hiddenCardsLayo m_hiddenCardsLayout = hiddenCardsLayout; } + private Activity getActivity(Context context) + { + while (context instanceof ContextWrapper) + { + if (context instanceof Activity) + { + return (Activity)context; + } + context = ((ContextWrapper)context).getBaseContext(); + } + return null; + } + @Override public void onClick(View v) { + Activity hostingActivity = getActivity(v.getContext()); + if(hostingActivity != null) + { + View currentFocusedView = hostingActivity.getCurrentFocus(); + if (currentFocusedView != null) + { + currentFocusedView.clearFocus(); + } + } + v.setPressed(m_invisibleCard.getVisibility() != View.VISIBLE); for(int i = 0; i < m_hiddenCardsLayout.getChildCount(); ++i) { @@ -130,6 +156,7 @@ public void onClick(View v) if (m_invisibleCard.getVisibility() == View.VISIBLE) { mainCardView.setPadding(padding, padding, padding, 0); + m_invisibleCard.requestFocus(); } else { diff --git a/source/android/adaptivecards/src/main/java/io/adaptivecards/renderer/input/TextInputRenderer.java b/source/android/adaptivecards/src/main/java/io/adaptivecards/renderer/input/TextInputRenderer.java index 68f5cfa6cb..9fd92d0a8a 100644 --- a/source/android/adaptivecards/src/main/java/io/adaptivecards/renderer/input/TextInputRenderer.java +++ b/source/android/adaptivecards/src/main/java/io/adaptivecards/renderer/input/TextInputRenderer.java @@ -75,6 +75,33 @@ else if (textInputStyle == TextInputStyle.Email) } } + private class EditTextTouchListener implements View.OnTouchListener + { + EditTextTouchListener(Object tag) + { + m_tag = tag; + } + + @Override + public boolean onTouch(View v, MotionEvent event) + { + // Solution taken from here: https://stackoverflow.com/questions/6123973/android-edittext-vertical-scrolling-problem + if (v.getTag() == m_tag) + { + v.getParent().requestDisallowInterceptTouchEvent(true); + switch (event.getAction() & MotionEvent.ACTION_MASK) + { + case MotionEvent.ACTION_UP: + v.getParent().requestDisallowInterceptTouchEvent(false); + break; + } + } + return false; + } + + private Object m_tag = null; + } + protected EditText renderInternal( RenderedAdaptiveCard renderedCard, Context context, @@ -145,7 +172,7 @@ else if ((textInput = TextInput.dynamic_cast(baseCardElement)) == null) TextInputHandler textInputHandler = new TextInputHandler(textInput); setSpacingAndSeparator(context, viewGroup, textInput.GetSpacing(), textInput.GetSeparator(), hostConfig, true /* horizontal line */); - EditText editText = renderInternal( + final EditText editText = renderInternal( renderedCard, context, viewGroup, @@ -155,12 +182,13 @@ else if ((textInput = TextInput.dynamic_cast(baseCardElement)) == null) textInputHandler, hostConfig); editText.setSingleLine(!textInput.GetIsMultiline()); + editText.setTag(textInput); if (textInput.GetIsMultiline()) { editText.setLines(3); - editText.setScroller(new Scroller((context))); - editText.setVerticalScrollBarEnabled(true); - editText.setMovementMethod(new ScrollingMovementMethod()); + // Solution taken from here: https://stackoverflow.com/questions/6123973/android-edittext-vertical-scrolling-problem + editText.setOnTouchListener(new EditTextTouchListener(textInput)); + } setTextInputStyle(editText, textInput.GetTextInputStyle()); int maxLength = (int) Math.min(textInput.GetMaxLength(), Integer.MAX_VALUE); diff --git a/source/android/adaptivecards/src/main/java/io/adaptivecards/renderer/readonly/ImageRenderer.java b/source/android/adaptivecards/src/main/java/io/adaptivecards/renderer/readonly/ImageRenderer.java index 35a1b34273..c6e6a02f1e 100644 --- a/source/android/adaptivecards/src/main/java/io/adaptivecards/renderer/readonly/ImageRenderer.java +++ b/source/android/adaptivecards/src/main/java/io/adaptivecards/renderer/readonly/ImageRenderer.java @@ -16,6 +16,8 @@ import io.adaptivecards.objectmodel.ContainerStyle; import io.adaptivecards.objectmodel.HeightType; +import io.adaptivecards.renderer.AdaptiveCardRenderer; +import io.adaptivecards.renderer.IOnlineImageLoader; import io.adaptivecards.renderer.InnerImageLoaderAsync; import io.adaptivecards.renderer.RenderedAdaptiveCard; import io.adaptivecards.renderer.Util; @@ -30,6 +32,7 @@ import io.adaptivecards.objectmodel.ImageStyle; import io.adaptivecards.renderer.BaseCardElementRenderer; import io.adaptivecards.renderer.layout.HorizontalFlowLayout; +import io.adaptivecards.renderer.registration.CardRendererRegistration; public class ImageRenderer extends BaseCardElementRenderer { @@ -124,6 +127,12 @@ else if ((image = Image.dynamic_cast(baseCardElement)) == null) ImageView imageView = new ImageView(context); imageView.setTag(image); ImageRendererImageLoaderAsync imageLoaderAsync = new ImageRendererImageLoaderAsync(renderedCard, imageView, hostConfig.getImageBaseUrl(), image.GetImageStyle()); + + IOnlineImageLoader onlineImageLoader = CardRendererRegistration.getInstance().getOnlineImageLoader(); + if(onlineImageLoader != null) + { + imageLoaderAsync.registerCustomOnlineImageLoader(onlineImageLoader); + } imageLoaderAsync.execute(image.GetUrl()); LinearLayout.LayoutParams layoutParams; diff --git a/source/android/adaptivecards/src/main/java/io/adaptivecards/renderer/registration/CardRendererRegistration.java b/source/android/adaptivecards/src/main/java/io/adaptivecards/renderer/registration/CardRendererRegistration.java index 7196322214..7d26ed9420 100644 --- a/source/android/adaptivecards/src/main/java/io/adaptivecards/renderer/registration/CardRendererRegistration.java +++ b/source/android/adaptivecards/src/main/java/io/adaptivecards/renderer/registration/CardRendererRegistration.java @@ -9,7 +9,12 @@ import io.adaptivecards.objectmodel.ContainerStyle; import io.adaptivecards.renderer.AdaptiveWarning; +import io.adaptivecards.renderer.IOnlineImageLoader; +import io.adaptivecards.renderer.IActionLayoutRenderer; +import io.adaptivecards.renderer.IBaseActionElementRenderer; import io.adaptivecards.renderer.RenderedAdaptiveCard; +import io.adaptivecards.renderer.action.ActionElementRenderer; +import io.adaptivecards.renderer.ActionLayoutRenderer; import io.adaptivecards.renderer.actionhandler.ICardActionHandler; import io.adaptivecards.objectmodel.BaseCardElement; import io.adaptivecards.objectmodel.BaseCardElementVector; @@ -54,6 +59,10 @@ private CardRendererRegistration() registerRenderer(CardElementTypeToString(CardElementType.TimeInput), TimeInputRenderer.getInstance()); registerRenderer(CardElementTypeToString(CardElementType.ToggleInput), ToggleInputRenderer.getInstance()); registerRenderer(CardElementTypeToString(CardElementType.ChoiceSetInput), ChoiceSetInputRenderer.getInstance()); + + // Register Action Renderer + m_actionRenderer = ActionElementRenderer.getInstance(); + m_actionLayoutRenderer = ActionLayoutRenderer.getInstance(); } public static CardRendererRegistration getInstance() @@ -85,6 +94,36 @@ public IBaseCardElementRenderer getRenderer(String cardElementType) return m_typeToRendererMap.get(cardElementType); } + public void registerOnlineImageLoader(IOnlineImageLoader imageLoader) + { + m_onlineImageLoader = imageLoader; + } + + public IOnlineImageLoader getOnlineImageLoader() + { + return m_onlineImageLoader; + } + + public void registerActionRenderer(IBaseActionElementRenderer actionRenderer) + { + m_actionRenderer = actionRenderer; + } + + public IBaseActionElementRenderer getActionRenderer() + { + return m_actionRenderer; + } + + public void registerActionLayoutRenderer(IActionLayoutRenderer actionLayoutRenderer) + { + m_actionLayoutRenderer = actionLayoutRenderer; + } + + public IActionLayoutRenderer getActionLayoutRenderer() + { + return m_actionLayoutRenderer; + } + public View render( RenderedAdaptiveCard renderedCard, Context context, @@ -131,4 +170,7 @@ public View render( private static CardRendererRegistration s_instance = null; private HashMap m_typeToRendererMap = new HashMap(); + private IBaseActionElementRenderer m_actionRenderer = null; + private IActionLayoutRenderer m_actionLayoutRenderer = null; + private IOnlineImageLoader m_onlineImageLoader = null; } diff --git a/source/dotnet/Library/AdaptiveCards.Rendering.Wpf.Xceed/XceedNumberInput.cs b/source/dotnet/Library/AdaptiveCards.Rendering.Wpf.Xceed/XceedNumberInput.cs index 91d9fe3e0d..117da22934 100644 --- a/source/dotnet/Library/AdaptiveCards.Rendering.Wpf.Xceed/XceedNumberInput.cs +++ b/source/dotnet/Library/AdaptiveCards.Rendering.Wpf.Xceed/XceedNumberInput.cs @@ -21,7 +21,7 @@ public static FrameworkElement Render(AdaptiveNumberInput input, AdaptiveRenderC numberPicker.Minimum = Convert.ToInt32(input.Min); if (!Double.IsNaN(input.Max)) - numberPicker.Minimum = Convert.ToInt32(input.Max); + numberPicker.Maximum = Convert.ToInt32(input.Max); numberPicker.Watermark = input.Placeholder; numberPicker.Style = context.GetStyle("Adaptive.Input.Number"); @@ -38,4 +38,4 @@ public static FrameworkElement Render(AdaptiveNumberInput input, AdaptiveRenderC } } -} \ No newline at end of file +} diff --git a/source/ios/AdaptiveCards/ADCIOSVisualizer/ADCIOSVisualizer.xcodeproj/project.pbxproj b/source/ios/AdaptiveCards/ADCIOSVisualizer/ADCIOSVisualizer.xcodeproj/project.pbxproj index 36e64fabac..6450acd28d 100644 --- a/source/ios/AdaptiveCards/ADCIOSVisualizer/ADCIOSVisualizer.xcodeproj/project.pbxproj +++ b/source/ios/AdaptiveCards/ADCIOSVisualizer/ADCIOSVisualizer.xcodeproj/project.pbxproj @@ -26,6 +26,8 @@ 6B9AB30620D9857B005C8E15 /* Image.Explicit.Size.json in Resources */ = {isa = PBXBuildFile; fileRef = 6B9AB30520D9857A005C8E15 /* Image.Explicit.Size.json */; }; 6B9AB30C20DC4FB3005C8E15 /* IconsInSomeActions.json in Resources */ = {isa = PBXBuildFile; fileRef = 6B9AB30B20DC4FB3005C8E15 /* IconsInSomeActions.json */; }; 6B9BDF7020E18E5B00F13155 /* CustomImageRenderer.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6B9BDF6F20E18E5B00F13155 /* CustomImageRenderer.mm */; }; + 6B9BDF7C20E6E0C200F13155 /* Column.Explicit.Size.json in Resources */ = {isa = PBXBuildFile; fileRef = 6B9BDF7B20E6E0C200F13155 /* Column.Explicit.Size.json */; }; + 6B9BDFCC20F6D11F00F13155 /* ADCResolver.m in Sources */ = {isa = PBXBuildFile; fileRef = 6B9BDFCB20F6D11F00F13155 /* ADCResolver.m */; }; 6BF339D620A665E600DA5973 /* CustomTextBlockRenderer.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6BF339D420A665E600DA5973 /* CustomTextBlockRenderer.mm */; }; 6BF339E320A66A3F00DA5973 /* AdaptiveCards.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6BF339E220A66A3F00DA5973 /* AdaptiveCards.framework */; }; 6BF339E420A66A4D00DA5973 /* AdaptiveCards.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 6BF339E220A66A3F00DA5973 /* AdaptiveCards.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; @@ -145,6 +147,9 @@ 6B9AB30B20DC4FB3005C8E15 /* IconsInSomeActions.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = IconsInSomeActions.json; path = ../../../../samples/Tests/IconsInSomeActions.json; sourceTree = ""; }; 6B9BDF6E20E18E5B00F13155 /* CustomImageRenderer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CustomImageRenderer.h; sourceTree = ""; }; 6B9BDF6F20E18E5B00F13155 /* CustomImageRenderer.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = CustomImageRenderer.mm; sourceTree = ""; }; + 6B9BDF7B20E6E0C200F13155 /* Column.Explicit.Size.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = Column.Explicit.Size.json; path = ../../../../samples/Tests/Column.Explicit.Size.json; sourceTree = ""; }; + 6B9BDFCB20F6D11F00F13155 /* ADCResolver.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ADCResolver.m; sourceTree = ""; }; + 6B9BDFCD20F6D16E00F13155 /* ADCResolver.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ADCResolver.h; sourceTree = ""; }; 6BF339D420A665E600DA5973 /* CustomTextBlockRenderer.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = CustomTextBlockRenderer.mm; sourceTree = ""; }; 6BF339D520A665E600DA5973 /* CustomTextBlockRenderer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CustomTextBlockRenderer.h; sourceTree = ""; }; 6BF339E220A66A3F00DA5973 /* AdaptiveCards.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = AdaptiveCards.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -314,6 +319,8 @@ F423C0791EE1FB6100905679 /* AppDelegate.m */, F423C07B1EE1FB6100905679 /* ViewController.h */, F423C07C1EE1FB6100905679 /* ViewController.m */, + 6B9BDFCB20F6D11F00F13155 /* ADCResolver.m */, + 6B9BDFCD20F6D16E00F13155 /* ADCResolver.h */, F423C07E1EE1FB6100905679 /* Main.storyboard */, F423C0841EE1FB6100905679 /* Assets.xcassets */, F423C0861EE1FB6100905679 /* LaunchScreen.storyboard */, @@ -353,6 +360,7 @@ F4D33E341F045B6E00941E44 /* Jsons */ = { isa = PBXGroup; children = ( + 6B9BDF7B20E6E0C200F13155 /* Column.Explicit.Size.json */, 6B9AB30B20DC4FB3005C8E15 /* IconsInSomeActions.json */, 6B9AB30520D9857A005C8E15 /* Image.Explicit.Size.json */, 30A3885C20D315AA00AAEE59 /* NotificationCard.json */, @@ -575,6 +583,7 @@ F423C0881EE1FB6100905679 /* LaunchScreen.storyboard in Resources */, F423C0851EE1FB6100905679 /* Assets.xcassets in Resources */, F4933CE01F79852C00F6EBFD /* Input.Text.Style.json in Resources */, + 6B9BDF7C20E6E0C200F13155 /* Column.Explicit.Size.json in Resources */, F4933CC71F79852C00F6EBFD /* Column.Spacing.json in Resources */, F4933CC31F79852C00F6EBFD /* Action.ShowCard.json in Resources */, F4933CD31F79852C00F6EBFD /* FactSet.json in Resources */, @@ -629,6 +638,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 6B9BDFCC20F6D11F00F13155 /* ADCResolver.m in Sources */, F4F44BAA204CF98900A2F24C /* CustomProgressBarRenderer.mm in Sources */, F4D33E861F04705800941E44 /* ACVTableViewController.m in Sources */, 6BF339D620A665E600DA5973 /* CustomTextBlockRenderer.mm in Sources */, diff --git a/source/ios/AdaptiveCards/ADCIOSVisualizer/ADCIOSVisualizer.xcodeproj/xcshareddata/xcschemes/ADCIOSVisualizer.xcscheme b/source/ios/AdaptiveCards/ADCIOSVisualizer/ADCIOSVisualizer.xcodeproj/xcshareddata/xcschemes/ADCIOSVisualizer.xcscheme index 309444afb1..323b156b4f 100644 --- a/source/ios/AdaptiveCards/ADCIOSVisualizer/ADCIOSVisualizer.xcodeproj/xcshareddata/xcschemes/ADCIOSVisualizer.xcscheme +++ b/source/ios/AdaptiveCards/ADCIOSVisualizer/ADCIOSVisualizer.xcodeproj/xcshareddata/xcschemes/ADCIOSVisualizer.xcscheme @@ -1,6 +1,6 @@ + +#import + +@interface ADCResolver:NSObject + +@end diff --git a/source/ios/AdaptiveCards/ADCIOSVisualizer/ADCIOSVisualizer/ADCResolver.m b/source/ios/AdaptiveCards/ADCIOSVisualizer/ADCIOSVisualizer/ADCResolver.m new file mode 100644 index 0000000000..5c8c66ee8e --- /dev/null +++ b/source/ios/AdaptiveCards/ADCIOSVisualizer/ADCIOSVisualizer/ADCResolver.m @@ -0,0 +1,20 @@ +// +// ADCResolver.m +// ADCIOSVisualizer +// +// Created by Inyoung Woo on 7/11/18. +// Copyright © 2018 Microsoft. All rights reserved. +// + +#import "ADCResolver.h" + +@implementation ADCResolver + +- (UIImage *)resolveImageResource:(NSURL *)url { + // download image + UIImage *img = [UIImage imageWithData:[NSData dataWithContentsOfURL:url]]; + return img; +} + +@end + diff --git a/source/ios/AdaptiveCards/ADCIOSVisualizer/ADCIOSVisualizer/ViewController.m b/source/ios/AdaptiveCards/ADCIOSVisualizer/ADCIOSVisualizer/ViewController.m index f952e1e583..55a83b11f3 100644 --- a/source/ios/AdaptiveCards/ADCIOSVisualizer/ADCIOSVisualizer/ViewController.m +++ b/source/ios/AdaptiveCards/ADCIOSVisualizer/ADCIOSVisualizer/ViewController.m @@ -12,10 +12,12 @@ #import "CustomProgressBarRenderer.h" #import "CustomTextBlockRenderer.h" #import "CustomImageRenderer.h" +#import "ADCResolver.h" @interface ViewController () { BOOL _enableCustomRenderer; + ACOResourceResolvers *_resolvers; } @end @@ -71,12 +73,14 @@ - (IBAction)toggleCustomRenderer:(id)sender [registration setBaseCardElementRenderer:[CustomTextBlockRenderer getInstance] cardElementType:ACRTextBlock]; [registration setBaseCardElementRenderer:[CustomInputNumberRenderer getInstance] cardElementType:ACRNumberInput]; [registration setBaseCardElementRenderer:[CustomImageRenderer getInstance] cardElementType:ACRImage]; + _enableCustomRendererButton.backgroundColor = UIColor.redColor; } else { [registration setActionRenderer:nil cardElementType:@3]; [registration setBaseCardElementRenderer:nil cardElementType:ACRTextBlock]; [registration setBaseCardElementRenderer:nil cardElementType:ACRNumberInput]; [registration setBaseCardElementRenderer:nil cardElementType:ACRImage]; + _enableCustomRendererButton.backgroundColor = [UIColor colorWithRed:0/255 green:122.0/255 blue:1 alpha:1]; } [self update:self.editableStr]; } @@ -99,6 +103,9 @@ - (IBAction)applyText:(id)sender - (void)viewDidLoad { [super viewDidLoad]; [self registerForKeyboardNotifications]; + _resolvers = [[ACOResourceResolvers alloc] init]; + ADCResolver *resolver = [[ADCResolver alloc] init]; + [_resolvers setResourceResolver:resolver scheme:@"http"]; _enableCustomRenderer = NO; self.curView = nil; self.ACVTabVC = [[ACVTableViewController alloc] init]; @@ -202,7 +209,7 @@ - (void)update:(NSString *) jsonStr { self.editableStr = jsonStr; ACRRenderResult *renderResult; - ACOHostConfigParseResult *hostconfigParseResult = [ACOHostConfig fromJson:self.hostconfig]; + ACOHostConfigParseResult *hostconfigParseResult = [ACOHostConfig fromJson:self.hostconfig resourceResolvers:_resolvers]; ACOAdaptiveCardParseResult *cardParseResult = [ACOAdaptiveCard fromJson:jsonStr]; if(cardParseResult.isValid){ ACRRegistration *registration = [ACRRegistration getInstance]; @@ -210,7 +217,7 @@ - (void)update:(NSString *) jsonStr CustomProgressBarRenderer *progressBarRenderer = [[CustomProgressBarRenderer alloc] init]; [registration setCustomElementParser:progressBarRenderer]; - renderResult = [ACRRenderer render:cardParseResult.card config:hostconfigParseResult.config widthConstraint:300]; + renderResult = [ACRRenderer render:cardParseResult.card config:hostconfigParseResult.config widthConstraint:335]; } if(renderResult.succeeded) @@ -228,7 +235,7 @@ - (void)update:(NSString *) jsonStr [NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:_scrView attribute:NSLayoutAttributeTop multiplier:1.0 constant:0].active = YES; [NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:_scrView attribute:NSLayoutAttributeBottom multiplier:1.0 constant:0].active = YES; - [NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:_scrView attribute:NSLayoutAttributeLeading multiplier:1.0 constant:0].active = YES; + [NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:_scrView attribute:NSLayoutAttributeLeading multiplier:1.0 constant:3].active = YES; [NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:_scrView attribute:NSLayoutAttributeTrailing multiplier:1.0 constant:0].active = YES; } diff --git a/source/ios/AdaptiveCards/ADCIOSVisualizer/resources/sample.json b/source/ios/AdaptiveCards/ADCIOSVisualizer/resources/sample.json index 80bd8452da..91dbb611a3 100644 --- a/source/ios/AdaptiveCards/ADCIOSVisualizer/resources/sample.json +++ b/source/ios/AdaptiveCards/ADCIOSVisualizer/resources/sample.json @@ -110,6 +110,7 @@ "color": "Default", "isSubtle": false, "weight": "Bolder", + "maxWidth":150, "warp": true }, "value": { diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards.xcodeproj/project.pbxproj b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards.xcodeproj/project.pbxproj index 8df9fe9ed4..d6e4a4fe73 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards.xcodeproj/project.pbxproj +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards.xcodeproj/project.pbxproj @@ -37,8 +37,13 @@ 6B7B1A9820BE2CBC00260731 /* ACRUIImageView.h in Headers */ = {isa = PBXBuildFile; fileRef = 6B7B1A9620BE2CBC00260731 /* ACRUIImageView.h */; settings = {ATTRIBUTES = (Public, ); }; }; 6B9AB2FD20D327DB005C8E15 /* ACRCellForCompactMode.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6B9AB2FB20D327DB005C8E15 /* ACRCellForCompactMode.xib */; }; 6B9AB30220D32A89005C8E15 /* ACRButton.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6B9AB30120D32A89005C8E15 /* ACRButton.xib */; }; - 6B9AB31020DD82A2005C8E15 /* ACRTextView.h in Headers */ = {isa = PBXBuildFile; fileRef = 6B9AB30E20DD82A2005C8E15 /* ACRTextView.h */; }; + 6B9AB31020DD82A2005C8E15 /* ACRTextView.h in Headers */ = {isa = PBXBuildFile; fileRef = 6B9AB30E20DD82A2005C8E15 /* ACRTextView.h */; settings = {ATTRIBUTES = (Public, ); }; }; 6B9AB31120DD82A2005C8E15 /* ACRTextView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6B9AB30F20DD82A2005C8E15 /* ACRTextView.mm */; }; + 6B9BDF7320E1BD0E00F13155 /* ACRToggleInputDataSource.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6B9BDF7120E1BD0E00F13155 /* ACRToggleInputDataSource.mm */; }; + 6B9BDF7420E1BD0E00F13155 /* ACRToggleInputDataSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 6B9BDF7220E1BD0E00F13155 /* ACRToggleInputDataSource.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 6B9BDF7F20F40D1000F13155 /* ACOResourceResolvers.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6B9BDF7D20F40D0F00F13155 /* ACOResourceResolvers.mm */; }; + 6B9BDF8020F40D1000F13155 /* ACOResourceResolvers.h in Headers */ = {isa = PBXBuildFile; fileRef = 6B9BDF7E20F40D1000F13155 /* ACOResourceResolvers.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 6B9BDFCA20F6BF5D00F13155 /* ACOIResourceResolver.h in Headers */ = {isa = PBXBuildFile; fileRef = 6B9BDFC920F6BF5D00F13155 /* ACOIResourceResolver.h */; settings = {ATTRIBUTES = (Public, ); }; }; 6BF339D320A6649500DA5973 /* json.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = F4071C771FCCBAEF00AF4FEA /* json.h */; }; F401A8771F0DB69B006D7AF2 /* ACRImageSetUICollectionView.h in Headers */ = {isa = PBXBuildFile; fileRef = F401A8751F0DB69B006D7AF2 /* ACRImageSetUICollectionView.h */; settings = {ATTRIBUTES = (Public, ); }; }; F401A8781F0DB69B006D7AF2 /* ACRImageSetUICollectionView.mm in Sources */ = {isa = PBXBuildFile; fileRef = F401A8761F0DB69B006D7AF2 /* ACRImageSetUICollectionView.mm */; }; @@ -86,8 +91,6 @@ F43110441F357487001AAE30 /* ACRInputTableView.mm in Sources */ = {isa = PBXBuildFile; fileRef = F431103E1F357487001AAE30 /* ACRInputTableView.mm */; }; F43110451F357487001AAE30 /* ACOHostConfig.h in Headers */ = {isa = PBXBuildFile; fileRef = F431103F1F357487001AAE30 /* ACOHostConfig.h */; settings = {ATTRIBUTES = (Public, ); }; }; F43110461F357487001AAE30 /* ACOHostConfig.mm in Sources */ = {isa = PBXBuildFile; fileRef = F43110401F357487001AAE30 /* ACOHostConfig.mm */; }; - F43110471F357487001AAE30 /* ACRToggleInputDataSource.h in Headers */ = {isa = PBXBuildFile; fileRef = F43110411F357487001AAE30 /* ACRToggleInputDataSource.h */; settings = {ATTRIBUTES = (Public, ); }; }; - F43110481F357487001AAE30 /* ACRToggleInputDataSource.mm in Sources */ = {isa = PBXBuildFile; fileRef = F43110421F357487001AAE30 /* ACRToggleInputDataSource.mm */; }; F43660781F0706D800EBA868 /* SharedAdaptiveCard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F43660761F0706D800EBA868 /* SharedAdaptiveCard.cpp */; }; F43660791F0706D800EBA868 /* SharedAdaptiveCard.h in Headers */ = {isa = PBXBuildFile; fileRef = F43660771F0706D800EBA868 /* SharedAdaptiveCard.h */; settings = {ATTRIBUTES = (Public, ); }; }; F43A94111F1D60E30001920B /* ACRFactSetRenderer.mm in Sources */ = {isa = PBXBuildFile; fileRef = F43A940F1F1D60E30001920B /* ACRFactSetRenderer.mm */; }; @@ -286,6 +289,11 @@ 6B9AB30120D32A89005C8E15 /* ACRButton.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ACRButton.xib; sourceTree = ""; }; 6B9AB30E20DD82A2005C8E15 /* ACRTextView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ACRTextView.h; sourceTree = ""; }; 6B9AB30F20DD82A2005C8E15 /* ACRTextView.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ACRTextView.mm; sourceTree = ""; }; + 6B9BDF7120E1BD0E00F13155 /* ACRToggleInputDataSource.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ACRToggleInputDataSource.mm; sourceTree = ""; }; + 6B9BDF7220E1BD0E00F13155 /* ACRToggleInputDataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ACRToggleInputDataSource.h; sourceTree = ""; }; + 6B9BDF7D20F40D0F00F13155 /* ACOResourceResolvers.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ACOResourceResolvers.mm; sourceTree = ""; }; + 6B9BDF7E20F40D1000F13155 /* ACOResourceResolvers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ACOResourceResolvers.h; sourceTree = ""; }; + 6B9BDFC920F6BF5D00F13155 /* ACOIResourceResolver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ACOIResourceResolver.h; sourceTree = ""; }; F401A8751F0DB69B006D7AF2 /* ACRImageSetUICollectionView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ACRImageSetUICollectionView.h; sourceTree = ""; }; F401A8761F0DB69B006D7AF2 /* ACRImageSetUICollectionView.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ACRImageSetUICollectionView.mm; sourceTree = ""; }; F401A8791F0DCBC8006D7AF2 /* ACRImageSetRenderer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ACRImageSetRenderer.h; sourceTree = ""; }; @@ -341,8 +349,6 @@ F431103E1F357487001AAE30 /* ACRInputTableView.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ACRInputTableView.mm; sourceTree = ""; }; F431103F1F357487001AAE30 /* ACOHostConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ACOHostConfig.h; sourceTree = ""; }; F43110401F357487001AAE30 /* ACOHostConfig.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ACOHostConfig.mm; sourceTree = ""; }; - F43110411F357487001AAE30 /* ACRToggleInputDataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ACRToggleInputDataSource.h; sourceTree = ""; }; - F43110421F357487001AAE30 /* ACRToggleInputDataSource.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ACRToggleInputDataSource.mm; sourceTree = ""; }; F43660761F0706D800EBA868 /* SharedAdaptiveCard.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SharedAdaptiveCard.cpp; path = ../../../../shared/cpp/ObjectModel/SharedAdaptiveCard.cpp; sourceTree = ""; }; F43660771F0706D800EBA868 /* SharedAdaptiveCard.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SharedAdaptiveCard.h; path = ../../../../shared/cpp/ObjectModel/SharedAdaptiveCard.h; sourceTree = ""; }; F43A940E1F1D60E30001920B /* ACRFactSetRenderer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ACRFactSetRenderer.h; sourceTree = ""; }; @@ -577,6 +583,9 @@ 6B1147D01F32E53A008846EC /* ACRActionDelegate.h */, F42979401F322C3E00E89914 /* ACRErrors.h */, F42979411F322C3E00E89914 /* ACRErrors.mm */, + 6B9BDFC920F6BF5D00F13155 /* ACOIResourceResolver.h */, + 6B9BDF7E20F40D1000F13155 /* ACOResourceResolvers.h */, + 6B9BDF7D20F40D0F00F13155 /* ACOResourceResolvers.mm */, F423C0B91EE1FBAA00905679 /* Info.plist */, ); path = AdaptiveCards; @@ -707,8 +716,8 @@ F431103E1F357487001AAE30 /* ACRInputTableView.mm */, F43A94161F20502D0001920B /* ACRInputToggleRenderer.h */, F43A94171F20502D0001920B /* ACRInputToggleRenderer.mm */, - F43110411F357487001AAE30 /* ACRToggleInputDataSource.h */, - F43110421F357487001AAE30 /* ACRToggleInputDataSource.mm */, + 6B9BDF7220E1BD0E00F13155 /* ACRToggleInputDataSource.h */, + 6B9BDF7120E1BD0E00F13155 /* ACRToggleInputDataSource.mm */, 6B6840F61F25EC2D008A933F /* ACRInputChoiceSetRenderer.h */, 6B6840F71F25EC2D008A933F /* ACRInputChoiceSetRenderer.mm */, F495FC0D2022AC920093D4DE /* ACRChoiceSetViewDataSourceCompactStyle.h */, @@ -871,7 +880,6 @@ F448731D1EE2261F00FCAFAE /* ParseUtil.h in Headers */, 6B7B1A9320B4D2AB00260731 /* MediaSource.h in Headers */, F448731E1EE2261F00FCAFAE /* pch.h in Headers */, - 6B9AB31020DD82A2005C8E15 /* ACRTextView.h in Headers */, F448730A1EE2261F00FCAFAE /* Enums.h in Headers */, F44872F61EE2261F00FCAFAE /* AdaptiveCardParseException.h in Headers */, F44872FA1EE2261F00FCAFAE /* BaseCardElement.h in Headers */, @@ -941,10 +949,13 @@ F4F6BA38204F2954003741B6 /* ACRParseWarningPrivate.h in Headers */, F49683541F6CA24600DF0D3A /* ACRRenderResult.h in Headers */, F401A8771F0DB69B006D7AF2 /* ACRImageSetUICollectionView.h in Headers */, - F43110471F357487001AAE30 /* ACRToggleInputDataSource.h in Headers */, F4F44BA1204CED2400A2F24C /* ACRCustomRenderer.h in Headers */, 6B1147D81F32F922008846EC /* ACRShowCardTarget.h in Headers */, + 6B9AB31020DD82A2005C8E15 /* ACRTextView.h in Headers */, + 6B9BDF7420E1BD0E00F13155 /* ACRToggleInputDataSource.h in Headers */, 6B3787BB20CB3E0E00015401 /* ACRContentHoldingUIScrollView.h in Headers */, + 6B9BDF8020F40D1000F13155 /* ACOResourceResolvers.h in Headers */, + 6B9BDFCA20F6BF5D00F13155 /* ACOIResourceResolver.h in Headers */, F495FC0B2022A18F0093D4DE /* ACRChoiceSetViewDataSource.h in Headers */, F4C1F5E81F2ABB3C0018CB78 /* ACRIBaseActionElementRenderer.h in Headers */, F4C1F5EB1F2ABD6B0018CB78 /* ACRBaseActionElementRenderer.h in Headers */, @@ -1001,7 +1012,7 @@ F423C0AC1EE1FBA900905679 /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0930; + LastUpgradeCheck = 0940; ORGANIZATIONNAME = Microsoft; TargetAttributes = { F423C0B41EE1FBA900905679 = { @@ -1097,6 +1108,7 @@ buildActionMask = 2147483647; files = ( F4F44B8E204A145200A2F24C /* ACOBaseCardElement.mm in Sources */, + 6B9BDF7F20F40D1000F13155 /* ACOResourceResolvers.mm in Sources */, F42741291EFB374A00399FBB /* ACRColumnSetRenderer.mm in Sources */, F448731C1EE2261F00FCAFAE /* ParseUtil.cpp in Sources */, F44873291EE2261F00FCAFAE /* ToggleInput.cpp in Sources */, @@ -1138,7 +1150,6 @@ F44872F51EE2261F00FCAFAE /* AdaptiveCardParseException.cpp in Sources */, F429794D1F32684900E89914 /* ACRDateTextField.mm in Sources */, F4F44B8120478C6F00A2F24C /* Util.cpp in Sources */, - F43110481F357487001AAE30 /* ACRToggleInputDataSource.mm in Sources */, 6B6840F91F25EC2D008A933F /* ACRInputChoiceSetRenderer.mm in Sources */, F42741131EF873A600399FBB /* ACRImageRenderer.mm in Sources */, F448730B1EE2261F00FCAFAE /* Fact.cpp in Sources */, @@ -1183,6 +1194,7 @@ F4F6BA3B204F3109003741B6 /* ACRAggregateTarget.mm in Sources */, F42741251EFB274C00399FBB /* ACRColumnRenderer.mm in Sources */, F4F255701F98247600A80D39 /* ACOBaseActionElement.mm in Sources */, + 6B9BDF7320E1BD0E00F13155 /* ACRToggleInputDataSource.mm in Sources */, F4C1F5DA1F218ABC0018CB78 /* ACRInputTimeRenderer.mm in Sources */, F4D402121F7DAC2C00D0356B /* ACOAdaptiveCardParseResult.mm in Sources */, F43110441F357487001AAE30 /* ACRInputTableView.mm in Sources */, @@ -1219,6 +1231,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; @@ -1280,6 +1293,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; @@ -1335,6 +1349,7 @@ F423C0CA1EE1FBAA00905679 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + BITCODE_GENERATION_MODE = bitcode; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; DEFINES_MODULE = YES; @@ -1367,6 +1382,7 @@ F423C0CB1EE1FBAA00905679 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + BITCODE_GENERATION_MODE = bitcode; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; DEFINES_MODULE = YES; diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards.xcodeproj/xcshareddata/xcschemes/AdaptiveCards-Universal.xcscheme b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards.xcodeproj/xcshareddata/xcschemes/AdaptiveCards-Universal.xcscheme index e893bd4cac..1330aa9d03 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards.xcodeproj/xcshareddata/xcschemes/AdaptiveCards-Universal.xcscheme +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards.xcodeproj/xcshareddata/xcschemes/AdaptiveCards-Universal.xcscheme @@ -1,6 +1,6 @@ +#import + +#import + #import #import diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOHostConfig.h b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOHostConfig.h index fe907acc1e..a9a7bc7e47 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOHostConfig.h +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOHostConfig.h @@ -7,14 +7,21 @@ #import #import "ACOHostConfigParseResult.h" +#import "ACOResourceResolvers.h" @interface ACOHostConfig:NSObject @property NSArray *fontFamilyNames; @property BOOL allActionsHaveIcons; @property CGFloat buttonPadding; +@property ACOResourceResolvers *resolvers; + - (instancetype)init; +- (NSObject *)getResourceResolverForScheme:(NSString *)scheme; + + (ACOHostConfigParseResult *)fromJson:(NSString *)payload; ++ (ACOHostConfigParseResult *)fromJson:(NSString *)payload resourceResolvers:(ACOResourceResolvers *)resolvers; + @end diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOHostConfig.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOHostConfig.mm index 3472edc56e..c97a7c4b08 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOHostConfig.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOHostConfig.mm @@ -42,20 +42,17 @@ - (instancetype)initWithConfig:(std::shared_ptr const &)config return self; } -+ (ACOHostConfigParseResult *)fromJson:(NSString *)payload; ++ (ACOHostConfigParseResult *)fromJson:(NSString *)payload resourceResolvers:(ACOResourceResolvers *)resolvers { ACOHostConfigParseResult *result = nil; - - if(payload) - { - try - { + + if(payload) { + try { std::shared_ptr cHostConfig = std::make_shared(AdaptiveCards::HostConfig::DeserializeFromString(std::string([payload UTF8String]))); ACOHostConfig *config= [[ACOHostConfig alloc] initWithConfig:cHostConfig]; result = [[ACOHostConfigParseResult alloc] init:config errors:nil]; - } - catch(const AdaptiveCardParseException& e) - { + config->_resolvers = resolvers; + } catch(const AdaptiveCardParseException& e) { // converts AdaptiveCardParseException to NSError ErrorStatusCode errorStatusCode = e.GetStatusCode(); NSInteger errorCode = (long)errorStatusCode; @@ -69,6 +66,16 @@ + (ACOHostConfigParseResult *)fromJson:(NSString *)payload; return result; } ++ (ACOHostConfigParseResult *)fromJson:(NSString *)payload; +{ + ACOHostConfigParseResult *result = nil; + + if(payload) { + result = [ACOHostConfig fromJson:payload resourceResolvers:nil]; + } + return result; +} + - (std::shared_ptr)getHostConfig { return _config; @@ -79,6 +86,15 @@ - (void)setHostConfig:(std::shared_ptr const &)config _config = config; } +- (NSObject *)getResourceResolverForScheme:(NSString *)scheme +{ + if(!scheme) { + return nil; + } + + return [_resolvers getResourceResolverForScheme:scheme]; +} + + (UIColor *)getTextBlockColor:(ForegroundColor)txtClr colorsConfig:(ColorsConfig const &)config subtleOption:(bool)isSubtle diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOIResourceResolver.h b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOIResourceResolver.h new file mode 100644 index 0000000000..d797281d3e --- /dev/null +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOIResourceResolver.h @@ -0,0 +1,14 @@ +// +// ACOIResourceResolver.h +// ACOIResourceResolver +// +// Copyright © 2018 Microsoft. All rights reserved. +// + +#import + +@protocol ACOIResourceResolver + +- (UIImage *)resolveImageResource:(NSURL *)url; + +@end diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOResourceResolvers.h b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOResourceResolvers.h new file mode 100644 index 0000000000..77aa1fee84 --- /dev/null +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOResourceResolvers.h @@ -0,0 +1,18 @@ +// +// ACOResourceResolvers.h +// ACOResourceResolvers +// +// Copyright © 2018 Microsoft. All rights reserved. +// + +#import +#import +#import "ACOIResourceResolver.h" + +@interface ACOResourceResolvers:NSObject + +- (instancetype)init; +- (NSObject *)getResourceResolverForScheme:(NSString *)scheme; +- (void)setResourceResolver:(NSObject *)resolver scheme:(NSString *)scheme; + +@end diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOResourceResolvers.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOResourceResolvers.mm new file mode 100644 index 0000000000..753fc9abb3 --- /dev/null +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOResourceResolvers.mm @@ -0,0 +1,34 @@ +// +// ACOResourceResolvers.mm +// ACOResourceResolvers.h +// +// Copyright © 2018 Microsoft. All rights reserved. +// +#import +#import "ACOResourceResolvers.h" + +@implementation ACOResourceResolvers +{ + NSMutableDictionary *> *_resolvers; +} + +- (instancetype)init +{ + self = [super init]; + if (self) { + _resolvers = [[NSMutableDictionary alloc] init]; + } + return self; +} + +- (void)setResourceResolver:(NSObject *)resolver scheme:(NSString *)scheme +{ + self->_resolvers[scheme] = resolver; +} + +- (NSObject *)getResourceResolverForScheme:(NSString *)scheme +{ + return self->_resolvers[scheme]; +} + +@end diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRActionOpenURLRenderer.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRActionOpenURLRenderer.mm index 53277e988b..bb06ddf3bb 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRActionOpenURLRenderer.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRActionOpenURLRenderer.mm @@ -31,9 +31,8 @@ - (UIButton* )renderButton:(ACRView *)rootView std::shared_ptr action = std::dynamic_pointer_cast(elem); NSString *title = [NSString stringWithCString:action->GetTitle().c_str() encoding:NSUTF8StringEncoding]; - NSString *iconUrl = [NSString stringWithCString:action->GetUrl().c_str() encoding:(NSUTF8StringEncoding)]; - UIButton *button = [UIButton rootView:rootView baseActionElement:acoElem title:title iconUrl:iconUrl andHostConfig:acoConfig]; + UIButton *button = [UIButton rootView:rootView baseActionElement:acoElem title:title andHostConfig:acoConfig]; ACRAggregateTarget *target = [[ACRAggregateTarget alloc] initWithActionElement:acoElem rootView:(ACRView *)rootView]; diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRActionShowCardRenderer.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRActionShowCardRenderer.mm index b5fcbc2607..8794b57c5a 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRActionShowCardRenderer.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRActionShowCardRenderer.mm @@ -31,9 +31,8 @@ - (UIButton* )renderButton:(ACRView *)rootView std::shared_ptr action = std::dynamic_pointer_cast(elem); NSString *title = [NSString stringWithCString:action->GetTitle().c_str() encoding:NSUTF8StringEncoding]; - NSString *iconUrl = [NSString stringWithCString:action->GetIconUrl().c_str() encoding:NSUTF8StringEncoding]; - UIButton *button = [UIButton rootView:rootView baseActionElement:acoElem title:title iconUrl:iconUrl andHostConfig:acoConfig]; + UIButton *button = [UIButton rootView:rootView baseActionElement:acoElem title:title andHostConfig:acoConfig]; ACRShowCardTarget *target = [[ACRShowCardTarget alloc] initWithActionElement:action config:acoConfig diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRActionSubmitRenderer.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRActionSubmitRenderer.mm index 66c6c7cbbe..4c184def86 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRActionSubmitRenderer.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRActionSubmitRenderer.mm @@ -31,8 +31,8 @@ - (UIButton* )renderButton:(ACRView *)view std::shared_ptr action = std::dynamic_pointer_cast(elem); NSString *title = [NSString stringWithCString:action->GetTitle().c_str() encoding:NSUTF8StringEncoding]; - NSString *iconUrl = [NSString stringWithCString:action->GetIconUrl().c_str() encoding:NSUTF8StringEncoding]; - UIButton *button = [UIButton rootView:view baseActionElement:acoElem title:title iconUrl:iconUrl andHostConfig:acoConfig]; + + UIButton *button = [UIButton rootView:view baseActionElement:acoElem title:title andHostConfig:acoConfig]; ACRAggregateTarget *target = [[ACRAggregateTarget alloc] initWithActionElement:acoElem rootView:(ACRView*)view]; diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRAggregateTarget.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRAggregateTarget.mm index 2f2d971205..1851fa335f 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRAggregateTarget.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRAggregateTarget.mm @@ -31,7 +31,6 @@ - (instancetype)initWithActionElement:(ACOBaseActionElement *)actionElement root - (IBAction)send:(UIButton *)sender { - [sender setSelected:YES]; [_view.acrActionDelegate didFetchUserResponses:[_view card] action:_actionElement]; } diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRButton.h b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRButton.h index 23d3cde2c3..1199baddeb 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRButton.h +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRButton.h @@ -12,6 +12,5 @@ + (UIButton *)rootView:(ACRView *)rootView baseActionElement:(ACOBaseActionElement *)acoAction title:(NSString *)title - iconUrl:(NSString *)iconUrl andHostConfig:(ACOHostConfig *)config; @end diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRButton.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRButton.mm index 47c131e49d..82e1e8557d 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRButton.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRButton.mm @@ -74,7 +74,6 @@ + (void)setImageView:(UIImage*)image inButton:(UIButton*)button withConfig:(ACOH + (UIButton* )rootView:(ACRView *)rootView baseActionElement:(ACOBaseActionElement *)acoAction title:(NSString *)title - iconUrl:(NSString *)iconUrl andHostConfig:(ACOHostConfig *)config; { NSBundle* bundle = [NSBundle bundleWithIdentifier:@"MSFT.AdaptiveCards"]; @@ -84,7 +83,7 @@ + (UIButton* )rootView:(ACRView *)rootView std::shared_ptr action = [acoAction element]; NSDictionary *imageViewMap = [rootView getImageMap]; - NSString *key = [ACRView generateKeyForActionElement:action]; + NSString *key = [NSString stringWithCString:action->GetIconUrl().c_str() encoding:[NSString defaultCStringEncoding]]; UIImage *img = imageViewMap[key]; if(img){ diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRCellForCompactMode.xib b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRCellForCompactMode.xib index 1d216b20e4..ceea4824ce 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRCellForCompactMode.xib +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRCellForCompactMode.xib @@ -1,5 +1,5 @@ - + @@ -18,7 +18,7 @@ - diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRChoiceSetViewDataSource.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRChoiceSetViewDataSource.mm index 3bb487006a..0c4e638ca3 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRChoiceSetViewDataSource.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRChoiceSetViewDataSource.mm @@ -10,11 +10,13 @@ using namespace AdaptiveCards; +const CGFloat padding = 16.0f; +const CGFloat accessoryViewWidth = 50.0f; + @implementation ACRChoiceSetViewDataSource { std::shared_ptr _choiceSetDataSource; NSMutableDictionary *_userSelections; - NSUInteger _numberOfUserSelections; NSIndexPath *_lastSelectedIndexPath; NSMutableSet *_defaultValuesSet; NSArray *_defaultValuesArray; @@ -67,6 +69,9 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N NSString *title = [NSString stringWithCString:_choiceSetDataSource->GetChoices()[indexPath.row]->GetTitle().c_str() encoding:NSUTF8StringEncoding]; cell.textLabel.text = title; + cell.textLabel.numberOfLines = 0; + cell.textLabel.lineBreakMode = NSLineBreakByWordWrapping; + cell.textLabel.adjustsFontSizeToFitWidth = NO; NSString *keyForDefaultValue = [NSString stringWithCString:_choiceSetDataSource->GetChoices()[indexPath.row]->GetValue().c_str() encoding:NSUTF8StringEncoding]; @@ -76,7 +81,6 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N _userSelections[[NSNumber numberWithInteger:indexPath.row]] = [NSNumber numberWithBool:YES]; [_defaultValuesSet removeObject:keyForDefaultValue]; cell.accessoryType = UITableViewCellAccessoryCheckmark; - _numberOfUserSelections++; } else { cell.accessoryType = UITableViewCellAccessoryNone; } @@ -95,7 +99,6 @@ - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)ce [self tableView:tableView didSelectRowAtIndexPath:indexPath]; [cell setSelected:YES animated:NO]; cell.accessoryType = UITableViewCellAccessoryCheckmark; - _numberOfUserSelections--; _lastSelectedIndexPath = indexPath; } } @@ -112,41 +115,42 @@ - (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInte - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { - if([tableView cellForRowAtIndexPath:indexPath].accessoryType == UITableViewCellAccessoryCheckmark) - { - if(_numberOfUserSelections > 1){ - [tableView cellForRowAtIndexPath:indexPath].accessoryType = UITableViewCellAccessoryNone; - _userSelections[[NSNumber numberWithInteger:indexPath.row]] = [NSNumber numberWithBool:NO]; - _numberOfUserSelections--; + if (!_isMultiChoicesAllowed) { + [tableView cellForRowAtIndexPath:indexPath].accessoryType = UITableViewCellAccessoryCheckmark; + _userSelections[[NSNumber numberWithInteger:indexPath.row]] = [NSNumber numberWithBool:YES]; + if (_lastSelectedIndexPath && _lastSelectedIndexPath != indexPath) { + [self tableView:tableView didDeselectRowAtIndexPath:_lastSelectedIndexPath]; + _lastSelectedIndexPath = nil; } - } - else - { + } else if ([tableView cellForRowAtIndexPath:indexPath].accessoryType == UITableViewCellAccessoryCheckmark) { + [tableView cellForRowAtIndexPath:indexPath].accessoryType = UITableViewCellAccessoryNone; + _userSelections[[NSNumber numberWithInteger:indexPath.row]] = [NSNumber numberWithBool:NO]; + } else { [tableView cellForRowAtIndexPath:indexPath].accessoryType = UITableViewCellAccessoryCheckmark; _userSelections[[NSNumber numberWithInteger:indexPath.row]] = [NSNumber numberWithBool:YES]; - _numberOfUserSelections++; - } - - // didDeselectRowAtIndexPath doesn't get called for the cells that was already selected before the tableView came to view - // if multi choice is not allowed, then uncheck pre-selection - if(_isMultiChoicesAllowed == NO && _lastSelectedIndexPath.row != indexPath.row) - { - [tableView cellForRowAtIndexPath:_lastSelectedIndexPath].accessoryType = UITableViewCellAccessoryNone; - _userSelections[[NSNumber numberWithInteger:_lastSelectedIndexPath.row]] = [NSNumber numberWithBool:NO]; } } - (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(nonnull NSIndexPath *)indexPath { // uncheck selection if multi choice is not allowed - if(_isMultiChoicesAllowed == NO) - { + if (!_isMultiChoicesAllowed) { [tableView cellForRowAtIndexPath:indexPath].accessoryType = UITableViewCellAccessoryNone; _userSelections[[NSNumber numberWithInteger:indexPath.row]] = [NSNumber numberWithBool:NO]; - _numberOfUserSelections--; } } +- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath +{ + UITableViewCell *cell = [tableView.dataSource tableView:tableView cellForRowAtIndexPath:indexPath]; + CGSize labelStringSize = + [cell.textLabel.text boundingRectWithSize:CGSizeMake(cell.contentView.frame.size.width - accessoryViewWidth, CGFLOAT_MAX) + options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading + attributes:@{NSFontAttributeName:cell.textLabel.font} + context:nil].size; + return labelStringSize.height + padding; +} + - (BOOL)validate:(NSError **)error { // no need to validate diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRChoiceSetViewDataSourceCompactStyle.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRChoiceSetViewDataSourceCompactStyle.mm index 85606bf2ee..f42c74480a 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRChoiceSetViewDataSourceCompactStyle.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRChoiceSetViewDataSourceCompactStyle.mm @@ -12,6 +12,8 @@ #import "ACRActionDelegate.h" using namespace AdaptiveCards; +const CGFloat padding = 16.0f; +const CGFloat accessoryViewWidth = 8.0f; @implementation ACRChoiceSetViewDataSourceCompactStyle { @@ -58,8 +60,14 @@ - (instancetype)initWithInputChoiceSet:(std::shared_ptrGetChoices().empty()){ + firstChoice = [NSString stringWithCString:(_choiceSetInput->GetChoices()[0])->GetTitle().c_str() encoding:NSUTF8StringEncoding]; + _defaultString = firstChoice; + } else { + _defaultString = @""; + } + } } return self; } @@ -85,12 +93,16 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier]; if(!cell) { - NSBundle *bundle = [NSBundle bundleWithIdentifier:@"MSFT.AdaptiveCards"]; - [tableView registerNib:[UINib nibWithNibName:@"ACRCellForCompactMode" bundle:bundle] forCellReuseIdentifier:identifier]; - cell = [tableView dequeueReusableCellWithIdentifier:identifier]; + cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault + reuseIdentifier:identifier]; } - cell.textLabel.text = ([_defaultString length])? _defaultString : @"Make Choice"; + cell.textLabel.text = ([_defaultString length])? _defaultString : @""; + cell.textLabel.numberOfLines = 0; + cell.textLabel.adjustsFontSizeToFitWidth = NO; + cell.textLabel.lineBreakMode = NSLineBreakByWordWrapping; + cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; + [cell setSelectionStyle:UITableViewCellSelectionStyleNone]; return cell; } @@ -104,6 +116,16 @@ - (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInte return nil; } +- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath +{ + UITableViewCell *cell = [tableView.dataSource tableView:tableView cellForRowAtIndexPath:indexPath]; + CGSize labelStringSize = [cell.textLabel.text boundingRectWithSize:CGSizeMake(cell.contentView.frame.size.width - accessoryViewWidth, CGFLOAT_MAX) + options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading + attributes:@{NSFontAttributeName:cell.textLabel.font} + context:nil].size; + return labelStringSize.height + padding; +} + // when cell is selected, create a tableView with a navigator control bar. - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { @@ -133,8 +155,7 @@ - (void)handleUIBarButtonSystemItemDoneEvent { [_tableView cellForRowAtIndexPath:_indexPath].selected = NO; NSString *choice = [(ACRChoiceSetViewDataSource *)_dataSource getTitlesOfChoices]; - [_tableView cellForRowAtIndexPath:_indexPath].textLabel.text = (choice)? choice : @"Make Choice"; - + [_tableView cellForRowAtIndexPath:_indexPath].textLabel.text = (choice)? choice : @""; _indexPath = nil; _tableView = nil; } diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnRenderer.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnRenderer.mm index 53be187b30..1d9bca9725 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnRenderer.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnRenderer.mm @@ -39,8 +39,9 @@ - (UIView *)render:(UIView *)viewGroup ACRColumnView* column = [[ACRColumnView alloc] initWithStyle:(ACRContainerStyle)columnElem->GetStyle() parentStyle:[viewGroup style] hostConfig:acoConfig]; + column.pixelWidth = columnElem->GetPixelWidth(); if(columnElem->GetWidth() == "stretch" || columnElem->GetWidth() == "") { - column.columnWidth = @"stretch"; + column.columnWidth = @"stretch"; } else if(columnElem->GetWidth() == "auto"){ column.columnWidth = @"auto"; } @@ -52,7 +53,7 @@ - (UIView *)render:(UIView *)viewGroup andHostConfig:acoConfig]; [viewGroup addArrangedSubview:column]; - + [column setClipsToBounds:TRUE]; std::shared_ptr selectAction = columnElem->GetSelectAction(); diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnSetRenderer.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnSetRenderer.mm index 0912c12119..ecda216668 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnSetRenderer.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnSetRenderer.mm @@ -63,7 +63,15 @@ - (UIView* )render:(UIView *)viewGroup curView = (ACRColumnView *)[columRenderer render:columnSetView rootView:rootView inputs:inputs baseCardElement:acoColumn hostConfig:acoConfig]; // when stretch, views with stretch properties should have equal width - if([curView.columnWidth isEqualToString:@"stretch"]){ + if(curView.pixelWidth){ + [NSLayoutConstraint constraintWithItem:curView + attribute:NSLayoutAttributeWidth + relatedBy:NSLayoutRelationEqual + toItem:nil + attribute:NSLayoutAttributeNotAnAttribute + multiplier:1 + constant:curView.pixelWidth].active = YES; + } else if([curView.columnWidth isEqualToString:@"stretch"]){ if(stretchView){ [NSLayoutConstraint constraintWithItem:curView attribute:NSLayoutAttributeWidth diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnView.h b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnView.h index af720df7d3..ae3118f868 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnView.h +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnView.h @@ -9,6 +9,7 @@ @interface ACRColumnView:ACRContentStackView @property NSString *columnWidth; +@property CGFloat pixelWidth; @property BOOL hasStretchableView; @end diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRContainerRenderer.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRContainerRenderer.mm index f0b8122a5e..ff478357ee 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRContainerRenderer.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRContainerRenderer.mm @@ -38,6 +38,7 @@ - (UIView *)render:(UIView *)viewGroup ACRColumnView *container = [[ACRColumnView alloc] initWithStyle:(ACRContainerStyle)containerElem->GetStyle() parentStyle:[viewGroup style] hostConfig:acoConfig]; + container.frame = viewGroup.frame; [ACRRenderer render:container rootView:rootView inputs:inputs diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRContentStackView.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRContentStackView.mm index ef7ab0903a..f134bffe42 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRContentStackView.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRContentStackView.mm @@ -40,7 +40,7 @@ - (instancetype)initWithStyle:(ACRContainerStyle)style } - (instancetype)initWithFrame:(CGRect)frame attributes:(nullable NSDictionary *)attributes{ - self = [super initWithFrame:CGRectMake(0,0,frame.size.width,0)]; + self = [super initWithFrame:CGRectMake(0,0,frame.size.width, frame.size.height)]; if(self) { _stackView = [[UIStackView alloc] initWithFrame:frame]; [self config:attributes]; @@ -49,7 +49,7 @@ - (instancetype)initWithFrame:(CGRect)frame attributes:(nullable NSDictionary *)attributes if(attributes){ NSNumber *distribAttrib = attributes[@"distribution"]; - if(distribAttrib){ + if([distribAttrib boolValue]){ self.stackView.distribution = (UIStackViewDistribution)[distribAttrib integerValue]; } NSNumber *alignAttrib = attributes[@"alignment"]; - if(alignAttrib){ + if([alignAttrib boolValue]){ self.stackView.alignment = (UIStackViewAlignment)[alignAttrib integerValue]; } NSNumber *spacingAttrib = attributes[@"spacing"]; - if(spacingAttrib){ + if([spacingAttrib boolValue]){ self.stackView.spacing = [spacingAttrib floatValue]; } } diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRDateTextField.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRDateTextField.mm index 82396e199b..c8ef052248 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRDateTextField.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRDateTextField.mm @@ -22,7 +22,7 @@ - (instancetype)initWithTimeDateInput:(std::shared_ptr const & dateStyle:(NSDateFormatterStyle)dateStyle { NSBundle *bundle = [NSBundle bundleWithIdentifier:@"MSFT.AdaptiveCards"]; - self = [bundle loadNibNamed:@"ACRDateTextField" owner:self options:nil][0]; + self = [super init]; if(self) { NSString *valueStr = nil; @@ -84,6 +84,7 @@ - (instancetype)initWithTimeDateInput:(std::shared_ptr const & self.text = valueStr; self.allowsEditingTextAttributes = NO; self.borderStyle = UITextBorderStyleRoundedRect; + self.backgroundColor = UIColor.groupTableViewBackgroundColor; if(date) { diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRFactSetRenderer.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRFactSetRenderer.mm index 37bc2b78cd..6994a8fa09 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRFactSetRenderer.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRFactSetRenderer.mm @@ -131,9 +131,11 @@ - (UIView *)render:(UIView *)viewGroup [titleLab setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal]; [titleLab setContentHuggingPriority:UILayoutPriorityDefaultHigh forAxis:UILayoutConstraintAxisHorizontal]; titleLab.isTitle = YES; - NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:titleLab attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationLessThanOrEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:config->factSet.title.maxWidth]; - constraint.active = YES; - constraint.priority = UILayoutPriorityDefaultHigh; + if (config->factSet.title.maxWidth) { + NSLayoutConstraint *constraintForTitleLab = [NSLayoutConstraint constraintWithItem:titleLab attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationLessThanOrEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:config->factSet.title.maxWidth]; + constraintForTitleLab.active = YES; + constraintForTitleLab.priority = UILayoutPriorityRequired; + } NSString *value = [NSString stringWithCString:fact->GetValue().c_str() encoding:NSUTF8StringEncoding]; UILabel *valueLab = [ACRFactSetRenderer buildLabel:value hostConfig:acoConfig diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRImageRenderer.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRImageRenderer.mm index 5c434d1da1..418dabc23c 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRImageRenderer.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRImageRenderer.mm @@ -46,8 +46,7 @@ - (UIView *)render:(UIView *)viewGroup NSMutableDictionary *imageViewMap = [rootView getImageMap]; // Syncronize access to imageViewMap - NSNumber *number = [NSNumber numberWithUnsignedLongLong:(unsigned long long)imgElem.get()]; - NSString *key = [number stringValue]; + NSString *key = [NSString stringWithCString:imgElem->GetUrl().c_str() encoding:[NSString defaultCStringEncoding]]; UIImage *img = imageViewMap[key]; CGFloat heightToWidthRatio = 0.0f, widthToHeightRatio = 0.0f; diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRImageSetUICollectionView.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRImageSetUICollectionView.mm index 7fafbf2b52..7eed783c75 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRImageSetUICollectionView.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRImageSetUICollectionView.mm @@ -92,7 +92,7 @@ - (CGSize)intrinsicContentSize return [self collectionViewContentSize]; } -- (CGSize) collectionViewContentSize +- (CGSize)collectionViewContentSize { size_t cellCounts = _imgSet->GetImages().size(); CGSize imageSize = ((UICollectionViewFlowLayout *)self.collectionViewLayout).itemSize; diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRInputChoiceSetRenderer.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRInputChoiceSetRenderer.mm index e234ab7af5..fa21634954 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRInputChoiceSetRenderer.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRInputChoiceSetRenderer.mm @@ -44,35 +44,34 @@ - (UIView *)render:(UIView *)viewGroup [choiceSetView setSeparatorStyle:UITableViewCellSeparatorStyleNone]; } else { dataSource = [[ACRChoiceSetViewDataSource alloc] initWithInputChoiceSet:choiceSet]; + [choiceSetView setSeparatorStyle:UITableViewCellSeparatorStyleSingleLineEtched]; } choiceSetView.delegate = dataSource; choiceSetView.dataSource = dataSource; - [inputs addObject:dataSource]; - - UIView *inputView = (UIView *)choiceSetView; - + [inputs addObject:dataSource]; + if(elem->GetHeight() == HeightType::Stretch){ ACRColumnView *textInputContainer = [[ACRColumnView alloc] init]; - [textInputContainer addArrangedSubview: inputView]; + [textInputContainer addArrangedSubview:choiceSetView]; // Add a blank view so the input field doesnt grow as large as it can and so it keeps the same behavior as Android and UWP UIView *blankTrailingSpace = [[UIView alloc] init]; [textInputContainer addArrangedSubview:blankTrailingSpace]; [textInputContainer adjustHuggingForLastElement]; - [viewGroup addArrangedSubview: textInputContainer]; + [viewGroup addArrangedSubview:textInputContainer]; } else { - [viewGroup addArrangedSubview:inputView]; + [viewGroup addArrangedSubview:choiceSetView]; } - [NSLayoutConstraint constraintWithItem:inputView + [NSLayoutConstraint constraintWithItem:choiceSetView attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationLessThanOrEqual toItem:viewGroup attribute:NSLayoutAttributeLeading multiplier:1.0 constant:0].active = YES; - [NSLayoutConstraint constraintWithItem:inputView + [NSLayoutConstraint constraintWithItem:choiceSetView attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationLessThanOrEqual toItem:viewGroup @@ -80,7 +79,7 @@ - (UIView *)render:(UIView *)viewGroup multiplier:1.0 constant:0].active = YES; - return inputView; + return choiceSetView; } @end diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRInputNumberRenderer.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRInputNumberRenderer.mm index 4c43114281..5684877315 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRInputNumberRenderer.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRInputNumberRenderer.mm @@ -40,7 +40,7 @@ - (UIView *)render:(UIView *)viewGroup numInput.placeholder = [NSString stringWithCString:numInputBlck->GetPlaceholder().c_str() encoding:NSUTF8StringEncoding]; numInput.text = [NSString stringWithFormat: @"%d", numInputBlck->GetValue()]; numInput.allowsEditingTextAttributes = YES; - numInput.borderStyle = UITextBorderStyleLine; + numInput.borderStyle = UITextBorderStyleRoundedRect; numInput.keyboardType = UIKeyboardTypeNumberPad; numInput.min = numInputBlck->GetMin(); numInput.max = numInputBlck->GetMax(); diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRInputTableView.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRInputTableView.mm index d8f965b4ae..63545ef7e5 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRInputTableView.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRInputTableView.mm @@ -38,7 +38,13 @@ - (instancetype)initWithSuperview:(UIView *)view - (CGSize)intrinsicContentSize { - return self.contentSize; + NSInteger numberOfRows = [self.dataSource tableView:self numberOfRowsInSection:0]; + CGFloat height = 0.0f; + for (int i = 0; i < numberOfRows; i++){ + NSIndexPath *indexPath = [NSIndexPath indexPathForRow:i inSection:0]; + height += [self.delegate tableView:self heightForRowAtIndexPath:indexPath]; + } + return CGSizeMake(self.frame.size.width, height); } @end diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRInputToggleRenderer.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRInputToggleRenderer.mm index 28bdaeb3fd..e75465a759 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRInputToggleRenderer.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRInputToggleRenderer.mm @@ -7,13 +7,13 @@ #import "ACRInputToggleRenderer.h" #import "ACRInputTableView.h" -#import "ACRToggleInputDataSource.h" #import "ACRContentHoldingUIView.h" #import "ACRSeparator.h" #import "ToggleInput.h" #import "ACRColumnSetView.h" #import "ACOHostConfigPrivate.h" #import "ACOBaseCardElementPrivate.h" +#import "ACRToggleInputDataSource.h" @implementation ACRInputToggleRenderer @@ -37,41 +37,42 @@ - (UIView *)render:(UIView *)viewGroup std::shared_ptr config = [acoConfig getHostConfig]; std::shared_ptr elem = [acoElem element]; std::shared_ptr toggleBlck = std::dynamic_pointer_cast(elem); - NSBundle *bundle = [NSBundle bundleWithIdentifier:@"MSFT.AdaptiveCards"]; - ACRInputTableView *inputView = [bundle loadNibNamed:@"ACRInputTableView" owner:rootView options:nil][0]; + + ACRInputTableView *inputTableView = [[ACRInputTableView alloc] initWithSuperview:viewGroup]; + [inputTableView setSeparatorStyle:UITableViewCellSeparatorStyleNone]; ACRToggleInputDataSource *dataSource = [[ACRToggleInputDataSource alloc] initWithInputToggle:toggleBlck WithHostConfig:config]; + inputTableView.delegate = dataSource; + inputTableView.dataSource = dataSource; + [inputs addObject:dataSource]; - inputView.dataSource = dataSource; - inputView.delegate = (NSObject *)dataSource; if(elem->GetHeight() == HeightType::Stretch){ ACRColumnView *textInputContainer = [[ACRColumnView alloc] init]; - [textInputContainer addArrangedSubview: inputView]; + [textInputContainer addArrangedSubview:inputTableView]; // Add a blank view so the input field doesnt grow as large as it can and so it keeps the same behavior as Android and UWP UIView *blankTrailingSpace = [[UIView alloc] init]; [textInputContainer addArrangedSubview:blankTrailingSpace]; [textInputContainer adjustHuggingForLastElement]; - [viewGroup addArrangedSubview: textInputContainer]; + [viewGroup addArrangedSubview:textInputContainer]; } else { - [viewGroup addArrangedSubview:inputView]; + [viewGroup addArrangedSubview:inputTableView]; } - [inputView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"tabCellId"]; - [NSLayoutConstraint constraintWithItem:inputView + [NSLayoutConstraint constraintWithItem:inputTableView attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationLessThanOrEqual toItem:viewGroup attribute:NSLayoutAttributeLeading multiplier:1.0 constant:0].active = YES; - [NSLayoutConstraint constraintWithItem:inputView + [NSLayoutConstraint constraintWithItem:inputTableView attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationLessThanOrEqual toItem:viewGroup attribute:NSLayoutAttributeTrailing multiplier:1.0 constant:0].active = YES; - return inputView; + return inputTableView; } @end diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRRenderer.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRRenderer.mm index 8323b2b729..7428385c7b 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRRenderer.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRRenderer.mm @@ -67,10 +67,7 @@ + (UIView *)renderWithAdaptiveCards:(std::shared_ptr const &)adapt ACRColumnView *verticalView = containingView; if(![[ACRRegistration getInstance] isElementRendererOverriden:[ACRImageRenderer elemType]]){ - NSNumber *number = [NSNumber numberWithUnsignedLongLong:(unsigned long long)adaptiveCard.get()]; - NSString *key = [number stringValue]; - NSString *urlStr = [NSString stringWithCString:adaptiveCard->GetBackgroundImage().c_str() encoding:[NSString defaultCStringEncoding]]; - [rootView loadImage:urlStr key:key]; + [rootView loadImage:adaptiveCard->GetBackgroundImage()]; } if(!body.empty()) { ACRContainerStyle style = ([config getHostConfig]->adaptiveCard.allowCustomStyle)? (ACRContainerStyle)adaptiveCard->GetStyle() : ACRDefault; @@ -156,7 +153,7 @@ + (UIView *)renderWithAdaptiveCards:(std::shared_ptr const &)adapt contentWidth = maxWidth; } childview.frame = CGRectMake(0, 0, contentWidth, contentHeight); - containingView.frame = CGRectMake(0, 0, superview.frame.size.width, contentHeight); + containingView.frame = CGRectMake(0, 0, superview.frame.size.width, contentHeight + spacing); containingView.translatesAutoresizingMaskIntoConstraints = NO; [containingView addSubview:childview]; [NSLayoutConstraint constraintWithItem:containingView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:childview attribute:NSLayoutAttributeTop multiplier:1.0 constant:0].active = YES; diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRTextView.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRTextView.mm index 3e330bc214..8d49f475bb 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRTextView.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRTextView.mm @@ -25,6 +25,7 @@ - (instancetype)initWithFrame:(CGRect)frame element:(ACOBaseCardElement *)elemen self.text = _placeholderText; self.textColor = [UIColor lightGrayColor]; } + [self.layer setCornerRadius:5.0f]; } return self; } diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRToggleInputDataSource.h b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRToggleInputDataSource.h index 8a334ad836..626e31b257 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRToggleInputDataSource.h +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRToggleInputDataSource.h @@ -9,10 +9,10 @@ #import "ACRIBaseInputHandler.h" #import "ACRIBaseCardElementRenderer.h" #import "HostConfig.h" +#import "ACRColumnSetView.h" -@interface ACRToggleInputDataSource:NSObject +@interface ACRToggleInputDataSource:NSObject -@property BOOL isSelected; @property NSString *id; @property NSString *valueOn; @property NSString *valueOff; diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRToggleInputDataSource.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRToggleInputDataSource.mm index b7adda21d4..9048c44bb6 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRToggleInputDataSource.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRToggleInputDataSource.mm @@ -7,35 +7,42 @@ #import #import "ACRToggleInputDataSource.h" -#import "ToggleInput.h" #import "ACRIBaseCardElementRenderer.h" #import "HostConfig.h" +#import "ACRUILabel.h" +#import "ACRColumnSetView.h" using namespace AdaptiveCards; +const CGFloat padding = 16.0f; @implementation ACRToggleInputDataSource { - std::shared_ptr toggleInputDataSource; - std::shared_ptr config; + std::shared_ptr _toggleInputDataSource; + std::shared_ptr _config; + UISwitch *_toggleSwitch; + NSString *_title; } - (instancetype)initWithInputToggle:(std::shared_ptr const&)toggleInput WithHostConfig:(std::shared_ptr const&)hostConfig { self = [super init]; - if(self) - { - toggleInputDataSource = toggleInput; - config = hostConfig; - self.id = [[NSString alloc]initWithCString:toggleInputDataSource->GetId().c_str() + + if(self) { + _title = [NSString stringWithCString:toggleInput->GetTitle().c_str() + encoding:NSUTF8StringEncoding]; + _toggleSwitch = [[UISwitch alloc] init]; + _toggleInputDataSource = toggleInput; + _config = hostConfig; + self.id = [[NSString alloc]initWithCString:_toggleInputDataSource->GetId().c_str() encoding:NSUTF8StringEncoding]; - if(toggleInputDataSource->GetValue() == toggleInputDataSource->GetValueOn()) - { - self.isSelected = YES; + if(_toggleInputDataSource->GetValue() == _toggleInputDataSource->GetValueOn()) { + _toggleSwitch.on = YES; } - self.valueOn = [[NSString alloc]initWithCString:toggleInputDataSource->GetValueOn().c_str() + + self.valueOn = [[NSString alloc]initWithCString:_toggleInputDataSource->GetValueOn().c_str() encoding:NSUTF8StringEncoding]; - self.valueOff = [[NSString alloc]initWithCString:toggleInputDataSource->GetValueOff().c_str() + self.valueOff = [[NSString alloc]initWithCString:_toggleInputDataSource->GetValueOff().c_str() encoding:NSUTF8StringEncoding]; } return self; @@ -51,52 +58,30 @@ - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView return 1; } -- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section -{ - return nil; -} - -- (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section -{ - return nil; -} - - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { - static NSString *identifier = @"tabCellId"; + static NSString *identifier = @"cellForCompactMode"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier]; - if(!cell) - { + if(!cell) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier]; } - NSString *title = [NSString stringWithCString:toggleInputDataSource->GetTitle().c_str() - encoding:NSUTF8StringEncoding]; - if(self.isSelected) - { - cell.accessoryType = UITableViewCellAccessoryCheckmark; - } - - cell.textLabel.text = title; - + cell.textLabel.text = _title; + cell.textLabel.adjustsFontSizeToFitWidth = NO; + cell.textLabel.numberOfLines = 0; + cell.accessoryView = _toggleSwitch; + cell.selectionStyle = UITableViewCellSelectionStyleNone; return cell; } -- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath +- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { - [tableView cellForRowAtIndexPath:indexPath].selected = NO; - if([tableView cellForRowAtIndexPath:indexPath].accessoryType == - UITableViewCellAccessoryCheckmark) - { - [tableView cellForRowAtIndexPath:indexPath].accessoryType = UITableViewCellAccessoryNone; - self.isSelected = NO; - } - else - { - [tableView cellForRowAtIndexPath:indexPath].accessoryType = - UITableViewCellAccessoryCheckmark; - self.isSelected = YES; - } + return nil; +} + +- (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section +{ + return nil; } - (BOOL)validate:(NSError **)error @@ -107,7 +92,20 @@ - (BOOL)validate:(NSError **)error - (void)getInput:(NSMutableDictionary *)dictionary { - dictionary[self.id] = self.isSelected? self.valueOn : self.valueOff; + dictionary[self.id] = _toggleSwitch.on? self.valueOn : self.valueOff; +} + +- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath +{ + UITableViewCell *cell = [tableView.dataSource tableView:tableView cellForRowAtIndexPath:indexPath]; + CGFloat toggleHeight = [_toggleSwitch intrinsicContentSize].height; + CGSize labelStringSize = + [cell.textLabel.text boundingRectWithSize:CGSizeMake(cell.contentView.frame.size.width, CGFLOAT_MAX) + options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading + attributes:@{NSFontAttributeName:cell.textLabel.font} + context:nil].size; + CGFloat height = MAX(labelStringSize.height, toggleHeight); + return height + padding; } @end diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRView.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRView.mm index 0d233a6958..e399cabd63 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRView.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRView.mm @@ -27,6 +27,7 @@ #import "FactSet.h" using namespace AdaptiveCards; +typedef UIImage* (^ImageLoadBlock)(NSURL *url); @implementation ACRView { @@ -105,24 +106,18 @@ - (UIView *)render NSNumber *number = [NSNumber numberWithUnsignedLongLong:(unsigned long long)[_adaptiveCard card].get()]; NSString *key = [number stringValue]; if([key length]){ - ACRBaseCardElementRenderer *imageRenderer = [[ACRRegistration getInstance] getRenderer:[NSNumber numberWithInteger:ACRImage]]; UIView *imgView = nil; - if([[ACRRegistration getInstance] isElementRendererOverriden:[ACRImageRenderer elemType]]){ - std::shared_ptr imageElement = std::make_shared(); - imageElement->SetImageSize(ImageSize::Stretch); - imageElement->SetUrl([_adaptiveCard card]->GetBackgroundImage()); - ACOBaseCardElement *acoElem = [[ACOBaseCardElement alloc] init]; - [acoElem setElem:imageElement]; - imgView = [imageRenderer render:nil rootView:self inputs:nil baseCardElement:acoElem hostConfig:_hostConfig]; - } else { + if(![[ACRRegistration getInstance] isElementRendererOverriden:[ACRImageRenderer elemType]]){ UIImage *img = _imageViewMap[key]; imgView = [[ACRUIImageView alloc] initWithImage:img]; } - imgView.translatesAutoresizingMaskIntoConstraints = NO; - [newView addSubview:imgView]; - [newView sendSubviewToBack:imgView]; - [NSLayoutConstraint constraintWithItem:imgView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:newView attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0].active = YES; - [NSLayoutConstraint constraintWithItem:imgView attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:newView attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0].active = YES; + if(imgView) { + imgView.translatesAutoresizingMaskIntoConstraints = NO; + [newView addSubview:imgView]; + [newView sendSubviewToBack:imgView]; + [NSLayoutConstraint constraintWithItem:imgView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:newView attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0].active = YES; + [NSLayoutConstraint constraintWithItem:imgView attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:newView attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0].active = YES; + } } [self callDidLoadElementsIfNeeded]; return newView; @@ -205,10 +200,7 @@ - (void)addTasksToConcurrentQueue:(std::vector> { /// tag a base card element with unique key std::shared_ptrimgElem = std::static_pointer_cast(elem); - NSString *urlStr = [NSString stringWithCString:imgElem->GetUrl().c_str() - encoding:[NSString defaultCStringEncoding]]; - NSString *key = [ACRView generateKeyForElement:imgElem]; - [self loadImage:urlStr key:key]; + [self loadImage:imgElem->GetUrl()]; break; } case CardElementType::ImageSet: @@ -219,10 +211,7 @@ - (void)addTasksToConcurrentQueue:(std::vector> img->SetImageSize(imgSetElem->GetImageSize()); if([rendererRegistration isElementRendererOverriden:(ACRCardElementType) CardElementType::Image] == NO){ - NSString *urlStr = [NSString stringWithCString:img->GetUrl().c_str() - encoding:[NSString defaultCStringEncoding]]; - NSString *key = [ACRView generateKeyForElement:img]; - [self loadImage:urlStr key:key]; + [self loadImage:img->GetUrl()]; } } break; @@ -267,10 +256,8 @@ - (void)addTasksToConcurrentQueue:(std::vector> - (void)loadImagesForActionsAndCheckIfAllActionsHaveIconImages:(std::vector> const &)actions hostconfig:(ACOHostConfig *)hostConfig; { for(auto &action : actions){ - NSString *urlStr = [NSString stringWithCString:action->GetIconUrl().c_str() encoding:[NSString defaultCStringEncoding]]; - if([urlStr length]) { - NSString *key = [ACRView generateKeyForActionElement:action]; - [self loadImage:urlStr key:key]; + if(action->GetIconUrl().size()) { + [self loadImage:action->GetIconUrl()]; } else { hostConfig.allActionsHaveIcons = NO; } @@ -360,15 +347,31 @@ - (void)processTextConcurrently:(std::shared_ptr const &)textEl }); } -- (void)loadImage:(NSString *)url key:(NSString *)key +- (void)loadImage:(std::string const &)urlStr { + NSString *nSUrlStr = [NSString stringWithCString:urlStr.c_str() + encoding:[NSString defaultCStringEncoding]]; + NSURL *url = [NSURL URLWithString:nSUrlStr]; + NSObject *imageResourceResolver = [_hostConfig getResourceResolverForScheme:[url scheme]]; + ImageLoadBlock imageloadblock = nil; + if(!imageResourceResolver || ![imageResourceResolver respondsToSelector:@selector(resolveImageResource:)]) { + imageloadblock = ^(NSURL *url){ + // download image + UIImage *img = [UIImage imageWithData:[NSData dataWithContentsOfURL:url]]; + return img; + }; + } + dispatch_group_async(_async_tasks_group, _global_queue, ^{ - NSURL *nsurl = [NSURL URLWithString:url]; - // download image - UIImage *img = [UIImage imageWithData:[NSData dataWithContentsOfURL:nsurl]]; + UIImage *img = nil; + if(imageloadblock) { + img = imageloadblock(url); + } else if(imageResourceResolver){ + img = [imageResourceResolver resolveImageResource:url]; + } - dispatch_sync(self->_serial_queue, ^{self->_imageViewMap[key] = img;}); + dispatch_sync(self->_serial_queue, ^{self->_imageViewMap[nSUrlStr] = img;}); } ); } @@ -382,24 +385,11 @@ - (void)tagBaseCardElement:(std::shared_ptr const &)elem ++_serialNumber; } -+ (NSString *)generateKeyForElement:(std::shared_ptr const &)elem -{ - NSNumber *number = [NSNumber numberWithUnsignedLongLong:(unsigned long long)elem.get()]; - NSString *key = [number stringValue]; - return key; -} - -+ (NSString *)generateKeyForActionElement:(std::shared_ptr const &)elem -{ - NSNumber *number = [NSNumber numberWithUnsignedLongLong:(unsigned long long)elem.get()]; - NSString *key = [number stringValue]; - return key; -} - - (NSMutableDictionary *)getImageMap { return _imageViewMap; } + - (dispatch_queue_t)getSerialQueue { return _serial_queue; diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRViewPrivate.h b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRViewPrivate.h index b7c36d0c1b..2a989171f7 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRViewPrivate.h +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRViewPrivate.h @@ -11,15 +11,12 @@ @interface ACRView() -- (void)loadImage:(NSString *)url key:(NSString *)key; // Walk through adaptive cards elements and if images are found, download and process images concurrently and on different thread // from main thread, so images process won't block UI thread. - (void)addTasksToConcurrentQueue:(std::vector> const &) body; // async method - (void)loadImagesForActionsAndCheckIfAllActionsHaveIconImages:(std::vector> const &)actions hostconfig:(ACOHostConfig *)hostConfig; -+ (NSString *)generateKeyForElement:(std::shared_ptr const &)elem; - -+ (NSString *)generateKeyForActionElement:(std::shared_ptr const &)elem; +- (void)loadImage:(std::string const &)urlStr; @end diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/CMakeLists.txt b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/CMakeLists.txt index 63b8843943..fe20f8ea62 100755 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/CMakeLists.txt +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required (VERSION 3.5) -set(CMAKE_OSX_SYSROOT iphonesimulator11.3) +set(CMAKE_OSX_SYSROOT iphonesimulator11.4) add_compile_options(-std=c++14 -Wall) project (AdaptiveCardsLib) include_directories(../../../../shared/cpp/ObjectModel) diff --git a/source/shared/cpp/ObjectModel/DateTimePreparsedToken.cpp b/source/shared/cpp/ObjectModel/DateTimePreparsedToken.cpp index e501cae4af..b5d2cfc93b 100644 --- a/source/shared/cpp/ObjectModel/DateTimePreparsedToken.cpp +++ b/source/shared/cpp/ObjectModel/DateTimePreparsedToken.cpp @@ -3,13 +3,13 @@ using namespace AdaptiveSharedNamespace; -DateTimePreparsedToken::DateTimePreparsedToken() : m_text(""), m_format(DateTimePreparsedTokenFormat::RegularString), - m_date{} +DateTimePreparsedToken::DateTimePreparsedToken() : m_text(""), m_date{}, + m_format(DateTimePreparsedTokenFormat::RegularString) { } DateTimePreparsedToken::DateTimePreparsedToken(std::string const &text, DateTimePreparsedTokenFormat format) : - m_text(text), m_format(format), m_date{} + m_text(text), m_date{}, m_format(format) { } diff --git a/source/shared/cpp/ObjectModel/MediaSource.h b/source/shared/cpp/ObjectModel/MediaSource.h index 5812bb3048..2d5a69d246 100644 --- a/source/shared/cpp/ObjectModel/MediaSource.h +++ b/source/shared/cpp/ObjectModel/MediaSource.h @@ -4,7 +4,7 @@ #include "BaseCardElement.h" #include "Enums.h" #include "ElementParserRegistration.h" -#include "util.h" +#include "Util.h" namespace AdaptiveSharedNamespace { class MediaSource diff --git a/source/shared/cpp/ObjectModel/SharedAdaptiveCard.cpp b/source/shared/cpp/ObjectModel/SharedAdaptiveCard.cpp index 3e3c1a37e4..182f986f61 100644 --- a/source/shared/cpp/ObjectModel/SharedAdaptiveCard.cpp +++ b/source/shared/cpp/ObjectModel/SharedAdaptiveCard.cpp @@ -8,8 +8,8 @@ using namespace AdaptiveSharedNamespace; -AdaptiveCard::AdaptiveCard(): m_style(ContainerStyle::None), m_height(HeightType::Auto), - m_verticalContentAlignment(VerticalContentAlignment::Top) +AdaptiveCard::AdaptiveCard(): m_style(ContainerStyle::None), + m_verticalContentAlignment(VerticalContentAlignment::Top), m_height(HeightType::Auto) { } @@ -92,25 +92,35 @@ std::shared_ptr AdaptiveCard::Deserialize( { ParseUtil::ThrowIfNotJsonObject(json); + bool enforceVersion = (rendererVersion != std::numeric_limits::max()); + // Verify this is an adaptive card ParseUtil::ExpectTypeString(json, CardElementType::AdaptiveCard); std::vector> warnings; - std::string version = ParseUtil::GetString(json, AdaptiveCardSchemaKey::Version); + std::string version = ParseUtil::GetString(json, AdaptiveCardSchemaKey::Version, enforceVersion); std::string fallbackText = ParseUtil::GetString(json, AdaptiveCardSchemaKey::FallbackText); std::string language = ParseUtil::GetString(json, AdaptiveCardSchemaKey::Language); - if (rendererVersion != std::numeric_limits::max()) + if (enforceVersion) { double versionAsDouble; try { versionAsDouble = std::stod(version.c_str()); } + catch (const std::invalid_argument&) + { + throw AdaptiveCardParseException(ErrorStatusCode::InvalidPropertyValue, "Card version invalid: " + version); + } + catch (const std::out_of_range&) + { + throw AdaptiveCardParseException(ErrorStatusCode::InvalidPropertyValue, "Card version out of range: " + version); + } catch (...) { - throw AdaptiveCardParseException(ErrorStatusCode::InvalidPropertyValue, "Card version not valid"); + throw AdaptiveCardParseException(ErrorStatusCode::InvalidPropertyValue, "Unable to parse card version: " + version); } if (rendererVersion < versionAsDouble) diff --git a/source/uwp/Renderer/idl/AdaptiveCards.Rendering.Uwp.idl b/source/uwp/Renderer/idl/AdaptiveCards.Rendering.Uwp.idl index dafccf1a47..0e78151d49 100644 --- a/source/uwp/Renderer/idl/AdaptiveCards.Rendering.Uwp.idl +++ b/source/uwp/Renderer/idl/AdaptiveCards.Rendering.Uwp.idl @@ -2539,11 +2539,8 @@ AdaptiveNamespaceStart [eventadd] HRESULT Action([in] Windows.Foundation.TypedEventHandler* pHandler, [out][retval] EventRegistrationToken* pToken); [eventremove] HRESULT Action([in] EventRegistrationToken token); - [eventadd] HRESULT MediaPlay([in] Windows.Foundation.TypedEventHandler* pHandler, [out][retval] EventRegistrationToken* pToken); - [eventremove] HRESULT MediaPlay([in] EventRegistrationToken token); - - [eventadd] HRESULT MediaEnded([in] Windows.Foundation.TypedEventHandler* pHandler, [out][retval] EventRegistrationToken* pToken); - [eventremove] HRESULT MediaEnded([in] EventRegistrationToken token); + [eventadd] HRESULT MediaClicked([in] Windows.Foundation.TypedEventHandler* pHandler, [out][retval] EventRegistrationToken* pToken); + [eventremove] HRESULT MediaClicked([in] EventRegistrationToken token); }; [ @@ -2770,8 +2767,7 @@ AdaptiveNamespaceStart ] interface IAdaptiveMediaEventInvoker : IInspectable { - HRESULT SendMediaPlayEvent([in] AdaptiveMedia* mediaElement); - HRESULT SendMediaEndedEvent([in] AdaptiveMedia* mediaElement); + HRESULT SendMediaClickedEvent([in] AdaptiveMedia* mediaElement); }; [ diff --git a/source/uwp/Renderer/lib/AdaptiveMediaEventInvoker.cpp b/source/uwp/Renderer/lib/AdaptiveMediaEventInvoker.cpp index b44d28fb09..c03cb20c3a 100644 --- a/source/uwp/Renderer/lib/AdaptiveMediaEventInvoker.cpp +++ b/source/uwp/Renderer/lib/AdaptiveMediaEventInvoker.cpp @@ -23,15 +23,9 @@ AdaptiveNamespaceStart } CATCH_RETURN; _Use_decl_annotations_ - HRESULT AdaptiveMediaEventInvoker::SendMediaPlayEvent(IAdaptiveMedia* mediaElement) + HRESULT AdaptiveMediaEventInvoker::SendMediaClickedEvent(IAdaptiveMedia* mediaElement) { - return m_renderResult->SendMediaPlayEvent(mediaElement); - } - - _Use_decl_annotations_ - HRESULT AdaptiveMediaEventInvoker::SendMediaEndedEvent(IAdaptiveMedia* mediaElement) - { - return m_renderResult->SendMediaEndedEvent(mediaElement); + return m_renderResult->SendMediaClickedEvent(mediaElement); } AdaptiveNamespaceEnd diff --git a/source/uwp/Renderer/lib/AdaptiveMediaEventInvoker.h b/source/uwp/Renderer/lib/AdaptiveMediaEventInvoker.h index c946d5e667..8f0c140e74 100644 --- a/source/uwp/Renderer/lib/AdaptiveMediaEventInvoker.h +++ b/source/uwp/Renderer/lib/AdaptiveMediaEventInvoker.h @@ -16,8 +16,7 @@ AdaptiveNamespaceStart HRESULT RuntimeClassInitialize(AdaptiveNamespace::RenderedAdaptiveCard* renderResult) noexcept; - IFACEMETHODIMP SendMediaPlayEvent(_In_ ABI::AdaptiveNamespace::IAdaptiveMedia* mediaElement); - IFACEMETHODIMP SendMediaEndedEvent(_In_ ABI::AdaptiveNamespace::IAdaptiveMedia* mediaElement); + IFACEMETHODIMP SendMediaClickedEvent(_In_ ABI::AdaptiveNamespace::IAdaptiveMedia* mediaElement); private: Microsoft::WRL::ComPtr m_renderResult; diff --git a/source/uwp/Renderer/lib/MediaHelpers.cpp b/source/uwp/Renderer/lib/MediaHelpers.cpp index e0e23a1a93..a7103fa8cb 100644 --- a/source/uwp/Renderer/lib/MediaHelpers.cpp +++ b/source/uwp/Renderer/lib/MediaHelpers.cpp @@ -52,6 +52,10 @@ void GetMediaPosterAsImage( THROW_IF_FAILED(MakeAndInitialize(&adaptiveImage)); THROW_IF_FAILED(adaptiveImage->put_Url(posterString.Get())); + HString altText; + THROW_IF_FAILED(adaptiveMedia->get_AltText(altText.GetAddressOf())); + THROW_IF_FAILED(adaptiveImage->put_AltText(altText.Get())); + ComPtr elementRenderers; THROW_IF_FAILED(renderContext->get_ElementRenderers(&elementRenderers)); ComPtr imageRenderer; @@ -366,14 +370,13 @@ HRESULT HandleMediaClick( EventRegistrationToken mediaOpenedToken; THROW_IF_FAILED(mediaElement->add_MediaOpened(Callback([=](IInspectable* /*sender*/, IRoutedEventArgs* /*args*/) -> HRESULT { - RETURN_IF_FAILED(mediaInvoker->SendMediaPlayEvent(adaptiveMedia)); RETURN_IF_FAILED(mediaElement->Play()); return S_OK; }).Get(), &mediaOpenedToken)); } else { - RETURN_IF_FAILED(mediaInvoker->SendMediaPlayEvent(adaptiveMedia)); + RETURN_IF_FAILED(mediaInvoker->SendMediaClickedEvent(adaptiveMedia)); } return S_OK; diff --git a/source/uwp/Renderer/lib/RenderedAdaptiveCard.cpp b/source/uwp/Renderer/lib/RenderedAdaptiveCard.cpp index 964ed6b857..065035bd6a 100644 --- a/source/uwp/Renderer/lib/RenderedAdaptiveCard.cpp +++ b/source/uwp/Renderer/lib/RenderedAdaptiveCard.cpp @@ -42,8 +42,7 @@ AdaptiveNamespaceStart m_warnings = warnings; RETURN_IF_FAILED(MakeAndInitialize(&m_inputs)); m_actionEvents = std::make_shared(); - m_mediaPlayEvents = std::make_shared(); - m_mediaEndedEvents = std::make_shared(); + m_mediaClickedEvents = std::make_shared(); return S_OK; } @@ -80,31 +79,17 @@ AdaptiveNamespaceStart } _Use_decl_annotations_ - HRESULT RenderedAdaptiveCard::add_MediaPlay( + HRESULT RenderedAdaptiveCard::add_MediaClicked( ABI::Windows::Foundation::ITypedEventHandler* handler, EventRegistrationToken* token) { - return m_mediaPlayEvents->Add(handler, token); + return m_mediaClickedEvents->Add(handler, token); } _Use_decl_annotations_ - HRESULT RenderedAdaptiveCard::remove_MediaPlay(EventRegistrationToken token) + HRESULT RenderedAdaptiveCard::remove_MediaClicked(EventRegistrationToken token) { - return m_mediaPlayEvents->Remove(token); - } - - _Use_decl_annotations_ - HRESULT RenderedAdaptiveCard::add_MediaEnded( - ABI::Windows::Foundation::ITypedEventHandler* handler, - EventRegistrationToken* token) - { - return m_mediaEndedEvents->Add(handler, token); - } - - _Use_decl_annotations_ - HRESULT RenderedAdaptiveCard::remove_MediaEnded(EventRegistrationToken token) - { - return m_mediaEndedEvents->Remove(token); + return m_mediaClickedEvents->Remove(token); } _Use_decl_annotations_ @@ -130,20 +115,12 @@ AdaptiveNamespaceStart return m_actionEvents->InvokeAll(this, eventArgs.Get()); } - HRESULT RenderedAdaptiveCard::SendMediaPlayEvent(IAdaptiveMedia* mediaElement) - { - ComPtr eventArgs; - RETURN_IF_FAILED(MakeAndInitialize(&eventArgs, mediaElement)); - - return m_mediaPlayEvents->InvokeAll(this, eventArgs.Get()); - } - - HRESULT RenderedAdaptiveCard::SendMediaEndedEvent(IAdaptiveMedia* mediaElement) + HRESULT RenderedAdaptiveCard::SendMediaClickedEvent(IAdaptiveMedia* mediaElement) { ComPtr eventArgs; RETURN_IF_FAILED(MakeAndInitialize(&eventArgs, mediaElement)); - return m_mediaEndedEvents->InvokeAll(this, eventArgs.Get()); + return m_mediaClickedEvents->InvokeAll(this, eventArgs.Get()); } void RenderedAdaptiveCard::SetFrameworkElement(ABI::Windows::UI::Xaml::IFrameworkElement* value) diff --git a/source/uwp/Renderer/lib/RenderedAdaptiveCard.h b/source/uwp/Renderer/lib/RenderedAdaptiveCard.h index 790966795d..194ea66d86 100644 --- a/source/uwp/Renderer/lib/RenderedAdaptiveCard.h +++ b/source/uwp/Renderer/lib/RenderedAdaptiveCard.h @@ -32,19 +32,12 @@ AdaptiveNamespaceStart _Out_ EventRegistrationToken* token); IFACEMETHODIMP remove_Action(_In_ EventRegistrationToken token); - IFACEMETHODIMP add_MediaPlay( + IFACEMETHODIMP add_MediaClicked( _In_ ABI::Windows::Foundation::ITypedEventHandler< ABI::AdaptiveNamespace::RenderedAdaptiveCard*, ABI::AdaptiveNamespace::AdaptiveMediaEventArgs*>* handler, _Out_ EventRegistrationToken* token); - IFACEMETHODIMP remove_MediaPlay(_In_ EventRegistrationToken token); - - IFACEMETHODIMP add_MediaEnded( - _In_ ABI::Windows::Foundation::ITypedEventHandler< - ABI::AdaptiveNamespace::RenderedAdaptiveCard*, - ABI::AdaptiveNamespace::AdaptiveMediaEventArgs*>* handler, - _Out_ EventRegistrationToken* token); - IFACEMETHODIMP remove_MediaEnded(_In_ EventRegistrationToken token); + IFACEMETHODIMP remove_MediaClicked(_In_ EventRegistrationToken token); IFACEMETHODIMP get_Errors(_COM_Outptr_ ABI::Windows::Foundation::Collections::IVector** value); IFACEMETHODIMP get_Warnings(_COM_Outptr_ ABI::Windows::Foundation::Collections::IVector** value); @@ -53,8 +46,7 @@ AdaptiveNamespaceStart void SetFrameworkElement(ABI::Windows::UI::Xaml::IFrameworkElement* value); void SetOriginatingCard(ABI::AdaptiveNamespace::IAdaptiveCard* value); HRESULT SendActionEvent(ABI::AdaptiveNamespace::IAdaptiveActionElement* eventArgs); - HRESULT SendMediaPlayEvent(ABI::AdaptiveNamespace::IAdaptiveMedia* eventArgs); - HRESULT SendMediaEndedEvent(ABI::AdaptiveNamespace::IAdaptiveMedia* eventArgs); + HRESULT SendMediaClickedEvent(ABI::AdaptiveNamespace::IAdaptiveMedia* eventArgs); private: Microsoft::WRL::ComPtr m_originatingCard; @@ -63,8 +55,7 @@ AdaptiveNamespaceStart Microsoft::WRL::ComPtr> m_errors; Microsoft::WRL::ComPtr> m_warnings; std::shared_ptr m_actionEvents; - std::shared_ptr m_mediaPlayEvents; - std::shared_ptr m_mediaEndedEvents; + std::shared_ptr m_mediaClickedEvents; }; ActivatableClass(RenderedAdaptiveCard); diff --git a/source/uwp/Renderer/lib/XamlBuilder.cpp b/source/uwp/Renderer/lib/XamlBuilder.cpp index 4c2aa7a378..e91c11244a 100644 --- a/source/uwp/Renderer/lib/XamlBuilder.cpp +++ b/source/uwp/Renderer/lib/XamlBuilder.cpp @@ -40,6 +40,7 @@ using namespace ABI::Windows::UI::Xaml::Media; using namespace ABI::Windows::UI::Xaml::Media::Imaging; using namespace ABI::Windows::UI::Xaml::Shapes; using namespace ABI::Windows::UI::Xaml::Input; +using namespace ABI::Windows::UI::Xaml::Automation; using namespace ABI::Windows::Web::Http; using namespace ABI::Windows::Web::Http::Filters; @@ -507,7 +508,7 @@ AdaptiveNamespaceStart // the tracker to subscribe to the ImageLoaded/Failed events ComPtr bitmapImage = XamlHelpers::CreateXamlClass(HStringReference(RuntimeClass_Windows_UI_Xaml_Media_Imaging_BitmapImage)); - if ((m_enableXamlImageHandling) || (m_listeners.size() == 0)) + if (!m_enableXamlImageHandling && (m_listeners.size() != 0)) { m_imageLoadTracker.TrackBitmapImage(bitmapImage.Get()); } @@ -1780,6 +1781,17 @@ AdaptiveNamespaceStart ComPtr imageAsUIElement; THROW_IF_FAILED(frameworkElement.As(&imageAsUIElement)); + HString altText; + THROW_IF_FAILED(adaptiveImage->get_AltText(altText.GetAddressOf())); + + ComPtr imageAsDependencyObject; + THROW_IF_FAILED(imageAsUIElement.As(&imageAsDependencyObject)); + + ComPtr automationPropertiesStatics; + THROW_IF_FAILED(GetActivationFactory(HStringReference(RuntimeClass_Windows_UI_Xaml_Automation_AutomationProperties).Get(), &automationPropertiesStatics)); + + THROW_IF_FAILED(automationPropertiesStatics->SetName(imageAsDependencyObject.Get(), altText.Get())); + HandleSelectAction(adaptiveCardElement, selectAction.Get(), renderContext, imageAsUIElement.Get(), SupportsInteractivity(hostConfig.Get()), true, imageControl); } @@ -2867,15 +2879,6 @@ AdaptiveNamespaceStart // Make the media element collapsed until the user clicks THROW_IF_FAILED(mediaUIElement->put_Visibility(Visibility_Collapsed)); - // Add an event for media ended - EventRegistrationToken mediaEndedToken; - THROW_IF_FAILED(mediaElement->add_MediaEnded(Callback([mediaInvoker, adaptiveMedia](IInspectable* /*sender*/, IRoutedEventArgs* /*args*/) -> HRESULT - { - RETURN_IF_FAILED(mediaInvoker->SendMediaEndedEvent(adaptiveMedia.Get())); - - return S_OK; - }).Get(), &mediaEndedToken)); - XamlHelpers::AppendXamlElementToPanel(mediaElement.Get(), mediaPanel.Get()); } diff --git a/source/uwp/Visualizer/ViewModel/DocumentViewModel.cs b/source/uwp/Visualizer/ViewModel/DocumentViewModel.cs index 32913f45a2..d111111654 100644 --- a/source/uwp/Visualizer/ViewModel/DocumentViewModel.cs +++ b/source/uwp/Visualizer/ViewModel/DocumentViewModel.cs @@ -110,10 +110,10 @@ protected override async void LoadPayload(string payload) if (!MainPageViewModel.HostConfigEditor.HostConfig.Media.AllowInlinePlayback) { - renderResult.MediaPlay += async (sender, e) => + renderResult.MediaClicked += async (sender, e) => { var onPlayDialog = new ContentDialog(); - onPlayDialog.Content = "MediaPlayEvent:"; + onPlayDialog.Content = "MediaClickedEvent:"; foreach (var source in e.Media.Sources) { @@ -125,23 +125,6 @@ protected override async void LoadPayload(string payload) await onPlayDialog.ShowAsync(); }; } - else - { - renderResult.MediaEnded += async (sender, e) => - { - var mediaEndedDialog = new ContentDialog(); - mediaEndedDialog.Content = "Media Ended Event:"; - - foreach (var source in e.Media.Sources) - { - mediaEndedDialog.Content += "\n" + source.Url + " (" + source.MimeType + ")"; - } - - mediaEndedDialog.PrimaryButtonText = "Close"; - - await mediaEndedDialog.ShowAsync(); - }; - } } else {