Content Widget

Content Widget is a feature in our Software Development Kit that allows you to embed an easily customizable view with various types of content in your application.

Two view layouts are available:

  • Horizontal Slider - a single row view that slides horizontally within the screen.
  • Grid View - can be displayed as full- or half-screen grid layout within your app.

Both views offer a number of configuration options that allow you to style the view consistently in the app.

Additionally, the Content Widget automatically tracks events such as views or clicks within the view.

Note: Currently, the widget can only be used for displaying AI recommendations. Stay tuned for future development!

Prerequisites


  • Obtain a Client token from User Authentication.
  • Create an AI Recommendation Campaign as described here.
  • Create a Document as described here.

Such a document should contain the following content:

{
    "name": "Similar Products",
    "recommendations": "{% recommendations_json campaignId=COhsCCOdu8Cg %} {% endrecommendations_json %}"
}

Note: Take note of the Document’s slug for later use.
Tip: It’s a good practice to name slugs based on area of the app that you want to place the content in, for example product-details, menu.

Basic Implementation


Configure the ContentWidgetOptions and ContentWidgetAppearance settings first.

Class Description
ContentWidgetOptions Contains options for business logic, such as the slug, product identifier, and so on. Read more.
ContentWidgetAppearance Contains the UI configuration. Read more.

The example below is the most basic implementation.

String productId = "10214";
String slug = "similar";
        
ContentWidgetOptions options = new ContentWidgetOptions(this, slug);
options.attributes.put(ContentWidgetOptions.ContentWidgetOptionsAttributeKeyProductId, productId);
ContentWidgetItemLayout itemLayoutDetails = new ContentWidgetItemLayout();
ContentWidgetHorizontalSliderLayout layout = new ContentWidgetHorizontalSliderLayout();
ContentWidgetAppearance contentWidgetAppearance = new ContentWidgetAppearance(layout, itemLayoutDetails);
ContentWidget widget = new ContentWidget(options, contentWidgetAppearance);

View view = widget.getView(); // our widget
insertPoint.addView(view); // your view which will receive widget

Widget Options


The ContentWidgetOptions class is responsible for defining the business logic options of the widget, for example:

  • document slug
  • product Identifier is stored inside class instance in attributes HashMap.

The table explains the parameters that can be configured in ContentWidgetOptions.

Parameter Type Default Description
activity Activity - Activity to which you insert the widget (must be included)
slug String - Slug responsible for generating data
attributes Hashmap - Custom attributes for generating data

Example

String productId = "10214";
String slug = "similar";
ContentWidgetOptions options = new ContentWidgetOptions(this, slug);
options.attributes.put(ContentWidgetOptions.ContentWidgetOptionsAttributeKeyProductId, productId);

Appearance Configuration


The ContentWidgetAppearance class is responsible for defining the appearance of the widget.

The class consists of parameters that define the widget’s appearance, however two of them are the most important:

  • Main layout class: defines the way of distributing elements in the widget. Currently, two layouts are provided: ContentWidgetHorizontalSliderLayout and ContentWidgetGridLayout.
  • Item layout class: defines appearance and parameters for the item in the widget. Currently, there is only one layout provided: ContentWidgetBaseItemLayout.

The table explains the parameters that can be configured in ContentWidgetAppearance.

Parameter Type Default Description
layout ContentWidgetBaseLayout - Class that inherits from ContentWidgetBaseLayout, contains the UI details of the widget’s layout
itemLayout ContentWidgetBaseItemLayout - Class that inherits from ContentWidgetBaseItemLayout, contains the UI details of a single item in a widget

Layouts

Horizontal Slider


This layout is intended to present recommendations in a fixed-hight horizontal scrollable slider.

Each item in the slider is called a “card”.

Example widget configuration with horizontal slider:

Content Widget - Horizontal Slider

Parameters

The table below contains the parameters that can be configured in ContentWidgetHorizontalSliderLayout.

