Skip to content
Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,21 @@ public class AdConfiguration: AutoRefreshCountConfig {
Sets an ad unit as an rewarded
*/
public var isRewarded = false

/// The fallback rewarded-ad completion time, in seconds, used when the bid does not provide
/// `completion.banner.time`, `completion.video.time`, or `completion.video.endcard.time`.
///
/// The default value is 120 seconds. Assigning a negative value is ignored.
public var rewardedCompletionTime: TimeInterval {
get { _rewardedCompletionTime }
set {
if newValue >= 0 {
_rewardedCompletionTime = newValue
} else {
Log.warn("The rewarded completion time must not be negative.")
}
}
}

/**
Indicates whether the ad is built-in video e.g. 300x250.
Expand Down Expand Up @@ -134,6 +149,7 @@ public class AdConfiguration: AutoRefreshCountConfig {
// MARK: Private properties

private var _autoRefreshDelay: TimeInterval? = PrebidConstants.AUTO_REFRESH_DELAY_DEFAULT
private var _rewardedCompletionTime = RewardedConfig.fallbackCompletionTime

public var impORTBConfig: String?
public var globalORTBConfig: String?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ public class VideoControlsConfiguration: NSObject {

/// This property indicates whether mute controls is visible on the screen.
public var isSoundButtonVisible = false

/// Use to initialize video controls with server values.
public func initialize(with ortbAdConfiguration: ORTBAdConfiguration?) {

Expand Down Expand Up @@ -136,4 +136,5 @@ public class VideoControlsConfiguration: NSObject {

private var _skipButtonArea = PrebidConstants.BUTTON_AREA_DEFAULT.doubleValue
private var _skipButtonPosition = Position.topLeft

}
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,13 @@ public class RewardedAdUnit: NSObject, BaseInterstitialAdUnitProtocol {
get { adUnitConfig.adConfiguration.videoControlsConfig.isSoundButtonVisible }
set { adUnitConfig.adConfiguration.videoControlsConfig.isSoundButtonVisible = newValue }
}

/// The fallback completion time, in seconds, used when the bid omits rewarded completion criteria.
/// The default value is 120 seconds. Assigning a negative value is ignored.
public var rewardedCompletionTime: TimeInterval {
get { adUnitConfig.adConfiguration.rewardedCompletionTime }
set { adUnitConfig.adConfiguration.rewardedCompletionTime = newValue }
}

// MARK: Internal Properties

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import Foundation

@objc(PBMRewardedConfig) @objcMembers
public class RewardedConfig: NSObject {

static let fallbackCompletionTime: TimeInterval = 120

// MARK: - Reward

Expand Down Expand Up @@ -67,15 +69,26 @@ public class RewardedConfig: NSObject {

// MARK: - Default Values

/// The timeout duration for rewarded completion, measured in seconds.
public let defaultCompletionTime: NSNumber = 120
/// The fallback rewarded completion time, measured in seconds.
public let defaultCompletionTime: NSNumber

/// The playback event when the SDK should send a signal to the application that the user has earned the reward
public let defaultVideoPlaybackEvent = "complete"

private let ortbRewarded: ORTBRewardedConfiguration?

public init(ortbRewarded: ORTBRewardedConfiguration?) {
public convenience init(ortbRewarded: ORTBRewardedConfiguration?) {
self.init(
ortbRewarded: ortbRewarded,
defaultCompletionTime: Self.fallbackCompletionTime
)
}

public init(
ortbRewarded: ORTBRewardedConfiguration?,
defaultCompletionTime: TimeInterval
) {
self.ortbRewarded = ortbRewarded
self.defaultCompletionTime = NSNumber(value: defaultCompletionTime)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public class MediationBaseInterstitialAdUnit : NSObject {
get { adUnitConfig.adConfiguration.videoControlsConfig.isSoundButtonVisible }
set { adUnitConfig.adConfiguration.videoControlsConfig.isSoundButtonVisible = newValue }
}

/// The area for the close button in the video ad.
public var closeButtonArea: Double {
get { adUnitConfig.adConfiguration.videoControlsConfig.closeButtonArea }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@ import Foundation
/// This class is a part of Mediation API.
@objcMembers
public class MediationRewardedAdUnit : MediationBaseInterstitialAdUnit {

/// The fallback completion time, in seconds, used when the bid omits rewarded completion criteria.
/// The default value is 120 seconds. Assigning a negative value is ignored.
public var rewardedCompletionTime: TimeInterval {
get { adUnitConfig.adConfiguration.rewardedCompletionTime }
set { adUnitConfig.adConfiguration.rewardedCompletionTime = newValue }
}

// - MARK: Public Methods

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@ class DisplayView: UIView, PrebidMobileDisplayViewProtocol, AdViewManagerDelegat
adViewManager?.isCreativeOpened ?? false
}

/// The fallback completion time, in seconds, used when the bid omits rewarded completion criteria.
/// The default value is 120 seconds. Assigning a negative value is ignored.
public var rewardedCompletionTime: TimeInterval {
get { adConfiguration.adConfiguration.rewardedCompletionTime }
set { adConfiguration.adConfiguration.rewardedCompletionTime = newValue }
}

// MARK: - Internal Properties

let bid: Bid
Expand Down Expand Up @@ -60,7 +67,11 @@ class DisplayView: UIView, PrebidMobileDisplayViewProtocol, AdViewManagerDelegat
guard transactionFactory == nil else { return }

adConfiguration.adConfiguration.winningBidAdFormat = bid.adFormat
adConfiguration.adConfiguration.rewardedConfig = RewardedConfig(ortbRewarded: bid.rewardedConfig)
let rewardedConfig = RewardedConfig(
ortbRewarded: bid.rewardedConfig,
defaultCompletionTime: rewardedCompletionTime
)
adConfiguration.adConfiguration.rewardedConfig = rewardedConfig

transactionFactory = Factory.createTransactionFactory(
bid: bid,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@ public class InterstitialController:
get { adConfiguration.adConfiguration.videoControlsConfig }
set { adConfiguration.adConfiguration.videoControlsConfig = newValue }
}

/// The fallback completion time, in seconds, used when the bid omits rewarded completion criteria.
/// The default value is 120 seconds. Assigning a negative value is ignored.
public var rewardedCompletionTime: TimeInterval {
get { adConfiguration.adConfiguration.rewardedCompletionTime }
set { adConfiguration.adConfiguration.rewardedCompletionTime = newValue }
}

public var videoParameters: VideoParameters {
get { adConfiguration.adConfiguration.videoParameters }
Expand Down Expand Up @@ -72,14 +79,19 @@ public class InterstitialController:
}

adConfiguration.adConfiguration.winningBidAdFormat = bid.adFormat
adConfiguration.adConfiguration.rewardedConfig = RewardedConfig(ortbRewarded: bid.rewardedConfig)
videoControlsConfig.initialize(with: bid.videoAdConfiguration)

// This part is dedicating to test server-side ad configurations.
// Need to be removed when ext.prebid.passthrough will be available.
#if DEBUG
adConfiguration.adConfiguration.videoControlsConfig.initialize(with: bid.testVideoAdConfiguration)
#endif

let rewardedConfig = RewardedConfig(
ortbRewarded: bid.rewardedConfig,
defaultCompletionTime: rewardedCompletionTime
)
adConfiguration.adConfiguration.rewardedConfig = rewardedConfig

transactionFactory = Factory.createTransactionFactory(
bid: bid,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ public class AdUnitConfig: NSObject, NSCopying {
clone.adConfiguration.isBuiltInVideo = self.adConfiguration.isBuiltInVideo
clone.adConfiguration.isInterstitialAd = self.adConfiguration.isInterstitialAd
clone.adConfiguration.isRewarded = self.adConfiguration.isRewarded
clone.adConfiguration.rewardedCompletionTime = self.adConfiguration.rewardedCompletionTime
clone.adConfiguration.forceInterstitialPresentation = self.adConfiguration.forceInterstitialPresentation
clone.adConfiguration.interstitialLayout = self.adConfiguration.interstitialLayout
clone.nativeAdConfiguration = self.nativeAdConfiguration
Expand Down
35 changes: 35 additions & 0 deletions PrebidMobileTests/RenderingTests/Tests/PBMHTMLCreativeTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,41 @@ class PBMHTMLCreativeTest : XCTestCase, CreativeResolutionDelegate, CreativeView

waitForExpectations(timeout: 10)
}

func testRewardEvent_EndcardUsesConfiguredFallback() {
let exp = expectation(description: "Reward completion")

let rewardedConfig = RewardedConfig(
ortbRewarded: ORTBRewardedConfiguration(),
defaultCompletionTime: 1
)

let adConfiguration = AdConfiguration()
adConfiguration.rewardedConfig = rewardedConfig
adConfiguration.isRewarded = true
let creativeModel = CreativeModel(adConfiguration: adConfiguration)
self.htmlCreative = MockPBMHTMLCreative(
creativeModel: creativeModel,
transaction: UtilitiesForTesting.createEmptyTransaction()
)

htmlCreative.creativeModel.isCompanionAd = true
htmlCreative.onViewabilityChanged(
true,
viewExposure: Factory.createViewExposure(
exposureFactor: 5,
visibleRectangle: CGRect(origin: .zero, size: CGSize(width: 300, height: 250))
)
)

DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
if self.htmlCreative.creativeModel.userHasEarnedReward == true {
exp.fulfill()
}
}

waitForExpectations(timeout: 3)
}

func testPostRewardEvent_Banner() {
let rewardTime: NSNumber = 2
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,50 @@ class VideoControlsConfigTests: XCTestCase {
XCTAssertTrue(adConfiguration.isSoundButtonVisible == false)
}

func testRewardedCompletionTime() {
let adConfiguration = AdConfiguration()
XCTAssertEqual(adConfiguration.rewardedCompletionTime, 120)

adConfiguration.rewardedCompletionTime = 15
XCTAssertEqual(adConfiguration.rewardedCompletionTime, 15)

adConfiguration.rewardedCompletionTime = -1
XCTAssertEqual(adConfiguration.rewardedCompletionTime, 15)
}

func testInterstitialControllerPropagatesRewardedCompletionTime() {
let adConfiguration = AdUnitConfig(configId: "test")
let controller = InterstitialController(
bid: makeBid(),
adConfiguration: adConfiguration
)
controller.rewardedCompletionTime = 15

controller.loadAd()

XCTAssertEqual(
adConfiguration.adConfiguration.rewardedConfig?.defaultCompletionTime,
15
)
}

func testDisplayViewPropagatesRewardedCompletionTime() {
let adConfiguration = AdUnitConfig(configId: "test")
let displayView = DisplayView(
frame: .zero,
bid: makeBid(),
adConfiguration: adConfiguration
)
displayView.rewardedCompletionTime = 20

displayView.loadAd()

XCTAssertEqual(
adConfiguration.adConfiguration.rewardedConfig?.defaultCompletionTime,
20
)
}

func testCloseButtonArea() {
let adConfiguration = VideoControlsConfiguration()
XCTAssertEqual(adConfiguration.closeButtonArea, PrebidConstants.BUTTON_AREA_DEFAULT.doubleValue)
Expand Down Expand Up @@ -73,4 +117,8 @@ class VideoControlsConfigTests: XCTestCase {
XCTAssertEqual(adConfiguration.closeButtonPosition, .topLeft)

}

private func makeBid() -> Bid {
Bid(bid: ORTBBid<ORTBBidExt>(bidID: "test", impid: "imp1", price: 1))
}
}
Loading