Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 11 additions & 7 deletions android/app/src/main/java/tv/broadpeak/simid/app/PlayerActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import androidx.media3.ui.PlayerView
import androidx.core.net.toUri
import androidx.media3.common.util.UnstableApi
import androidx.media3.ui.PlayerNotificationManager
import tv.broadpeak.simid.controller.CreativeData
import tv.broadpeak.simid.controller.MediaState
import tv.broadpeak.simid.controller.Dimensions
import tv.broadpeak.smartlib.SmartLib
Expand Down Expand Up @@ -141,8 +142,9 @@ class PlayerActivity : AppCompatActivity() {
val b = intent.extras
val creativeUrl = b?.getString("creativeUrl") ?: return
val creativeAdParams = b.getString("creativeAdParams") ?: ""
val creativeClickThruUrl = b.getString("creativeClickThruUrl") ?: ""
val creativeDuration = b.getInt("creativeDuration")
loadSimid("input-creative", creativeUrl, creativeAdParams, creativeDuration.toFloat(), true)
loadSimid("input-creative", creativeUrl, creativeAdParams, creativeClickThruUrl, creativeDuration.toFloat(), true)
}

private fun initSmartLib(url: String) {
Expand Down Expand Up @@ -171,7 +173,8 @@ class PlayerActivity : AppCompatActivity() {
runOnUiThread {
val iframeResource = adData.nonLinearIframeResources[0].url
val adParameters = adData.nonLinearIframeResources[0].parameters
loadSimid(adData.adId, iframeResource, adParameters, (adData.duration.toFloat() / 1000.0F))
val clickThruUrl = adData.clickURL
loadSimid(adData.adId, iframeResource, adParameters, clickThruUrl, (adData.duration.toFloat() / 1000.0F))
}
}
}
Expand Down Expand Up @@ -216,7 +219,7 @@ class PlayerActivity : AppCompatActivity() {
}
}