Parameter Type Default Description
cardViewSize ViewGroup.LayoutParams 0 Size of a single item (cardView). You can provide this parameter in dp (recommended) or px.
cardViewHorizontalSpacing Int 0 Horizontal spacing between items. You can provide this parameter in dp (recommended) or px.
cardViewBackgroundColor Int #fff Background color of a cardView
Tip: CardViewSize can be set either by attribution or by a setter.

Example

ContentWidgetHorizontalSliderLayout layout = new ContentWidgetHorizontalSliderLayout();

layout.setCardViewSize(250, 300);
layout.cardViewHorizontalSpacing = 15;
layout.cardViewBackgroundColor = ContextCompat.getColor(Synerise.getApplicationContext(), R.color.regent);

Grid View


This layout presents recommendations in a vertical scrollable grid, with elements organized into columns and rows. You can create a full- or half-screen widget.

Example widget configuration with grid layout: Content Widget - Grid View

Constructor

Parameter Type Default Description
preferredWidth Float - Preferred width of your widget. You can pass the width in dp (recommended) or px
float screenWidthDp = displayMetrics.widthPixels;
ContentWidgetGridLayout layout = new ContentWidgetGridLayout(screenWidthDp);

Parameters

The table contains the parameters that can be configured in ContentWidgetGridLayout.

Parameter Type Default Description
cardViewSize ViewGroup.LayoutParams 0 Size of a single item (cardView)
cardViewVerticalSpacing Int 0 Vertical spacing between items in dp or px
cardViewHorizontalSpacing Int 0 Horizontal spacing between items in dp or px
cardViewBackgroundColor Int #fff Background color of a cardView
includeEdgeSpacing Boolean false When true, edge spacing is enabled

CardViewSize and preferredWidth can be set by attribution or by a setter.

Example

float screenWidthDp = displayMetrics.widthPixels;
ContentWidgetGridLayout layout = new ContentWidgetGridLayout(screenWidthDp);

layout.setCardViewSize(250, 300);
layout.cardViewHorizontalSpacing = 15;
layout.cardViewVerticalSpacing = 20;
layout.includeEdgeSpacing = false;
layout.cardViewBackgroundColor = ContextCompat.getColor(Synerise.getApplicationContext(), R.color.regent);

Basic item layout


This is the basic layout for items. It contains: the image, the title, and the price from the uploaded data.

Parameters

The table below contains parameters that can be configured in ContentWidgetItemLayout.

Parameter Type Default Description
imageHeightToCardHeightRatio Double 0.6 Image height. A ratio of 0.6 means that the image height equals 60% of the entire height of an item
imageWidthToCardWidthRatio Double 1 Image width. 1 means that the image width is equal to the width of the item
imageMargin Int 0 General margin of the image
cardViewCornerRadius Float 0 Corner radius of the cardView
cardViewElevation Float 0 Elevation of the cardView
itemTitleStyle Typeface - Typeface of the product title
itemTitleSize Int 12 Size of the title
itemTitleColor Int #000 Color of the title text
itemTitleMarginLeft Int 0 Left margin of the title text
itemTitleMarginRight Int 0 Right margin of the title text
itemTitleMarginBottom Int 0 Bottom margin of the title text
itemTitleMarginTop Int 0 Top margin of the title text
itemTitleGravity Int Gravity.LEFT Gravity of the title text
itemPriceStyle Typeface - Typeface of the price
itemPriceSize Int 12 Size of the price text
itemPriceColor Int #000 Color of the price text
itemPriceMarginLeft Int 0 Left margin of the price text
itemPriceMarginRight Int 0 Right margin of the price text
itemPriceMarginTop Int 0 Top margin of the price text
itemPriceMarginBottom Int 0 Bottom margin of the price text
itemPriceGravity Int Gravity.LEFT Gravity of the price text
itemSalePriceStyle Typeface - Typeface of the sale price
itemSalePriceSize Int 12 Size of the sale price
itemSalePriceColor Int #000 Color of the sale price text
itemSalePriceGravity Int Gravity.LEFT Gravity of the sale price text
itemSalePriceMarginLeft Int 0 Left margin of the sale price text
itemSalePriceMarginRight Int 0 Right margin of the sale price text
itemSalePriceMarginTop Int 0 Top margin of the sale price text
itemSalePriceMarginBottom Int 0 Bottom margin of the sale price text
itemSalePriceOrientation Int LinearLayout.HORIZONTAL Orientation of the sale price text
isItemSalePriceVisible boolean false Flag determining whether to show the sale price or not
itemActionButton ImageButtonCustomAction - Object storing all information about the ActionButton
imageButtonCustomActionGravity Int Gravity.TOP Gravity of the actionButton
Important: You can combine the Gravity.* parameters. For example, to align an item to top and right, use: Gravity.TOP | Gravity.RIGHT
Tip: ItemTitleMargins and ItemPriceMargins can be set using setters, as shown below.
public void setItemTitleMargins(int marginLeft, int marginRight, int marginTop, int marginBottom)

