Recommends Control

The Recommends control is a view and will be put directly into the application layout.

Required Arguments

Creation of the Recommends control requires two pieces of data at construction in order for the control to function properly.

  • Module ID - Unique identifier provided by account manager.
  • Publisher URL - URL for tracking content on the page. For example: If the control is displayed for a specific article within a domain, the URL should be for the specific article versus the base domain.
// Set module and publisher URL
val recommendsControl = RecommendsControl(this,
  "<YOUR MODULE ID>",
  "<YOUR PUBLISHER URL>")
// Set module and publisher URL
let recommendsControl = VASRecommendsControl(
  frame: CGRect(x: 0,
                y: 0,
                width: recommendsContainer.frame.width,
                height: recommendsContainer.frame.height),
  viewController: self,
  module: "<YOUR MODULE ID>",
  publisherUrl: "<YOUR PUBLISHER URL>")

Optional Parameters

While the Recommends control will work without the optional parameters, presentation of the Recommends control and content may be improved by providing optional parameters.

Parameter Description
title A string containing the headline to display about the module (i.e. “You May Like”).

Set an empty string to remove the heading title.
ctaLabel Not all modules will be configured for units containing call to action elements but for those modules, this value will be used for overriding the default call to action label used.

An example of this value would be a typical “Learn More” button within an ad that would instead display “Click Here” if the ctaLabel value was set to “Click Here”.
ageRange Age Range. Specified value will represent one of the following range values in the Recommends.AgeRange enum:

R1_12 - Ages 1 - 12
R13_17 - Ages 13 - 17
R18_24 - Ages 18 - 24
R25_34 - Ages 25 - 34
R35_44 - Ages 35 - 44
R45_54 - Ages 45 - 54
R55_64 - Ages 55 - 64
R65_120 - Ages 65 - 120
UNKNOWN - Unknown
gender User Gender. Specified value will represent one of the following values in the Recommends.Gender enum:

M - Male
F - Female
U - Unknown
contextualData Comma separated list of contextual info for the news/article that the user is reading where the ad will display.
interests Comma separated list of user interests.
wiki Comma separated list of WIKI entities that are associated with the currently viewed article or page.
extras A map of extra parameters that can be passed through on the request.
// create the control
val recommendsControl = RecommendsControl(this,
  "<replace with module>",
  "<replace with publisher URL>")

recommendsControl.id = R.id.recommends_control

// set optional parameters
recommendsControl.title = "My Custom Title"
recommendsControl.ctaLabel = "My Custom CTA Label"
recommendsControl.ageRange = RecommendsControl.AgeRange.R18_24
recommendsControl.gender = RecommendsControl.Gender.M
recommendsControl.contexualData = "technology,search,news,finance,weather"
recommendsControl.interests = "football,android,food"
recommendsControl.wiki = "Apple_Inc,IPhone,NASDAQ"

// create and set optional extras map
val extras = HashMap<String, String>()
extras[RecommendsControl.UserParameterKeys.REGION] = "California"
extras[RecommendsControl.UserParameterKeys.CITY] = "Sunnyvale"
extras[RecommendsControl.UserParameterKeys.POSTAL_CODE] = "94086"

recommendsControl.extras = extras
// NOTE: Do not use this recommendsControl in a handler because it 
// will be captured and cause a retain cycle. If needed, use the 
// property in self (and use weak self).

// create the control view with the initial frame set to view bounds
let recommendsControl = VASRecommendsControl(
  frame: self.view.bounds,
  viewController: self,
  anchorToSuperview: true,
  module: "<replace with module>",
  publisherUrl: "<replace with publisher URL>"
)

// set optional parameters
recommendsControl.title = "My Custom Title"
recommendsControl.ctaLabel = "My Custom CTA Label"
recommendsControl.ageRange = VASRecommendsControl.AgeRange.R18_24
recommendsControl.gender = VASRecommendsControl.Gender.MALE
recommendsControl.contextualData = "technology,search,news,finance,weather"
recommendsControl.interests = "football,android,food"
recommendsControl.wiki = "Apple_Inc,IPhone,NASDAQ"

// Set optional metadata
recommendsControl.extras = [
  VASRecommendsControl.UserParameterKeys.CITY: "Sunnyvale",
  VASRecommendsControl.UserParameterKeys.REGION: "California",
  VASRecommendsControl.UserParameterKeys.POSTAL_CODE: "94086",
]

Alternative Android XML Definition

Instead of creating the control programmatically, the publisher can define the Recommends control via XML layout in the application resources.

// define the recommends namespace within the XML layout
<androidx.constraintlayout.widget.ConstraintLayout    xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:app="http://schemas.android.com/apk/res-auto"
  xmlns:tools="http://schemas.android.com/tools"
  xmlns:recommends="http://verizon.com/ads/recommends">