private fun loadSimid(adId: String, creativeUri: String, adParameters: String, duration: Float, autoStart: Boolean = false) {
private fun loadSimid(adId: String, creativeUri: String, adParameters: String, clickThruUrl: String, duration: Float, autoStart: Boolean = false) {

if (playerContainer == null) {
return
Expand All @@ -227,7 +230,8 @@ class PlayerActivity : AppCompatActivity() {

Log.d(TAG, "Load SIMID: ${playerDimensions.toString()} $creativeUri $duration")

val simidController = SimidController(this, applicationContext, playerDimensions, playerDimensions, creativeUri, adParameters, duration)
val creativeData = CreativeData(adParameters, clickThruUrl)
val simidController = SimidController(this, applicationContext, playerDimensions, playerDimensions, creativeUri, creativeData, duration)

simidController.let { controller ->
controller.onAddSimid { webView -> addSimidWebView(adId, webView) }
Expand All @@ -237,7 +241,7 @@ class PlayerActivity : AppCompatActivity() {
controller.onGetMediaState { getMediaState() }
controller.onPauseMedia { pauseMedia() }
controller.onPlayMedia { playMedia() }
controller.onOpenClickthrough { uri -> openClickthrough(uri) }
controller.onOpenPage { uri -> openPage(uri) }
controller.onComplete { skipped -> completeAd(adId, skipped) }

controller.simidControllerApi(bpkSimidController!!)
Expand Down Expand Up @@ -359,8 +363,8 @@ class PlayerActivity : AppCompatActivity() {
return true
}

private fun openClickthrough(uri: String) {
Log.d(TAG, "Open clickthrough: $uri")
private fun openPage(uri: String) {
Log.d(TAG, "Open page: $uri")

val intent = Intent(Intent.ACTION_VIEW, uri.toUri())
startActivity(intent)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import android.app.Activity
import android.content.Context
import android.graphics.Rect
import tv.broadpeak.simid.controller.Dimensions
import tv.broadpeak.simid.controller.CreativeData
import tv.broadpeak.smartlib.ad.simid.GenericSimidControllerApi

class SimidController(
Expand All @@ -12,11 +13,11 @@ class SimidController(
private val mainPlayerDimensions: Dimensions,
private val creativeDimensions: Dimensions,
private val creativeUri: String,
private val adParameters: String = "",
private val creativeData: CreativeData,
private val adDuration: Float = 0.0F,
private val adSkippable: Boolean = false,
private val mediaStatePollingInterval: Long = MEDIA_TIMEUPDATE_INTERVAL_MS
) : tv.broadpeak.simid.controller.SimidController(activity, context, mainPlayerDimensions, creativeDimensions, creativeUri, adParameters, adDuration, adSkippable, mediaStatePollingInterval) {
) : tv.broadpeak.simid.controller.SimidController(activity, context, mainPlayerDimensions, creativeDimensions, creativeUri, creativeData, adDuration, adSkippable, mediaStatePollingInterval) {

private var simidControllerApi: GenericSimidControllerApi? = null

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import kotlinx.serialization.json.encodeToJsonElement
* @param playerDimensions the main player dimensions
* @param creativeDimensions the initial creative dimensions the application/player will set
* @param creativeUri The creative URI
* @param adParameters the creative ad parameters
* @param creativeData the creative data (ad parameters, clickThruUrl)
* @param adDuration the display duration of the creative (0 by default, meaning no requested duration)
* @param adSkippable true if the linear ad is skippable (false by default)
* @param mediaTimeupdateInterval the interval in ms to send media timeupdate message to the creative (250ms by default, -1 to disable)
Expand All @@ -35,7 +35,7 @@ public open class SimidController (
private var playerDimensions: Dimensions,
private var creativeDimensions: Dimensions,
private val creativeUri: String,
private val adParameters: String = "",
private val creativeData: CreativeData,
private val adDuration: Float = 0.0F,
private val adSkippable: Boolean = false,
private val mediaTimeupdateInterval: Long = MEDIA_TIMEUPDATE_INTERVAL_MS
Expand Down Expand Up @@ -65,7 +65,7 @@ public open class SimidController (
private var onShowSimid: ((Boolean) -> Unit)? = null
private var onResizeSimid: ((Dimensions) -> Boolean)? = null
private var onResizePlayer: ((Dimensions) -> Unit)? = null
private var onOpenClickthrough: ((String) -> Unit)? = null
private var onOpenPage: ((String) -> Unit)? = null
private var onComplete: ((Boolean) -> Unit)? = null

private val mainScope = MainScope()
Expand Down Expand Up @@ -103,8 +103,8 @@ public open class SimidController (
this.onResizePlayer = cb
}

fun onOpenClickthrough(cb: (String) -> Unit) {
this.onOpenClickthrough = cb
fun onOpenPage(cb: (String) -> Unit) {
this.onOpenPage = cb
}

fun onComplete(cb: (Boolean) -> Unit) {
Expand Down Expand Up @@ -186,6 +186,7 @@ public open class SimidController (
this.addMessageListener(CreativeMessage.REQUEST_STOP, ::onCreativeRequestStop)
this.addMessageListener(CreativeMessage.EXPAND_NONLINEAR, ::onCreativeExpandNonlinear)
this.addMessageListener(CreativeMessage.COLLAPSE_NONLINEAR, ::onCreativeCollapseNonlinear)
this.addMessageListener(CreativeMessage.CLICK_THRU, ::onCreativeClickThru)
this.addMessageListener(CreativeMessage.REQUEST_NAVIGATION, ::onCreativeRequestNavigation)
}

Expand Down Expand Up @@ -289,17 +290,21 @@ public open class SimidController (
stopAd(StopCode.CREATIVE_INITIATED)
}

private fun onCreativeRequestNavigation(message: Message) {
if (onOpenClickthrough == null) {
rejectMessage(message, PlayerErrorCode.NAVIGATION_NOT_SUPPORTED, "Navigation not supported by the player")
private fun onCreativeClickThru(message: Message) {
val args: CreativeClickThruMessageArgs = json.decodeFromJsonElement<CreativeClickThruMessageArgs>(message.args!!)

// Open landing page only when playerHandles is true
if (!(args.playerHandles ?: false)) {
this.resolveMessage(message)
return
}

val uri = args.uri ?: args.url // url deprecated in favor of uri
this.onOpenUri(message, args.url)
}
private fun onCreativeRequestNavigation(message: Message) {
val args: CreativeRequestNavigationMessageArgs = json.decodeFromJsonElement<CreativeRequestNavigationMessageArgs>(message.args!!)
// Spec §4.4.12.1: resolve before opening the URI so the creative receives
// the message prior to the app being backgrounded.
resolveMessage(message)
onPauseMedia?.invoke()
onOpenClickthrough?.invoke(args.uri)
this.onOpenUri(message, args.uri)
}
//endregion CREATIVE MESSAGE HANDLERS

Expand Down Expand Up @@ -392,15 +397,15 @@ public open class SimidController (
null, // This should be filled in on mobile
false, // player.isDeviceMuted,
1.0F, // player.volume,
if (onOpenClickthrough != null) NavigationSupport.PLAYER_HANDLES else NavigationSupport.AD_HANDLES,
if (this.onOpenPage != null) NavigationSupport.PLAYER_HANDLES else NavigationSupport.AD_HANDLES,
null, // CloseButtonSupport.AD_HANDLES,
adDuration
)

// Escape characters to avoid JSON parsing failure in Creative
val adParams = adParameters.replace("\"", "\\\"")
val adParams = this.creativeData.adParameters.replace("\"", "\\\"")

val creativeData = CreativeData(adParams)
val creativeData = CreativeData(adParams, this.creativeData.clickThruUrl)
val args = PlayerInitMessageArgs(environmentData, creativeData)

try {
Expand Down Expand Up @@ -533,9 +538,26 @@ public open class SimidController (
stopAd(StopCode.NON_LINEAR_DURATION_COMPLETE)
}
}
//endregion MAIN VIDEO STATE

private fun dimensions(rect: Rect): Dimensions {
return Dimensions(rect.top, rect.left, rect.width(), rect.height())
// region CLICK THROUGH
private fun onOpenUri(message: Message, uri: String?) {
if (uri == null) {
this.rejectMessage(message, PlayerErrorCode.NAVIGATION_NOT_SUPPORTED, "Invalid URI")
return
}

if (this.onOpenPage == null) {
this.rejectMessage(message, PlayerErrorCode.NAVIGATION_NOT_SUPPORTED, "Navigation not supported by the player")
return
}

// Spec §4.4.12.1: resolve before opening the window so the creative receives
// the message prior to the app being backgrounded.
this.resolveMessage(message)

this.onPauseMedia?.invoke()
this.onOpenPage?.invoke(uri)
}
//endregion MAIN VIDEO STATE
// endregion CLICK THROUGH
}
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,8 @@ data class CreativeClickThruMessageArgs(
val x: Int?,
val y: Int?,
val playerHandles: Boolean?,
val url: String?
val uri: String?,
val url: String? // deprecated in favor of uri
)

@Serializable
Expand Down
67 changes: 50 additions & 17 deletions ios/SimidController/SimidController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ open class SimidController: SimidComponent, WKScriptMessageHandler, WKNavigation
private var playerDimensions: Dimensions
private var creativeDimensions: Dimensions
private let creativeUri: String
private let adParameters: String
private let creativeData: CreativeData
private let adDuration: Double
private let adSkippable: Bool
private let mediaTimeupdateInterval: UInt64
Expand All @@ -31,15 +31,15 @@ open class SimidController: SimidComponent, WKScriptMessageHandler, WKNavigation
private var onShowSimid: ((Bool) -> Void)?
private var onResizeSimid: ((Dimensions) -> Bool)?
private var onResizePlayer: ((Dimensions) -> Void)?
private var onOpenClickthrough: ((String) -> Void)?
private var onOpenPage: ((String) -> Void)?
private var onComplete: ((Bool) -> Void)?

/**
* Set up the SIMID controller an starts listening for messages from the creative.
* @param playerDimensions the main player dimensions
* @param creativeDimensions the initial creative dimensions the application/player will set
* @param creativeUri The creative URI
* @param adParameters the creative ad parameters
* @param creativeData the creative data (ad parameters, clickThruUrl)
* @param adDuration the display duration of the creative (0 by default, meaning no requested duration)
* @param adSkippable true if the linear ad is skippable (false by default)
* @param mediaTimeupdateInterval the interval in ms to send media timeupdate message to the creative (250ms by default, -1 to disable)
Expand All @@ -48,15 +48,15 @@ open class SimidController: SimidComponent, WKScriptMessageHandler, WKNavigation
playerDimensions: Dimensions,
creativeDimensions: Dimensions,
creativeUri: String,
adParameters: String = "",
creativeData: CreativeData,
adDuration: Double = 0,
adSkippable: Bool = false,
mediaTimeupdateInterval: UInt64 = MEDIA_TIMEUPDATE_INTERVAL_MS
) {
self.playerDimensions = playerDimensions
self.creativeDimensions = creativeDimensions
self.creativeUri = creativeUri
self.adParameters = adParameters
self.creativeData = creativeData
self.adDuration = adDuration
self.adSkippable = adSkippable
self.mediaTimeupdateInterval = mediaTimeupdateInterval
Expand All @@ -73,7 +73,7 @@ open class SimidController: SimidComponent, WKScriptMessageHandler, WKNavigation
public func onShowSimid(_ cb: @escaping (Bool) -> Void) { self.onShowSimid = cb }
public func onResizeSimid(_ cb: @escaping (Dimensions) -> Bool) { self.onResizeSimid = cb }
public func onResizePlayer(_ cb: @escaping (Dimensions) -> Void) { self.onResizePlayer = cb }
public func onOpenClickthrough(_ cb: @escaping (String) -> Void) { self.onOpenClickthrough = cb }
public func onOpenPage(_ cb: @escaping (String) -> Void) { self.onOpenPage = cb }
public func onComplete(_ cb: @escaping (Bool) -> Void) { self.onComplete = cb }

public func getVersion() -> String {
Expand Down Expand Up @@ -159,6 +159,7 @@ open class SimidController: SimidComponent, WKScriptMessageHandler, WKNavigation
addMessageListener(CreativeMessage.REQUEST_STOP) { [weak self] message in self?.onCreativeRequestStop(message) }
addMessageListener(CreativeMessage.EXPAND_NONLINEAR) { [weak self] message in self?.onCreativeExpandNonlinear(message) }
addMessageListener(CreativeMessage.COLLAPSE_NONLINEAR) { [weak self] message in self?.onCreativeCollapseNonlinear(message) }
addMessageListener(CreativeMessage.CLICK_THRU) { [weak self] message in self?.onCreativeClickThru(message) }
addMessageListener(CreativeMessage.REQUEST_NAVIGATION) { [weak self] message in self?.onCreativeRequestNavigation(message) }
}

Expand Down Expand Up @@ -219,7 +220,7 @@ open class SimidController: SimidComponent, WKScriptMessageHandler, WKNavigation
self.creativeDimensions = creativeDim

// If creative successfully resized then resize the main player/
self.onResizePlayer?(mediaDim)
onResizePlayer(mediaDim)

self.resolveMessage(message)

Expand Down Expand Up @@ -256,16 +257,28 @@ open class SimidController: SimidComponent, WKScriptMessageHandler, WKNavigation
_ = self.onPlayMedia?()
(self.onResizeSimid?(creativeDimensions) ?? false) ? self.resolveMessage(message) : rejectMessage(message, errorCode: PlayerErrorCode.UNSPECIFIED, errorMessage: "Unable to collapse nonlinear ad")
}

private func onCreativeClickThru(_ message: Message) {
guard let args = message.args as? CreativeClickThruMessageArgs else {
self.rejectMessage(message)
return
}

// Open landing page only when playerHandles is true
if args.playerHandles != true {
self.resolveMessage(message)
return
}

let uri = args.uri ?? args.url // url deprecated in favor of uri
self.onOpenUri(message: message, uri: uri)
}

private func onCreativeRequestNavigation(_ message: Message) {
guard let args = message.args as? CreativeRequestNavigationMessageArgs else {
self.rejectMessage(message)
return
}

self.resolveMessage(message)
_ = self.onPauseMedia?()
self.onOpenClickthrough?(args.uri)
self.onOpenUri(message: message, uri: args.uri)
}

// MARK: - WEBVIEW MANAGEMENT
Expand Down Expand Up @@ -337,27 +350,27 @@ open class SimidController: SimidComponent, WKScriptMessageHandler, WKNavigation
deviceId: nil,
muted: false,
volume: 1.0,
navigationSupport: onOpenClickthrough != nil
navigationSupport: onOpenPage != nil
? NavigationSupport.PLAYER_HANDLES
: NavigationSupport.AD_HANDLES,
closeButtonSupport: nil,
nonlinearDuration: adDuration
)

// Escape characters to avoid JSON parsing failure in Creative
let adParams = adParameters.replacingOccurrences(
let adParams = self.creativeData.adParameters.replacingOccurrences(
of: "\"",
with: "\\\""
)

let creative = CreativeData(
let creativeData = CreativeData(
adParameters: adParams,
clickThruUrl: ""
clickThruUrl: self.creativeData.clickThruUrl
)

let args = PlayerInitMessageArgs(
environmentData: env,
creativeData: creative
creativeData: creativeData
)

Task {
Expand Down Expand Up @@ -463,4 +476,24 @@ open class SimidController: SimidComponent, WKScriptMessageHandler, WKNavigation
stopAd(reason: StopCode.NON_LINEAR_DURATION_COMPLETE)
}
}

// MARK: - Click through
private func onOpenUri(message: Message, uri: String?) {
guard uri != nil else {
self.rejectMessage(message, errorCode:PlayerErrorCode.NAVIGATION_NOT_SUPPORTED, errorMessage:"Invalid URI")
return
}

guard self.onOpenPage != nil else {
self.rejectMessage(message, errorCode:PlayerErrorCode.NAVIGATION_NOT_SUPPORTED, errorMessage: "Navigation not supported by the player")
return
}

// Spec §4.4.12.1: resolve before opening the window so the creative receives
// the message prior to the app being backgrounded.
self.resolveMessage(message)

_ = self.onPauseMedia?()
self.onOpenPage?(uri!)
}
}
Loading