Example

ContentWidgetItemLayout itemLayoutDetails = new ContentWidgetItemLayout();
itemLayoutDetails.cardViewElevation = 5;
itemLayoutDetails.cardViewCornerRadius = 10;
itemLayoutDetails.imageHeightToCardHeightRatio = 0.6
itemLayoutDetails.imageWidthToCardWidthRatio = 1;
itemLayoutDetails.imageMargin = 5;
itemLayoutDetails.itemTitleStyle = Typeface.create("sans-serif-condensed", Typeface.NORMAL);
itemLayoutDetails.itemTitleSize = 12;
itemLayoutDetails.itemTitleColor = ContextCompat.getColor(Synerise.getApplicationContext(), R.color.charcoal);
itemLayoutDetails.itemTitleGravity = Gravity.LEFT;
itemLayoutDetails.setItemTitleMargins(10, 0, 0, 0);
itemLayoutDetails.itemPriceStyle = Typeface.create("sans-serif-light", Typeface.BOLD);
itemLayoutDetails.itemPriceSize = 12;
itemLayoutDetails.itemPriceColor = ContextCompat.getColor(Synerise.getApplicationContext(), R.color.charcoal);
itemLayoutDetails.itemPriceGravity = Gravity.LEFT;
itemLayoutDetails.setItemPriceMargins(10, 0, 3, 0);

Interaction with the widget

Public Interface


load() - Starts fetching data and creates a view structure of the widget.

getView() - Gets the root view of the whole widget view structure.

onContentWidgetListener is used to inform developers about the state of a widget. Read more below.

Listener


  • onLoading(ContentWidget contentWidget, boolean isLoading) - Called when the widget loading state changes.
  • onLoad(ContentWidget contentWidget) - Called after the widget is loaded.
  • onLoadingError(ContentWidget contentWidget, ApiError apiError) - Called when an error occurs while loading.
  • onSizeChange(ContentWidget contentWidget, ViewGroup.LayoutParams size) - Called when the widget size changes.
  • onClickActionReceive(ContentWidget contentWidget, BaseModel model) - Called when the user clicks a widget item.

Image Button Custom Action

ImageButtonCustomAction is used to add imageButton to your widget. You can add a singleStateButton or a selectableButton.

Parameters

The following table contains the parameters that can be configured in ImageButtonCustomAction.

Parameter Type Default Description
predefinedAction PredefinedActionType null PredefinedAction determines which event is sent on click
marginLeft Int 0 Left margin of the image button
marginRight Int 0 Right margin of the image button
marginTop Int - Top margin of the image button
marginBottom Int 0 Bottom margin of the image button
Tip: ImageButtonCustomAction margins can be set using setters, as shown below.
public void setImageButtonCustomActionMargins(int marginLeft, int marginRight, int marginTop, int marginBottom)

setStateDrawables

This method is responsible for passing icons to the image button.

Method name: imageButtonCustomAction.setStateDrawables(dislikeIcon, likeIcon)

Declaration

public void setStateDrawables(@NonNull Drawable defaultStateDrawable, @Nullable Drawable selectedStateDrawable)