// define the View Component in the XML layout
<com.verizon.ads.recommendscontrol.RecommendsControl
  android:id="@+id/recommendsControl"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  app:layout_constraintTop_toBottomOf="@id/publisher_content"
  app:layout_constraintStart_toStartOf="parent"
  app:layout_constraintEnd_toEndOf="parent"
  recommends:publisher_url="http://www.yahoo.com"
  recommends:module="mobileweb-test"
  recommends:title="My Custom Title"
  recommends:cta_label="My Custom CTA Label"
  recommends:age_range="R18_24"
  recommends:gender="MALE"
  recommends:contextual_data="technology,search,news,finance,weather"
  recommends:interests="football,android,food"
  recommends:wiki="Apple_Inc,IPhone,NASDAQ"/>

Once the XML layout is defined, inflate the layout in your application and use the View Component by setting optional values, event handlers, etc.

// create and set optional extras map
val extras = HashMap<String, String>()
extras[RecommendsControl.UserParameterKeys.REGION] = "California"
extras[RecommendsControl.UserParameterKeys.CITY] = "Sunnyvale"
extras[RecommendsControl.UserParameterKeys.POSTAL_CODE] = "94086"

recommendsControl.extras = extras

// set event handlers and call loadContent
...

Setup Event Handlers

Setting event handlers are optional but may prove useful from a debugging or tracking perspective.

Click Handling

Any publisher that wants to handle clicks on recirculated versus sponsored content differently should check the “sponsored” argument on the click handler and process the URL for recirculated content themselves.

A publisher must return true from the click handler if handling the click event for recirculated content. This prevents the SDK from also trying to handle the click event.

// setup desired event handlers
recommendsControl.successHandler = {
  Log.i("RecommendsControl", "Recommends content was successfully loaded.")
  // Insert your success handler code here
}

recommendsControl.clickHandler = { url, sponsored ->
  // Insert your click handler code here
  if (!sponsored) {
    Log.i(TAG, "Handling click for organic content")

    ActivityUtils.startCustomTabActivityFromUrl(this, url)

    true
  } else {
    false
  }
}

recommendsControl.errorHandler = { errorInfo ->
  if (errorInfo.errorCode == RecommendsControl.ERROR_CODE_NO_FILL) {
    Log.e(TAG, "The Recommends server did not return any content.")
  } else {
    Log.e(TAG, "ERROR[${errorInfo.errorCode}]: ${errorInfo.description}")
  }
  // Insert your error handler code here
}
// Replace with your success handler code or omit if not needed.
recommendsControl.successHandler = { [weak self] message in
  guard let strongSelf = self, let recommendsControl = self?.recommendsControl else {
    return
  }
            
  print("RECEIVED A SUCCESS CALLBACK - message: \(message)")
            
  strongSelf.recommendsContainer.addSubview(recommendsControl)
}

// Replace with your click handler code or omit if not needed.
recommendsControl.clickHandler = { [weak self] url, sponsored in
  guard let strongSelf = self else { return false }
            
  print("RECEIVED A CLICK CALLBACK - url: \(url) sponsored: \(sponsored)")
            
  guard !sponsored else {
    // indicate that we did not handle the click and default processing should be done
    return false
  }
            
  strongSelf.handleRecirculatedClick(url)
            
  // indicate that we handled the click and nothing more needs to be done
  return true
}

// Replace with your error handler code or omit if not needed.
recommendsControl.errorHandler = { [weak self] error in
  guard self != nil else { return }
            
  print("RECEIVED AN ERROR CALLBACK - error: \(error.description)")
            
  if (error.domain == VASRecommendsError.domain &&
    error.code == VASRecommendsError.Code.noFill.rawValue) {
    print("The server did not return any content.")
  }
}

// Replace with your resized handler or omit if not needed for your layout.
recommendsControl.resizedHandler = { [weak self] size in
  guard let strongSelf = self else { return }
            
  print("RECEIVED A RESIZED CALLBACK - size: \(String(describing: size))")
            
  strongSelf.recommendsControlHeight.constant = size.height
}

Layout Control

The layout logic below is one example of how the control can be added to a view hierarchy. The specific layout approach will depend on the publisher implementation.

// Add the control to the view hierarchy
val layoutParams = ConstraintLayout.LayoutParams(
  ConstraintLayout.LayoutParams.MATCH_PARENT,
  ConstraintLayout.LayoutParams.WRAP_CONTENT
)

content_layout.addView(recommendsControl, layoutParams)

val constraintSet = ConstraintSet()
constraintSet.clone(content_layout)
constraintSet.connect(R.id.recommends_control, ConstraintSet.TOP,
  R.id.publisher_content, ConstraintSet.BOTTOM)

constraintSet.applyTo(content_layout)
// Add the control to the view hierarchy
var recommendsContainer: UIView!
...
recommendsContainer.addSubview(recommendsControl)

Load Content

Loading the content should be done as early as possible in the lifecycle of the screen so that content is ready to be displayed by the time the user reaches the Recommends control.

If loadContent is called more than once, the subsequent call will result in the Recommends content being reloaded.

// Load Recommends content
recommendsControl.loadContent()
// Load Recommends content
recommendsControl.loadContent()

Mobile ID

The SDK will automatically collect mobile identification data that is compliant with GDPR, CCPA and other privacy settings for use in targeting behavior.

Further details around privacy compliance and settings can be found here.