in source/android/adaptivecards/src/main/java/io/adaptivecards/renderer/registration/CardRendererRegistration.java [284:453]
public void renderElementAndPerformFallback(
RenderedAdaptiveCard renderedCard,
Context context,
FragmentManager fragmentManager,
BaseCardElement cardElement,
ViewGroup viewGroup,
ICardActionHandler cardActionHandler,
HostConfig hostConfig,
RenderArgs renderArgs,
FeatureRegistration featureRegistration) throws AdaptiveFallbackException, Exception
{
IBaseCardElementRenderer renderer = m_typeToRendererMap.get(cardElement.GetElementTypeString());
boolean elementHasFallback = (cardElement.GetFallbackType() != FallbackType.None);
RenderArgs childRenderArgs = new RenderArgs(renderArgs);
childRenderArgs.setAncestorHasFallback(elementHasFallback || renderArgs.getAncestorHasFallback());
// To avoid tampering with this method, this two variables are introduced:
// - renderedElement contains the element that was finally rendered (after performing fallback)
// this allows us to check if it was an input and render the label and error message
BaseCardElement renderedElement = null;
View renderedElementView = null;
// - mockLayout is a layout to contain the rendered element, as android renderers have to add
// the drawn views into the container view, it makes it easier to do not draw something unwanted
// into the final rendered card
ViewGroup mockLayout = null;
if (Util.isOfType(cardElement, Column.class))
{
mockLayout = new FlexboxLayout(context);
}
else
{
mockLayout = new LinearLayout(context);
}
try
{
if (renderer == null)
{
throw new AdaptiveFallbackException(cardElement);
}
if ((featureRegistration != null) && (!cardElement.MeetsRequirements(featureRegistration)))
{
throw new AdaptiveFallbackException(cardElement, featureRegistration);
}
if (cardElement.GetElementType() == CardElementType.ActionSet)
{
renderArgs.setRootLevelActions(false);
}
renderedElementView = renderer.render(renderedCard, context, fragmentManager, mockLayout, cardElement, cardActionHandler, hostConfig, childRenderArgs);
renderedElement = cardElement;
}
catch (AdaptiveFallbackException e)
{
if (elementHasFallback)
{
if (cardElement.GetFallbackType() == FallbackType.Content)
{
BaseElement fallbackElement = cardElement.GetFallbackContent();
while (fallbackElement != null)
{
// Try to render the fallback element
try
{
BaseCardElement fallbackCardElement = Util.castToBaseCardElement(fallbackElement);
IBaseCardElementRenderer fallbackRenderer = m_typeToRendererMap.get(fallbackElement.GetElementTypeString());
if (fallbackRenderer == null)
{
throw new AdaptiveFallbackException(fallbackCardElement);
}
if ((featureRegistration != null) && (!fallbackElement.MeetsRequirements(featureRegistration)))
{
throw new AdaptiveFallbackException(fallbackCardElement, featureRegistration);
}
renderedCard.addWarning(new AdaptiveWarning(AdaptiveWarning.UNKNOWN_ELEMENT_TYPE,
"Performing fallback for '" + cardElement.GetElementTypeString() +
"' (fallback element type: '" + fallbackCardElement.GetElementTypeString() + "')"));
// before rendering, check if the element to render is an input, if it is, then create an stretchable input layout, and add the label
// pass that as the viewgroup and
renderedElementView = fallbackRenderer.render(renderedCard, context, fragmentManager, mockLayout, fallbackCardElement, cardActionHandler, hostConfig, childRenderArgs);
renderedElement = fallbackCardElement;
break;
}
catch (AdaptiveFallbackException e2)
{
// As the fallback element didn't exist, go back to trying
if (fallbackElement.GetFallbackType() == FallbackType.Content)
{
fallbackElement = fallbackElement.GetFallbackContent();
}
else
{
// The element has no fallback, just clear the element so the cycle ends
fallbackElement = null;
}
renderedElement = null;
}
}
}
else if (cardElement.GetFallbackType() == FallbackType.Drop)
{
renderedCard.addWarning(new AdaptiveWarning(AdaptiveWarning.UNKNOWN_ELEMENT_TYPE,
"Dropping element '" + cardElement.GetElementTypeString() + "' for fallback"));
renderedElement = null;
}
}
else if (renderArgs.getAncestorHasFallback())
{
// There's an ancestor with fallback so we throw to trigger it
throw e;
}
else
{
// The element doesn't have a fallback, so the element can't be rendered and it's dropped
renderedCard.addWarning(new AdaptiveWarning(AdaptiveWarning.UNKNOWN_ELEMENT_TYPE, "Unsupported card element type: " + cardElement.GetElementTypeString()));
renderedElement = null;
}
}
if (renderedElement != null && renderedElementView != null)
{
View taggedView = findElementWithTagContent(mockLayout);
TagContent tagContent = BaseCardElementRenderer.getTagContent(taggedView);
// Sets this view container as the element is being moved to the correct location
tagContent.SetViewContainer(viewGroup);
// Render the spacing so no other renderers have to do it
boolean isColumn = Util.isOfType(renderedElement, Column.class);
// Only columns render vertical spacing, so if it's not a column, then we need a horizontal spacing
HandleSpacing(context, viewGroup, renderedElement, hostConfig, tagContent, !isColumn);
// Check if the element is an input or must be stretched
BaseInputElement baseInputElement = Util.tryCastTo(renderedElement, BaseInputElement.class);
if (baseInputElement != null)
{
// put the element in a Stretchable input layout and
HandleLabelAndValidation(renderedCard, mockLayout, viewGroup, baseInputElement, context, hostConfig, renderArgs, tagContent);
}
else
{
// Column, container, image and imageSet handle their height on their own, so let's not add an extra view for them
if (renderedElement.GetHeight() == HeightType.Stretch && !isColumn && !Util.isOfType(renderedElement, Container.class)
&& !Util.isOfType(renderedElement, Image.class) && !Util.isOfType(renderedElement, ImageSet.class))
{
// put the element in a StretchableElementLayout
HandleStretchHeight(mockLayout, viewGroup, renderedElement, context, tagContent);
}
else
{
Util.MoveChildrenViews(mockLayout, viewGroup);
}
}
HandleVisibility(renderedElement, renderedElementView);
}
}