Parameters

Parameter Type Mandatory Default Description
defaultStateDrawable Drawable yes Icon that appears in default and false state
selectedStateDrawable Drawable no Icon that appears in the selected state

Return Value

Void type

Example

Drawable likeHeart = ContextCompat.getDrawable(this, R.drawable.ic_heart);
Drawable unlikeHeart = ContextCompat.getDrawable(this, R.drawable.ic_heart_outline);
favouriteIcon.setStateDrawables(unlikeHeart, likeHeart);

Note: If you want a singleStateButton, pass null instead of selectedStateDrawable.

Public interface

Set a listener by using setOnItemActionListener.

OnActionItemStateListener is used to inform developers about the state of an ImageButton.

Listener

  • onReceiveClickAction(BaseModel model, boolean isSelected, ImageButton imageButton) - called when the user clicks the image button in the widget.
  • onStateCheck(BaseModel model) - called before showing the view, when selectableImageButton needs information about the button state.

Sample Implementations

Horizontal Slider

Slider layout with a fixed height.

public void loadWidget() {
    String productId = "10214";
    String slug = "similar";
    ContentWidgetOptions options = new ContentWidgetOptions(this, slug);
	options.attributes.put(ContentWidgetOptions.ContentWidgetOptionsAttributeKeyProductId, productId);

    ContentWidgetItemLayout itemLayoutDetails = new ContentWidgetItemLayout();
    ContentWidgetHorizontalSliderLayout layout = new ContentWidgetHorizontalSliderLayout();

    layout.setCardViewSize(250, 350);
    itemLayoutDetails.cardViewElevation = 5;
    itemLayoutDetails.cardViewCornerRadius = 10;
    layout.cardViewHorizontalSpacing = 20;
    layout.cardViewBackgroundColor = ContextCompat.getColor(Synerise.getApplicationContext(), R.color.regent);

    //Image size as ratio to cardview size
    itemLayoutDetails.imageHeightToCardHeightRatio = 0.6
    itemLayoutDetails.imageWidthToCardWidthRatio = 1;
    itemLayoutDetails.imageMargin = 5; //have to be set when you set cardViewElevation

    //TextView product name
    itemLayoutDetails.itemTitleStyle = Typeface.create("sans-serif-condensed", Typeface.NORMAL);
    itemLayoutDetails.itemTitleSize = 12;
    itemLayoutDetails.itemTitleColor = ContextCompat.getColor(Synerise.getApplicationContext(), R.color.charcoal);
    itemLayoutDetails.itemTitleGravity = Gravity.LEFT;
    itemLayoutDetails.setItemTitleMargins(10, 0, 0, 0);

    //TextView Product price
    itemLayoutDetails.itemPriceStyle = Typeface.create("sans-serif-light", Typeface.BOLD);
    itemLayoutDetails.itemPriceSize = 12;
    itemLayoutDetails.itemPriceColor = ContextCompat.getColor(Synerise.getApplicationContext(), R.color.charcoal);
    itemLayoutDetails.itemPriceGravity = Gravity.LEFT;
    itemLayoutDetails.setItemPriceMargins(10, 0, 3, 0);

	//TextView Product Sale price
    itemLayoutDetails.itemSalePriceStyle = Typeface.create("sans-serif", Typeface.BOLD);
    itemLayoutDetails.itemSalePriceSize = 13;
    itemLayoutDetails.itemSalePriceColor = ContextCompat.getColor(Synerise.getApplicationContext(), R.color.red);
    itemLayoutDetails.itemSalePriceOrientation = LinearLayout.HORIZONTAL;
    itemLayoutDetails.isItemSalePriceVisible = true;
    itemLayoutDetails.setItemSalePriceMargins(5, 0, 3, 0);

    //ImageButton
    ImageButtonCustomAction favouriteIcon = new ImageButtonCustomAction();
    Drawable likeHeart = ContextCompat.getDrawable(this, R.drawable.ic_heart);
    Drawable unlikeHeart = ContextCompat.getDrawable(this, R.drawable.ic_heart_outline);
    favouriteIcon.setStateDrawables(unlikeHeart, likeHeart);
    favouriteIcon.setImageButtonCustomActionMargins(0, 0, 0, 0);
    favouriteIcon.predefinedAction = PredefinedActionType.SEND_LIKE_EVENT;
    favouriteIcon.setOnItemActionListener(new OnActionItemStateListener() {
        @Override
        public void onReceiveClickAction(BaseModel model, boolean isSelected, ImageButton imageButton) {
            Recommendation recommendation = (Recommendation) model;
            ViewUtils.pulse(imageButton);
        }

        @Override
        public boolean onStateCheck(BaseModel model) {
			// Here you should set buttonState based on a product and a client.
            return false;
        }
    });

    itemLayoutDetails.setItemAction(favouriteIcon);
	
    ContentWidgetAppearance contentWidgetAppearance = new ContentWidgetAppearance(layout, itemLayoutDetails);
    ContentWidget widget = new ContentWidget(options, contentWidgetAppearance);

    widget.setOnContentWidgetListener(new OnContentWidgetListener() {
            @Override
            public void onLoading(ContentWidget contentWidget, boolean isLoading) {

            }

            @Override
            public void onLoadingError(ContentWidget contentWidget, ApiError apiError) {
                Toast.makeText(getApplicationContext(), apiError.toString(),Toast.LENGTH_LONG).show();
            }

            @Override
            public void onLoad(ContentWidget contentWidget) {
                insertPoint.removeAllViews();
                View view = widget.getView(); // our widget
                insertPoint.addView(view); // your view which will receive widget
            }

            @Override
            public void onClickActionReceive(ContentWidget contentWidget, BaseModel model) {
                Recommendation recommendation = (Recommendation)model;
                startActivity(WidgetRecommendedProductDetailsActivity.createIntent(getApplicationContext(), recommendation.getProductRetailerPartNo()));
            }

            @Override
            public void onSizeChange(ContentWidget contentWidget, ViewGroup.LayoutParams size) {
                ViewGroup.LayoutParams params = insertPoint.getLayoutParams();
                params.height = size.height;
                insertPoint.setLayoutParams(params);
            }
        });
}

Grid View


If you want to avoid double scroll view, you should set the height as shown in the widgetSizeDidChange callback.

private void loadFullScreenWidget() {
    String slug = "similar";
    String productId = "10214";
    ContentWidgetOptions options = new ContentWidgetOptions(this, slug);
	options.attributes.put(ContentWidgetOptions.ContentWidgetOptionsAttributeKeyProductId, productId);
    ContentWidgetItemLayout itemLayoutDetails = new ContentWidgetItemLayout();
    DisplayMetrics displayMetrics = new DisplayMetrics();
    getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
    float screenWidthDp = displayMetrics.widthPixels;
    ContentWidgetGridLayout layout = new ContentWidgetGridLayout(screenWidthDp);

    //CardView parameters
    layout.setCardViewSize(250, 350);
    itemLayoutDetails.cardViewElevation = 5;
    itemLayoutDetails.cardViewCornerRadius = 5;
    layout.cardViewHorizontalSpacing = 10;
    layout.cardViewVerticalSpacing = 10;
    layout.includeEdgeSpacing = false;
    layout.cardViewBackgroundColor = ContextCompat.getColor(Synerise.getApplicationContext(), R.color.regent);

    //Image size as ratio to cardview size
    itemLayoutDetails.imageHeightToCardHeightRatio = 0.7;
    itemLayoutDetails.imageWidthToCardWidthRatio = 1;
    itemLayoutDetails.imageMargin = 5; //have to be set when you set cardViewElevation

    //TextView product name
    itemLayoutDetails.itemTitleStyle = Typeface.create("sans-serif-condensed", Typeface.NORMAL);
    itemLayoutDetails.itemTitleSize = 12;
    itemLayoutDetails.itemTitleColor = ContextCompat.getColor(Synerise.getApplicationContext(), R.color.charcoal);
    itemLayoutDetails.itemTitleGravity = Gravity.LEFT;
    itemLayoutDetails.setItemTitleMargins(10, 0, 10, 0);

    //TextView Product price
    itemLayoutDetails.itemPriceStyle = Typeface.create("sans-serif-light", Typeface.BOLD);
    itemLayoutDetails.itemPriceSize = 12;
    itemLayoutDetails.itemPriceColor = ContextCompat.getColor(Synerise.getApplicationContext(), R.color.charcoal);
    itemLayoutDetails.itemPriceGravity = Gravity.LEFT;
    itemLayoutDetails.setItemPriceMargins(10, 0, 10, 0);
	
	//TextView Product Sale Price
	itemLayoutDetails.itemSalePriceStyle = Typeface.create("sans-serif", Typeface.BOLD)
	itemLayoutDetails.itemSalePriceSize = 13
	itemLayoutDetails.itemSalePriceColor = ContextCompat.getColor(Synerise.getApplicationContext(), R.color.red)
	itemLayoutDetails.itemSalePriceOrientation = LinearLayout.HORIZONTAL
	itemLayoutDetails.isItemSalePriceVisible = true
	itemLayoutDetails.setItemSalePriceMargins(5, 0, 3, 0)
	
	//ImageButton
	val favouriteIcon = ImageButtonCustomAction()
	val likeHeart = ContextCompat.getDrawable(this, R.drawable.ic_heart)
	val unlikeHeart = ContextCompat.getDrawable(this, R.drawable.ic_heart_outline)
	favouriteIcon.setStateDrawables(unlikeHeart, likeHeart)
	favouriteIcon.setImageButtonCustomActionMargins(0, 0, 0, 0)
	favouriteIcon.predefinedAction = PredefinedActionType.SEND_LIKE_EVENT
	favouriteIcon.setOnItemActionListener(object:OnActionItemStateListener() {
		fun onReceiveClickAction(model:BaseModel, isSelected:Boolean, imageButton:ImageButton) {
			val recommendation = model as Recommendation
			ViewUtils.pulse(imageButton)
		}
		
		fun onStateCheck(model:BaseModel):Boolean {
			// Here you should set buttonState based on a product and a client.
			return false
		}
	})
	
	itemLayoutDetails.setItemAction(favouriteIcon)
    ContentWidgetAppearance contentWidgetAppearance = new ContentWidgetAppearance(layout, itemLayoutDetails);
    ContentWidget widget = new ContentWidget(options, contentWidgetAppearance);


    widget.setOnContentWidgetListener(new OnContentWidgetListener() {
            @Override
            public void onLoading(ContentWidget contentWidget, boolean isLoading) {

            }

            @Override
            public void onLoadingError(ContentWidget contentWidget, ApiError apiError) {
                Toast.makeText(getApplicationContext(), apiError.toString(),Toast.LENGTH_LONG).show();
            }

            @Override
            public void onLoad(ContentWidget contentWidget) {
                insertPoint.removeAllViews();
                View view = widget.getView();
                insertPoint.addView(view);
            }

            @Override
            public void onClickActionReceive(ContentWidget contentWidget, BaseModel model) {
                Recommendation recommendation = (Recommendation)model;
                startActivity(WidgetRecommendedProductDetailsActivity.createIntent(getApplicationContext(), recommendation.getProductRetailerPartNo()));
            }

            @Override
            public void onSizeChange(ContentWidget contentWidget, ViewGroup.LayoutParams size) {
                ViewGroup.LayoutParams params = insertPoint.getLayoutParams();
                params.height = size.height;
                insertPoint.setLayoutParams(params);
            }
        });
}

More information


You can find more information under the following links:

😕

We are sorry to hear that

Thank you for helping improve out documentation. If you need help or have any questions, please consider contacting support.

😉

Awesome!

Thank you for helping improve out documentation. If you need help or have any questions, please consider contacting support.