diff --git a/Example/PrebidDemoKotlin/src/main/java/org/prebid/mobile/prebidkotlindemo/activities/ads/admob/AdMobVideoRewardedActivity.kt b/Example/PrebidDemoKotlin/src/main/java/org/prebid/mobile/prebidkotlindemo/activities/ads/admob/AdMobVideoRewardedActivity.kt
index 73f4c8348..6d9702ec3 100644
--- a/Example/PrebidDemoKotlin/src/main/java/org/prebid/mobile/prebidkotlindemo/activities/ads/admob/AdMobVideoRewardedActivity.kt
+++ b/Example/PrebidDemoKotlin/src/main/java/org/prebid/mobile/prebidkotlindemo/activities/ads/admob/AdMobVideoRewardedActivity.kt
@@ -60,7 +60,9 @@ class AdMobVideoRewardedActivity : BaseAdActivity() {
override fun onAdLoaded(ad: RewardedAd) {
Log.d("AdMobRewarded", "Ad was loaded.")
rewardedAd = ad
- rewardedAd?.show(this@AdMobVideoRewardedActivity) {}
+ rewardedAd?.show(this@AdMobVideoRewardedActivity) {
+ Log.d("AdExample", "User earned reward: ${it.amount} ${it.type}")
+ }
}
override fun onAdFailedToLoad(adError: LoadAdError) {
diff --git a/Example/PrebidDemoKotlin/src/main/java/org/prebid/mobile/prebidkotlindemo/activities/ads/applovin/AppLovinMaxVideoRewardedActivity.kt b/Example/PrebidDemoKotlin/src/main/java/org/prebid/mobile/prebidkotlindemo/activities/ads/applovin/AppLovinMaxVideoRewardedActivity.kt
index 6c7f5d132..97295925f 100644
--- a/Example/PrebidDemoKotlin/src/main/java/org/prebid/mobile/prebidkotlindemo/activities/ads/applovin/AppLovinMaxVideoRewardedActivity.kt
+++ b/Example/PrebidDemoKotlin/src/main/java/org/prebid/mobile/prebidkotlindemo/activities/ads/applovin/AppLovinMaxVideoRewardedActivity.kt
@@ -16,6 +16,7 @@
package org.prebid.mobile.prebidkotlindemo.activities.ads.applovin
import android.os.Bundle
+import android.util.Log
import com.applovin.mediation.MaxAd
import com.applovin.mediation.MaxError
import com.applovin.mediation.MaxReward
@@ -56,7 +57,9 @@ class AppLovinMaxVideoRewardedActivity : BaseAdActivity() {
override fun onAdDisplayFailed(ad: MaxAd?, error: MaxError?) {}
override fun onRewardedVideoStarted(ad: MaxAd?) {}
override fun onRewardedVideoCompleted(ad: MaxAd?) {}
- override fun onUserRewarded(ad: MaxAd?, reward: MaxReward?) {}
+ override fun onUserRewarded(ad: MaxAd?, reward: MaxReward?) {
+ Log.d("AdExample", "User earned reward: $reward")
+ }
})
val mediationUtils = MaxMediationRewardedUtils(maxRewardedAd)
diff --git a/Example/PrebidDemoKotlin/src/main/java/org/prebid/mobile/prebidkotlindemo/activities/ads/gam/rendering/GamRenderingApiVideoRewardedActivity.kt b/Example/PrebidDemoKotlin/src/main/java/org/prebid/mobile/prebidkotlindemo/activities/ads/gam/rendering/GamRenderingApiVideoRewardedActivity.kt
index 7e69f8ff7..bb50a2481 100644
--- a/Example/PrebidDemoKotlin/src/main/java/org/prebid/mobile/prebidkotlindemo/activities/ads/gam/rendering/GamRenderingApiVideoRewardedActivity.kt
+++ b/Example/PrebidDemoKotlin/src/main/java/org/prebid/mobile/prebidkotlindemo/activities/ads/gam/rendering/GamRenderingApiVideoRewardedActivity.kt
@@ -16,11 +16,13 @@
package org.prebid.mobile.prebidkotlindemo.activities.ads.gam.rendering
import android.os.Bundle
+import android.util.Log
import org.prebid.mobile.api.exceptions.AdException
import org.prebid.mobile.api.rendering.RewardedAdUnit
import org.prebid.mobile.api.rendering.listeners.RewardedAdUnitListener
import org.prebid.mobile.eventhandlers.GamRewardedEventHandler
import org.prebid.mobile.prebidkotlindemo.activities.BaseAdActivity
+import org.prebid.mobile.rendering.interstitial.rewarded.Reward
class GamRenderingApiVideoRewardedActivity : BaseAdActivity() {
@@ -32,7 +34,7 @@ class GamRenderingApiVideoRewardedActivity : BaseAdActivity() {
private var adUnit: RewardedAdUnit? = null
- override fun onCreate(savedInstanceState: Bundle?) {
+ override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
createAd()
@@ -50,7 +52,9 @@ class GamRenderingApiVideoRewardedActivity : BaseAdActivity() {
override fun onAdFailed(rewardedAdUnit: RewardedAdUnit?, exception: AdException?) {}
override fun onAdClicked(rewardedAdUnit: RewardedAdUnit?) {}
override fun onAdClosed(rewardedAdUnit: RewardedAdUnit?) {}
- override fun onUserEarnedReward(rewardedAdUnit: RewardedAdUnit?) {}
+ override fun onUserEarnedReward(rewardedAdUnit: RewardedAdUnit?, reward: Reward?) {
+ Log.d("AdExample", "User earned reward: $reward")
+ }
})
adUnit?.loadAd()
}
diff --git a/Example/PrebidDemoKotlin/src/main/java/org/prebid/mobile/prebidkotlindemo/activities/ads/inapp/InAppVideoRewardedActivity.kt b/Example/PrebidDemoKotlin/src/main/java/org/prebid/mobile/prebidkotlindemo/activities/ads/inapp/InAppVideoRewardedActivity.kt
index c0c53dfbb..1d3e565f4 100644
--- a/Example/PrebidDemoKotlin/src/main/java/org/prebid/mobile/prebidkotlindemo/activities/ads/inapp/InAppVideoRewardedActivity.kt
+++ b/Example/PrebidDemoKotlin/src/main/java/org/prebid/mobile/prebidkotlindemo/activities/ads/inapp/InAppVideoRewardedActivity.kt
@@ -16,10 +16,12 @@
package org.prebid.mobile.prebidkotlindemo.activities.ads.inapp
import android.os.Bundle
+import android.util.Log
import org.prebid.mobile.api.exceptions.AdException
import org.prebid.mobile.api.rendering.RewardedAdUnit
import org.prebid.mobile.api.rendering.listeners.RewardedAdUnitListener
import org.prebid.mobile.prebidkotlindemo.activities.BaseAdActivity
+import org.prebid.mobile.rendering.interstitial.rewarded.Reward
class InAppVideoRewardedActivity : BaseAdActivity() {
@@ -47,7 +49,9 @@ class InAppVideoRewardedActivity : BaseAdActivity() {
override fun onAdFailed(rewardedAdUnit: RewardedAdUnit?, exception: AdException?) {}
override fun onAdClicked(rewardedAdUnit: RewardedAdUnit?) {}
override fun onAdClosed(rewardedAdUnit: RewardedAdUnit?) {}
- override fun onUserEarnedReward(rewardedAdUnit: RewardedAdUnit?) {}
+ override fun onUserEarnedReward(rewardedAdUnit: RewardedAdUnit?, reward: Reward?) {
+ Log.d("InAppVideoRewarded", "User earned reward: $reward")
+ }
})
adUnit?.loadAd()
}
diff --git a/Example/PrebidInternalTestApp/src/main/java/org/prebid/mobile/renderingtestapp/InternalTestApplication.kt b/Example/PrebidInternalTestApp/src/main/java/org/prebid/mobile/renderingtestapp/InternalTestApplication.kt
index 909a52576..6ae722c31 100644
--- a/Example/PrebidInternalTestApp/src/main/java/org/prebid/mobile/renderingtestapp/InternalTestApplication.kt
+++ b/Example/PrebidInternalTestApp/src/main/java/org/prebid/mobile/renderingtestapp/InternalTestApplication.kt
@@ -86,7 +86,7 @@ class InternalTestApplication : MultiDexApplication() {
}
fun clearAdConfigSettings() {
- PreferenceManager.getDefaultSharedPreferences(this).edit().clear().apply()
+ PreferenceManager.getDefaultSharedPreferences(this).edit().remove(getString(R.string.key_keep_settings)).apply()
}
private fun checkKeepConsentSettingsFlag() {
diff --git a/Example/PrebidInternalTestApp/src/main/java/org/prebid/mobile/renderingtestapp/plugplay/bidding/admob/AdMobRewardedFragment.kt b/Example/PrebidInternalTestApp/src/main/java/org/prebid/mobile/renderingtestapp/plugplay/bidding/admob/AdMobRewardedFragment.kt
index 4345e9f2b..b72ac1706 100644
--- a/Example/PrebidInternalTestApp/src/main/java/org/prebid/mobile/renderingtestapp/plugplay/bidding/admob/AdMobRewardedFragment.kt
+++ b/Example/PrebidInternalTestApp/src/main/java/org/prebid/mobile/renderingtestapp/plugplay/bidding/admob/AdMobRewardedFragment.kt
@@ -3,8 +3,6 @@ package org.prebid.mobile.renderingtestapp.plugplay.bidding.admob
import android.os.Bundle
import android.util.Log
import android.view.View
-import android.widget.Button
-import android.widget.TextView
import com.google.android.gms.ads.AdError
import com.google.android.gms.ads.AdRequest
import com.google.android.gms.ads.FullScreenContentCallback
@@ -19,7 +17,6 @@ import org.prebid.mobile.renderingtestapp.R
import org.prebid.mobile.renderingtestapp.databinding.FragmentAdmobRewardedBinding
import org.prebid.mobile.renderingtestapp.plugplay.config.AdConfiguratorDialogFragment
import org.prebid.mobile.renderingtestapp.utils.BaseEvents
-import org.prebid.mobile.renderingtestapp.widgets.EventCounterView
open class AdMobRewardedFragment : AdFragment() {
@@ -76,6 +73,9 @@ open class AdMobRewardedFragment : AdFragment() {
rewardedAd = ad
rewardedAd?.fullScreenContentCallback = createFullScreenContentCallback()
+ rewardedAd?.setOnPaidEventListener {
+ Log.d(TAG, "User earned reward: $it")
+ }
}
override fun onAdFailedToLoad(adError: LoadAdError) {
@@ -115,6 +115,7 @@ open class AdMobRewardedFragment : AdFragment() {
val rewardAmount = rewardItem.amount
val rewardType = rewardItem.type
Log.d(TAG, "User earned the reward ($rewardAmount, $rewardType)")
+ events.rewarded(true)
}
binding.btnLoad.text = getString(R.string.text_retry)
} else if (binding.btnLoad.text == getString(R.string.text_retry)) {
@@ -155,6 +156,7 @@ open class AdMobRewardedFragment : AdFragment() {
fun impression(b: Boolean) = enable(R.id.btnAdImpression, b)
fun clicked(b: Boolean) = enable(R.id.btnAdClicked, b)
fun failed(b: Boolean) = enable(R.id.btnAdFailed, b)
+ fun rewarded(b: Boolean) = enable(R.id.btnUserRewarded, b)
fun showed(b: Boolean) = enable(R.id.btnAdShowed, b)
fun dismissed(b: Boolean) = enable(R.id.btnAdDismissed, b)
diff --git a/Example/PrebidInternalTestApp/src/main/java/org/prebid/mobile/renderingtestapp/plugplay/bidding/base/BaseBidRewardedFragment.kt b/Example/PrebidInternalTestApp/src/main/java/org/prebid/mobile/renderingtestapp/plugplay/bidding/base/BaseBidRewardedFragment.kt
index 16f00a75b..ed174724f 100644
--- a/Example/PrebidInternalTestApp/src/main/java/org/prebid/mobile/renderingtestapp/plugplay/bidding/base/BaseBidRewardedFragment.kt
+++ b/Example/PrebidInternalTestApp/src/main/java/org/prebid/mobile/renderingtestapp/plugplay/bidding/base/BaseBidRewardedFragment.kt
@@ -19,25 +19,24 @@ package org.prebid.mobile.renderingtestapp.plugplay.bidding.base
import android.os.Bundle
import android.util.Log
import android.view.View
-import android.widget.Button
import org.prebid.mobile.api.exceptions.AdException
import org.prebid.mobile.api.rendering.RewardedAdUnit
import org.prebid.mobile.api.rendering.listeners.RewardedAdUnitListener
+import org.prebid.mobile.rendering.interstitial.rewarded.Reward
import org.prebid.mobile.renderingtestapp.AdFragment
import org.prebid.mobile.renderingtestapp.R
-import org.prebid.mobile.renderingtestapp.databinding.FragmentBiddingInterstitialBinding
+import org.prebid.mobile.renderingtestapp.databinding.FragmentBiddingRewardedBinding
import org.prebid.mobile.renderingtestapp.plugplay.config.AdConfiguratorDialogFragment
import org.prebid.mobile.renderingtestapp.utils.BaseEvents
-import org.prebid.mobile.renderingtestapp.widgets.EventCounterView
abstract class BaseBidRewardedFragment : AdFragment() {
private val TAG = BaseBidRewardedFragment::class.java.simpleName
- override val layoutRes = R.layout.fragment_bidding_interstitial
+ override val layoutRes = R.layout.fragment_bidding_rewarded
protected var rewardedAdUnit: RewardedAdUnit? = null
- protected val binding: FragmentBiddingInterstitialBinding
+ protected val binding: FragmentBiddingRewardedBinding
get() = getBinding()
protected lateinit var events: Events
@@ -84,11 +83,10 @@ abstract class BaseBidRewardedFragment : AdFragment() {
}
}
- protected fun createRewardedAdUnitListener() = object :
- RewardedAdUnitListener {
+ protected fun createRewardedAdUnitListener() = object : RewardedAdUnitListener {
override fun onAdLoaded(rewardedAdUnit: RewardedAdUnit?) {
- Log.d(TAG, "onAdLoaded() called with: reward = [${rewardedAdUnit?.userReward}]")
+ Log.d(TAG, "onAdLoaded() called")
events.loaded(true)
binding.btnLoad.setText(R.string.text_show)
binding.btnLoad.isEnabled = true
@@ -115,8 +113,9 @@ abstract class BaseBidRewardedFragment : AdFragment() {
events.closed(true)
}
- override fun onUserEarnedReward(rewardedAdUnit: RewardedAdUnit?) {
- Log.d(TAG, "onUserEarnedReward() called with: reward = [${rewardedAdUnit?.userReward}]")
+ override fun onUserEarnedReward(rewardedAdUnit: RewardedAdUnit?, reward: Reward?) {
+ Log.d(TAG, "User earned reward: $reward")
+ events.reward(true)
}
}
@@ -128,6 +127,7 @@ abstract class BaseBidRewardedFragment : AdFragment() {
fun clicked(b: Boolean) = enable(R.id.btnAdClicked, b)
fun closed(b: Boolean) = enable(R.id.btnAdClosed, b)
fun failed(b: Boolean) = enable(R.id.btnAdFailed, b)
+ fun reward(b: Boolean) = enable(R.id.btnReward, b)
fun displayed(b: Boolean) = enable(R.id.btnAdDisplayed, b)
diff --git a/Example/PrebidInternalTestApp/src/main/java/org/prebid/mobile/renderingtestapp/plugplay/bidding/max/MaxRewardedFragment.kt b/Example/PrebidInternalTestApp/src/main/java/org/prebid/mobile/renderingtestapp/plugplay/bidding/max/MaxRewardedFragment.kt
index 0aa2fc6b6..08cc638c5 100644
--- a/Example/PrebidInternalTestApp/src/main/java/org/prebid/mobile/renderingtestapp/plugplay/bidding/max/MaxRewardedFragment.kt
+++ b/Example/PrebidInternalTestApp/src/main/java/org/prebid/mobile/renderingtestapp/plugplay/bidding/max/MaxRewardedFragment.kt
@@ -3,8 +3,6 @@ package org.prebid.mobile.renderingtestapp.plugplay.bidding.max
import android.os.Bundle
import android.util.Log
import android.view.View
-import android.widget.Button
-import android.widget.TextView
import com.applovin.mediation.MaxAd
import com.applovin.mediation.MaxError
import com.applovin.mediation.MaxReward
@@ -17,7 +15,6 @@ import org.prebid.mobile.renderingtestapp.R
import org.prebid.mobile.renderingtestapp.databinding.FragmentBiddingRewardedApplovinMaxBinding
import org.prebid.mobile.renderingtestapp.plugplay.config.AdConfiguratorDialogFragment
import org.prebid.mobile.renderingtestapp.utils.BaseEvents
-import org.prebid.mobile.renderingtestapp.widgets.EventCounterView
open class MaxRewardedFragment : AdFragment() {
@@ -139,6 +136,7 @@ open class MaxRewardedFragment : AdFragment() {
}
override fun onUserRewarded(ad: MaxAd?, reward: MaxReward?) {
+ Log.d(TAG, "User earned reward: ${reward?.label} ${reward?.amount}")
events.userRewarded(true)
}
}
diff --git a/Example/PrebidInternalTestApp/src/main/java/org/prebid/mobile/renderingtestapp/utils/DemoItemProvider.kt b/Example/PrebidInternalTestApp/src/main/java/org/prebid/mobile/renderingtestapp/utils/DemoItemProvider.kt
index 5afe44381..1c7f7756b 100644
--- a/Example/PrebidInternalTestApp/src/main/java/org/prebid/mobile/renderingtestapp/utils/DemoItemProvider.kt
+++ b/Example/PrebidInternalTestApp/src/main/java/org/prebid/mobile/renderingtestapp/utils/DemoItemProvider.kt
@@ -1129,6 +1129,125 @@ class DemoItemProvider private constructor() {
)
)
+ // Rewarded
+ demoList.add(
+ DemoItem(
+ getString(R.string.demo_bidding_in_app_display_rewarded_time),
+ ppmRewardedAction,
+ ppmVideoTagList,
+ createBannerBundle(
+ R.string.imp_prebid_id_display_rewarded_time,
+ null,
+ 320,
+ 480
+ )
+ )
+ )
+ demoList.add(
+ DemoItem(
+ getString(R.string.demo_bidding_in_app_display_rewarded_event),
+ ppmRewardedAction,
+ ppmVideoTagList,
+ createBannerBundle(
+ R.string.imp_prebid_id_display_rewarded_event,
+ null,
+ 320,
+ 480
+ )
+ )
+ )
+ demoList.add(
+ DemoItem(
+ getString(R.string.demo_bidding_in_app_display_rewarded_default),
+ ppmRewardedAction,
+ ppmVideoTagList,
+ createBannerBundle(
+ R.string.imp_prebid_id_display_rewarded_default,
+ null,
+ 320,
+ 480
+ )
+ )
+ )
+ demoList.add(
+ DemoItem(
+ getString(R.string.demo_bidding_in_app_video_rewarded_time),
+ ppmRewardedAction,
+ ppmVideoTagList,
+ createBannerBundle(
+ R.string.imp_prebid_id_video_rewarded_time,
+ null,
+ 320,
+ 480
+ )
+ )
+ )
+ demoList.add(
+ DemoItem(
+ getString(R.string.demo_bidding_in_app_video_rewarded_event),
+ ppmRewardedAction,
+ ppmVideoTagList,
+ createBannerBundle(
+ R.string.imp_prebid_id_video_rewarded_event,
+ null,
+ 320,
+ 480
+ )
+ )
+ )
+ demoList.add(
+ DemoItem(
+ getString(R.string.demo_bidding_in_app_video_rewarded_default),
+ ppmRewardedAction,
+ ppmVideoTagList,
+ createBannerBundle(
+ R.string.imp_prebid_id_video_rewarded_default,
+ null,
+ 320,
+ 480
+ )
+ )
+ )
+ demoList.add(
+ DemoItem(
+ getString(R.string.demo_bidding_in_app_video_rewarded_endcard_time),
+ ppmRewardedAction,
+ ppmVideoTagList,
+ createBannerBundle(
+ R.string.imp_prebid_id_video_rewarded_endcard_time,
+ null,
+ 320,
+ 480
+ )
+ )
+ )
+ demoList.add(
+ DemoItem(
+ getString(R.string.demo_bidding_in_app_video_rewarded_endcard_event),
+ ppmRewardedAction,
+ ppmVideoTagList,
+ createBannerBundle(
+ R.string.imp_prebid_id_video_rewarded_endcard_event,
+ null,
+ 320,
+ 480
+ )
+ )
+ )
+ demoList.add(
+ DemoItem(
+ getString(R.string.demo_bidding_in_app_video_rewarded_endcard_default),
+ ppmRewardedAction,
+ ppmVideoTagList,
+ createBannerBundle(
+ R.string.imp_prebid_id_video_rewarded_endcard_default,
+ null,
+ 320,
+ 480
+ )
+ )
+ )
+
// Native
demoList.add(
DemoItem(
diff --git a/Example/PrebidInternalTestApp/src/main/res/layout/events_admob_rewarded.xml b/Example/PrebidInternalTestApp/src/main/res/layout/events_admob_rewarded.xml
index 27a7f18aa..4248b8e5b 100644
--- a/Example/PrebidInternalTestApp/src/main/res/layout/events_admob_rewarded.xml
+++ b/Example/PrebidInternalTestApp/src/main/res/layout/events_admob_rewarded.xml
@@ -69,4 +69,11 @@
android:text="@string/event_admob_failed"
android:layout_marginTop="@dimen/event_top_margin"/>
+
+
\ No newline at end of file
diff --git a/Example/PrebidInternalTestApp/src/main/res/layout/events_rewarded.xml b/Example/PrebidInternalTestApp/src/main/res/layout/events_rewarded.xml
new file mode 100644
index 000000000..1740524b9
--- /dev/null
+++ b/Example/PrebidInternalTestApp/src/main/res/layout/events_rewarded.xml
@@ -0,0 +1,85 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Example/PrebidInternalTestApp/src/main/res/layout/fragment_bidding_rewarded.xml b/Example/PrebidInternalTestApp/src/main/res/layout/fragment_bidding_rewarded.xml
new file mode 100644
index 000000000..2328738ed
--- /dev/null
+++ b/Example/PrebidInternalTestApp/src/main/res/layout/fragment_bidding_rewarded.xml
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Example/PrebidInternalTestApp/src/main/res/values/strings.xml b/Example/PrebidInternalTestApp/src/main/res/values/strings.xml
index c38886eec..201df95f7 100755
--- a/Example/PrebidInternalTestApp/src/main/res/values/strings.xml
+++ b/Example/PrebidInternalTestApp/src/main/res/values/strings.xml
@@ -119,6 +119,19 @@
Video Interstitial 320x480 With End Card With Ad Configuration (In-App)
Video Interstitial Vertical With End Card (In-App)
Video Interstitial Landscape With End Card (In-App)
+
+ Display Rewarded 320x480 (Default)
+ Display Rewarded 320x480 (5s to reward + 3s postreward + autoclose)
+ Display Rewarded 320x480 (click for url event + 3s postreward + close button)
+
+ Video Rewarded 320x480 (Default)
+ Video Rewarded 320x480 (3s to reward + 3s postreward + autoclose)
+ Video Rewarded 320x480 (midpoint + 2s postreward + close button)
+
+ Video Rewarded Endcard 320x480 (Default)
+ Video Rewarded Endcard 320x480 (5s to reward + 3s postreward + autoclose)
+ Video Rewarded Endcard 320x480 (click for url event + 3s postreward + close button)
+
Video Rewarded 320x480 (In-App)
Video Rewarded 320x480 with End Card (In-App) [noBids]
Video Rewarded 320x480 without End Card (In-App)
@@ -360,6 +373,18 @@
prebid-demo-display-interstitial-320-480
prebid-ita-display-interstitial-320-480-meta-custom-renderer
+ prebid-demo-banner-rewarded-default
+ prebid-demo-banner-rewarded-time
+ prebid-demo-banner-rewarded-event
+
+ prebid-demo-video-rewarded-default
+ prebid-demo-video-rewarded-time
+ prebid-demo-video-rewarded-playbackevent
+
+ prebid-demo-video-rewarded-endcard-default
+ prebid-demo-video-rewarded-endcard-time
+ prebid-demo-video-rewarded-endcard-event
+
Imp id is set in another place
prebid-demo-video-outstream
prebid-demo-video-outstream-original-api
diff --git a/PrebidMobile/PrebidMobile-admobAdapters/src/main/java/org/prebid/mobile/admob/PrebidRewardedAdapter.java b/PrebidMobile/PrebidMobile-admobAdapters/src/main/java/org/prebid/mobile/admob/PrebidRewardedAdapter.java
index c36eeb855..2f496e670 100644
--- a/PrebidMobile/PrebidMobile-admobAdapters/src/main/java/org/prebid/mobile/admob/PrebidRewardedAdapter.java
+++ b/PrebidMobile/PrebidMobile-admobAdapters/src/main/java/org/prebid/mobile/admob/PrebidRewardedAdapter.java
@@ -3,15 +3,16 @@
import androidx.annotation.Keep;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-
import com.google.android.gms.ads.mediation.MediationAdLoadCallback;
import com.google.android.gms.ads.mediation.MediationRewardedAd;
import com.google.android.gms.ads.mediation.MediationRewardedAdCallback;
import com.google.android.gms.ads.mediation.MediationRewardedAdConfiguration;
-
+import com.google.android.gms.ads.rewarded.RewardItem;
+import org.jetbrains.annotations.NotNull;
import org.prebid.mobile.api.exceptions.AdException;
import org.prebid.mobile.rendering.bidding.display.InterstitialController;
import org.prebid.mobile.rendering.bidding.interfaces.InterstitialControllerListener;
+import org.prebid.mobile.rendering.interstitial.rewarded.Reward;
/**
* Prebid rewarded adapter for AdMob integration.
@@ -41,8 +42,9 @@ public void loadRewardedAd(
}
try {
- InterstitialControllerListener listener = getListener(adMobLoadListener);
- interstitialController = new InterstitialController(configuration.getContext(), listener);
+ InterstitialControllerListener prebidListener = getListener(adMobLoadListener);
+ interstitialController = new InterstitialController(configuration.getContext(), prebidListener);
+ interstitialController.setRewardListener(prebidListener::onUserEarnedReward);
interstitialController.loadAd(responseId, true);
} catch (AdException e) {
adMobLoadListener.onFailure(AdErrors.interstitialControllerError(e.getMessage()));
@@ -92,6 +94,31 @@ public void onInterstitialFailedToLoad(AdException exception) {
exception.getMessage() != null ? exception.getMessage() : "Failed to load ad"
));
}
+
+ @Override
+ public void onUserEarnedReward() {
+ if (rewardedAdCallback != null && interstitialController != null) {
+ Reward reward = interstitialController.getReward();
+ rewardedAdCallback.onUserEarnedReward(new RewardItem() {
+ @Override
+ public int getAmount() {
+ if (reward != null) {
+ return reward.getCount();
+ }
+ return 0;
+ }
+
+ @NonNull
+ @Override
+ public @NotNull String getType() {
+ if (reward != null) {
+ return reward.getType();
+ }
+ return "";
+ }
+ });
+ }
+ }
};
}
diff --git a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/api/rendering/BaseInterstitialAdUnit.java b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/api/rendering/BaseInterstitialAdUnit.java
index f5bb7bf99..2eea11a21 100644
--- a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/api/rendering/BaseInterstitialAdUnit.java
+++ b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/api/rendering/BaseInterstitialAdUnit.java
@@ -16,17 +16,10 @@
package org.prebid.mobile.api.rendering;
-import static org.prebid.mobile.api.rendering.BaseInterstitialAdUnit.InterstitialAdUnitState.LOADING;
-import static org.prebid.mobile.api.rendering.BaseInterstitialAdUnit.InterstitialAdUnitState.READY_FOR_LOAD;
-import static org.prebid.mobile.api.rendering.BaseInterstitialAdUnit.InterstitialAdUnitState.READY_TO_DISPLAY_GAM;
-import static org.prebid.mobile.api.rendering.BaseInterstitialAdUnit.InterstitialAdUnitState.READY_TO_DISPLAY_PREBID;
-
import android.content.Context;
-
import androidx.annotation.FloatRange;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
-
import org.prebid.mobile.ContentObject;
import org.prebid.mobile.DataObject;
import org.prebid.mobile.LogUtil;
@@ -48,6 +41,8 @@
import java.util.Map;
import java.util.Set;
+import static org.prebid.mobile.api.rendering.BaseInterstitialAdUnit.InterstitialAdUnitState.*;
+
/**
* Internal base interstitial ad unit for rendering API.
*/
@@ -55,7 +50,9 @@ public abstract class BaseInterstitialAdUnit {
private static final String TAG = BaseInterstitialAdUnit.class.getSimpleName();
- protected AdUnitConfiguration adUnitConfig;
+ protected boolean userHasNotEarnedRewardYet = true;
+
+ protected AdUnitConfiguration config = new AdUnitConfiguration();
private BidLoader bidLoader;
private BidResponse bidResponse;
@@ -64,7 +61,7 @@ public abstract class BaseInterstitialAdUnit {
private final WeakReference weakContext;
private final BidRequesterListener bidRequesterListener = createBidRequesterListener();
- private final InterstitialControllerListener controllerListener = createInterstitialControllerListener();
+ protected final InterstitialControllerListener controllerListener = createInterstitialControllerListener();
protected BaseInterstitialAdUnit(Context context) {
weakContext = new WeakReference<>(context);
@@ -84,6 +81,8 @@ protected BaseInterstitialAdUnit(Context context) {
* Executes ad loading if no request is running.
*/
public void loadAd() {
+ userHasNotEarnedRewardYet = true;
+
if (bidLoader == null) {
LogUtil.error(TAG, "loadAd: Failed. BidLoader is not initialized.");
return;
@@ -136,7 +135,7 @@ public void addContextData(
String key,
String value
) {
- adUnitConfig.addExtData(key, value);
+ config.addExtData(key, value);
}
/**
@@ -147,7 +146,7 @@ public void updateContextData(
String key,
Set value
) {
- adUnitConfig.addExtData(key, value);
+ config.addExtData(key, value);
}
/**
@@ -155,7 +154,7 @@ public void updateContextData(
*/
@Deprecated
public void removeContextData(String key) {
- adUnitConfig.removeExtData(key);
+ config.removeExtData(key);
}
/**
@@ -163,7 +162,7 @@ public void removeContextData(String key) {
*/
@Deprecated
public void clearContextData() {
- adUnitConfig.clearExtData();
+ config.clearExtData();
}
/**
@@ -171,7 +170,7 @@ public void clearContextData() {
*/
@Deprecated
public Map> getContextDataDictionary() {
- return adUnitConfig.getExtDataDictionary();
+ return config.getExtDataDictionary();
}
/**
@@ -179,7 +178,7 @@ public Map> getContextDataDictionary() {
*/
@Deprecated
public void addContextKeyword(String keyword) {
- adUnitConfig.addExtKeyword(keyword);
+ config.addExtKeyword(keyword);
}
/**
@@ -187,7 +186,7 @@ public void addContextKeyword(String keyword) {
*/
@Deprecated
public void addContextKeywords(Set keywords) {
- adUnitConfig.addExtKeywords(keywords);
+ config.addExtKeywords(keywords);
}
/**
@@ -195,7 +194,7 @@ public void addContextKeywords(Set keywords) {
*/
@Deprecated
public void removeContextKeyword(String keyword) {
- adUnitConfig.removeExtKeyword(keyword);
+ config.removeExtKeyword(keyword);
}
/**
@@ -203,7 +202,7 @@ public void removeContextKeyword(String keyword) {
*/
@Deprecated
public Set getContextKeywordsSet() {
- return adUnitConfig.getExtKeywordsSet();
+ return config.getExtKeywordsSet();
}
/**
@@ -211,7 +210,7 @@ public Set getContextKeywordsSet() {
*/
@Deprecated
public void clearContextKeywords() {
- adUnitConfig.clearExtKeywords();
+ config.clearExtKeywords();
}
@@ -219,92 +218,92 @@ public void addExtData(
String key,
String value
) {
- adUnitConfig.addExtData(key, value);
+ config.addExtData(key, value);
}
public void updateExtData(
String key,
Set value
) {
- adUnitConfig.addExtData(key, value);
+ config.addExtData(key, value);
}
public void removeExtData(String key) {
- adUnitConfig.removeExtData(key);
+ config.removeExtData(key);
}
public void clearExtData() {
- adUnitConfig.clearExtData();
+ config.clearExtData();
}
public Map> getExtDataDictionary() {
- return adUnitConfig.getExtDataDictionary();
+ return config.getExtDataDictionary();
}
public void addExtKeyword(String keyword) {
- adUnitConfig.addExtKeyword(keyword);
+ config.addExtKeyword(keyword);
}
public void addExtKeywords(Set keywords) {
- adUnitConfig.addExtKeywords(keywords);
+ config.addExtKeywords(keywords);
}
public void removeExtKeyword(String keyword) {
- adUnitConfig.removeExtKeyword(keyword);
+ config.removeExtKeyword(keyword);
}
public Set getExtKeywordsSet() {
- return adUnitConfig.getExtKeywordsSet();
+ return config.getExtKeywordsSet();
}
public void clearExtKeywords() {
- adUnitConfig.clearExtKeywords();
+ config.clearExtKeywords();
}
public void setAppContent(ContentObject content) {
- adUnitConfig.setAppContent(content);
+ config.setAppContent(content);
}
public ContentObject getAppContent() {
- return adUnitConfig.getAppContent();
+ return config.getAppContent();
}
public void addUserData(DataObject dataObject) {
- adUnitConfig.addUserData(dataObject);
+ config.addUserData(dataObject);
}
public ArrayList getUserData() {
- return adUnitConfig.getUserData();
+ return config.getUserData();
}
public void clearUserData() {
- adUnitConfig.clearUserData();
+ config.clearUserData();
}
@Nullable
public String getOrtbConfig() {
- return adUnitConfig.getOrtbConfig();
+ return config.getOrtbConfig();
}
public void setOrtbConfig(@Nullable String ortbConfig) {
- adUnitConfig.setOrtbConfig(ortbConfig);
+ config.setOrtbConfig(ortbConfig);
}
@Nullable
public String getPbAdSlot() {
- return adUnitConfig.getPbAdSlot();
+ return config.getPbAdSlot();
}
public void setPbAdSlot(String adSlot) {
- adUnitConfig.setPbAdSlot(adSlot);
+ config.setPbAdSlot(adSlot);
}
/**
* Sets delay in seconds to show skip or close button.
*/
public void setSkipDelay(int secondsDelay) {
- adUnitConfig.setSkipDelay(secondsDelay);
+ config.setSkipDelay(secondsDelay);
}
/**
@@ -312,7 +311,7 @@ public void setSkipDelay(int secondsDelay) {
* If value less than 0.05, size will be default.
*/
public void setSkipButtonArea(@FloatRange(from = 0, to = 1.0) double buttonArea) {
- adUnitConfig.setSkipButtonArea(buttonArea);
+ config.setSkipButtonArea(buttonArea);
}
/**
@@ -320,19 +319,19 @@ public void setSkipButtonArea(@FloatRange(from = 0, to = 1.0) double buttonArea)
* Default value TOP_RIGHT.
*/
public void setSkipButtonPosition(Position skipButtonPosition) {
- adUnitConfig.setSkipButtonPosition(skipButtonPosition);
+ config.setSkipButtonPosition(skipButtonPosition);
}
public void setIsMuted(boolean isMuted) {
- adUnitConfig.setIsMuted(isMuted);
+ config.setIsMuted(isMuted);
}
public void setIsSoundButtonVisible(boolean isSoundButtonVisible) {
- adUnitConfig.setIsSoundButtonVisible(isSoundButtonVisible);
+ config.setIsSoundButtonVisible(isSoundButtonVisible);
}
public void setMaxVideoDuration(int seconds) {
- adUnitConfig.setMaxVideoDuration(seconds);
+ config.setMaxVideoDuration(seconds);
}
/**
@@ -340,7 +339,7 @@ public void setMaxVideoDuration(int seconds) {
* If value less than 0.05, size will be default.
*/
public void setCloseButtonArea(@FloatRange(from = 0, to = 1.0) double closeButtonArea) {
- adUnitConfig.setCloseButtonArea(closeButtonArea);
+ config.setCloseButtonArea(closeButtonArea);
}
/**
@@ -348,7 +347,7 @@ public void setCloseButtonArea(@FloatRange(from = 0, to = 1.0) double closeButto
* Default value TOP_RIGHT.
*/
public void setCloseButtonPosition(@Nullable Position closeButtonPosition) {
- adUnitConfig.setCloseButtonPosition(closeButtonPosition);
+ config.setCloseButtonPosition(closeButtonPosition);
}
/**
@@ -364,8 +363,8 @@ public void destroy() {
}
protected void init(AdUnitConfiguration adUnitConfiguration) {
- adUnitConfig = adUnitConfiguration;
- adUnitConfig.setAdPosition(AdPosition.FULLSCREEN);
+ config = adUnitConfiguration;
+ config.setAdPosition(AdPosition.FULLSCREEN);
initPrebidRenderingSdk();
initBidLoader();
@@ -374,7 +373,7 @@ protected void init(AdUnitConfiguration adUnitConfiguration) {
protected void loadPrebidAd() {
PrebidMobilePluginRenderer plugin = PrebidMobilePluginRegister.getInstance().getPluginForPreferredRenderer(bidResponse);
if (plugin != null) {
- interstitialController = plugin.createInterstitialController(getContext(), controllerListener, adUnitConfig, bidResponse);
+ interstitialController = plugin.createInterstitialController(getContext(), controllerListener, config, bidResponse);
}
if (interstitialController == null) {
notifyErrorListener(new AdException(
@@ -382,7 +381,7 @@ protected void loadPrebidAd() {
"InterstitialController is not defined. Unable to process bid."
));
} else {
- interstitialController.loadAd(adUnitConfig, bidResponse);
+ interstitialController.loadAd(config, bidResponse);
}
}
@@ -404,7 +403,7 @@ private void initPrebidRenderingSdk() {
}
private void initBidLoader() {
- bidLoader = new BidLoader(adUnitConfig, bidRequesterListener);
+ bidLoader = new BidLoader(config, bidRequesterListener);
}
private Bid getWinnerBid() {
@@ -429,7 +428,7 @@ final InterstitialAdUnitState getAdUnitState() {
}
public void addContent(ContentObject content) {
- adUnitConfig.setAppContent(content);
+ config.setAppContent(content);
}
private BidRequesterListener createBidRequesterListener() {
@@ -478,11 +477,23 @@ public void onInterstitialDisplayed() {
@Override
public void onInterstitialClosed() {
notifyAdEventListener(AdListenerEvent.AD_CLOSE);
- notifyAdEventListener(AdListenerEvent.USER_RECEIVED_PREBID_REWARD);
+ notifyUserReward();
+ }
+
+ @Override
+ public void onUserEarnedReward() {
+ notifyUserReward();
}
};
}
+ protected void notifyUserReward() {
+ if (userHasNotEarnedRewardYet) {
+ notifyAdEventListener(AdListenerEvent.USER_RECEIVED_PREBID_REWARD);
+ userHasNotEarnedRewardYet = false;
+ }
+ }
+
enum AdListenerEvent {
AD_CLOSE,
diff --git a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/api/rendering/InterstitialAdUnit.java b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/api/rendering/InterstitialAdUnit.java
index d196c3105..94e6a679c 100644
--- a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/api/rendering/InterstitialAdUnit.java
+++ b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/api/rendering/InterstitialAdUnit.java
@@ -16,14 +16,9 @@
package org.prebid.mobile.api.rendering;
-import static org.prebid.mobile.api.rendering.BaseInterstitialAdUnit.InterstitialAdUnitState.READY_FOR_LOAD;
-import static org.prebid.mobile.api.rendering.BaseInterstitialAdUnit.InterstitialAdUnitState.READY_TO_DISPLAY_GAM;
-
import android.content.Context;
-
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-
import org.prebid.mobile.AdSize;
import org.prebid.mobile.LogUtil;
import org.prebid.mobile.api.data.AdFormat;
@@ -32,7 +27,6 @@
import org.prebid.mobile.api.rendering.listeners.InterstitialAdUnitListener;
import org.prebid.mobile.api.rendering.pluginrenderer.PluginEventListener;
import org.prebid.mobile.api.rendering.pluginrenderer.PrebidMobilePluginRegister;
-import org.prebid.mobile.configuration.AdUnitConfiguration;
import org.prebid.mobile.rendering.bidding.data.bid.Bid;
import org.prebid.mobile.rendering.bidding.interfaces.InterstitialEventHandler;
import org.prebid.mobile.rendering.bidding.interfaces.StandaloneInterstitialEventHandler;
@@ -40,6 +34,9 @@
import java.util.EnumSet;
+import static org.prebid.mobile.api.rendering.BaseInterstitialAdUnit.InterstitialAdUnitState.READY_FOR_LOAD;
+import static org.prebid.mobile.api.rendering.BaseInterstitialAdUnit.InterstitialAdUnitState.READY_TO_DISPLAY_GAM;
+
/**
* Interstitial ad unit for rendering API.
*/
@@ -115,11 +112,10 @@ public InterstitialAdUnit(
}
this.eventHandler.setInterstitialEventListener(interstitialEventListener);
- AdUnitConfiguration adUnitConfiguration = new AdUnitConfiguration();
- adUnitConfiguration.setConfigId(configId);
- adUnitConfiguration.setAdUnitFormats(adUnitFormats);
- adUnitConfiguration.addAdFormat(AdFormat.INTERSTITIAL);
- init(adUnitConfiguration);
+ config.setConfigId(configId);
+ config.setAdUnitFormats(adUnitFormats);
+ config.addAdFormat(AdFormat.INTERSTITIAL);
+ init(config);
}
@@ -172,11 +168,11 @@ public void setInterstitialAdUnitListener(@Nullable InterstitialAdUnitListener a
}
public void setPluginEventListener(PluginEventListener pluginEventListener) {
- PrebidMobilePluginRegister.getInstance().registerEventListener(pluginEventListener, adUnitConfig.getFingerprint());
+ PrebidMobilePluginRegister.getInstance().registerEventListener(pluginEventListener, config.getFingerprint());
}
public void setMinSizePercentage(AdSize minSizePercentage) {
- adUnitConfig.setMinSizePercentage(minSizePercentage);
+ config.setMinSizePercentage(minSizePercentage);
}
@@ -188,7 +184,7 @@ public void destroy() {
}
adUnitEventsListener = null;
- PrebidMobilePluginRegister.getInstance().unregisterEventListener(adUnitConfig.getFingerprint());
+ PrebidMobilePluginRegister.getInstance().unregisterEventListener(config.getFingerprint());
}
diff --git a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/api/rendering/RewardedAdUnit.java b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/api/rendering/RewardedAdUnit.java
index 019cbf67e..16667fb7b 100644
--- a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/api/rendering/RewardedAdUnit.java
+++ b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/api/rendering/RewardedAdUnit.java
@@ -17,9 +17,7 @@
package org.prebid.mobile.api.rendering;
import android.content.Context;
-
import androidx.annotation.Nullable;
-
import org.prebid.mobile.LogUtil;
import org.prebid.mobile.api.data.AdFormat;
import org.prebid.mobile.api.exceptions.AdException;
@@ -29,6 +27,8 @@
import org.prebid.mobile.rendering.bidding.interfaces.RewardedEventHandler;
import org.prebid.mobile.rendering.bidding.interfaces.StandaloneRewardedVideoEventHandler;
import org.prebid.mobile.rendering.bidding.listeners.RewardedVideoEventListener;
+import org.prebid.mobile.rendering.interstitial.rewarded.Reward;
+import org.prebid.mobile.rendering.interstitial.rewarded.RewardManager;
import java.util.EnumSet;
@@ -39,72 +39,46 @@ public class RewardedAdUnit extends BaseInterstitialAdUnit {
private static final String TAG = RewardedAdUnit.class.getSimpleName();
+ /**
+ * Handler that is responsible for requesting, displaying and destroying of
+ * primary ad (e.g. GAM). Also it tracks impression and sets listener.
+ */
private final RewardedEventHandler eventHandler;
- @Nullable private RewardedAdUnitListener rewardedAdUnitListener;
-
- @Nullable private Object userReward;
-
- //region ==================== Listener implementation
-
- private final RewardedVideoEventListener eventListener = new RewardedVideoEventListener() {
- @Override
- public void onPrebidSdkWin() {
- if (isBidInvalid()) {
- changeInterstitialAdUnitState(InterstitialAdUnitState.READY_FOR_LOAD);
- notifyErrorListener(new AdException(
- AdException.INTERNAL_ERROR,
- "WinnerBid is null when executing onPrebidSdkWin."
- ));
- return;
- }
-
- loadPrebidAd();
- }
-
- @Override
- public void onAdServerWin(Object userReward) {
- RewardedAdUnit.this.userReward = userReward;
- changeInterstitialAdUnitState(InterstitialAdUnitState.READY_TO_DISPLAY_GAM);
- notifyAdEventListener(AdListenerEvent.AD_LOADED);
- }
-
- @Override
- public void onAdFailed(AdException exception) {
- if (isBidInvalid()) {
- changeInterstitialAdUnitState(InterstitialAdUnitState.READY_FOR_LOAD);
- notifyErrorListener(exception);
- return;
- }
-
- onPrebidSdkWin();
- }
-
- @Override
- public void onAdClicked() {
- notifyAdEventListener(AdListenerEvent.AD_CLICKED);
- }
-
- @Override
- public void onAdClosed() {
- notifyAdEventListener(AdListenerEvent.AD_CLOSE);
- }
-
- @Override
- public void onAdDisplayed() {
- changeInterstitialAdUnitState(InterstitialAdUnitState.READY_FOR_LOAD);
- notifyAdEventListener(AdListenerEvent.AD_DISPLAYED);
- }
-
- @Override
- public void onUserEarnedReward() {
- if (rewardedAdUnitListener != null) {
- rewardedAdUnitListener.onUserEarnedReward(RewardedAdUnit.this);
- }
- }
- };
- //endregion ==================== Listener implementation
+ /**
+ * Interstitial ad units events listener (like onAdLoaded, onAdFailed...)
+ */
+ @Nullable
+ private RewardedAdUnitListener userListener;
+
+ /**
+ * Listener that must be applied to InterstitialEventHandler.
+ * It is responsible for onAdServerWin or onPrebidSdkWin.
+ */
+ private final RewardedVideoEventListener eventListener = createRewardedListener();
+
+ /**
+ * Constructor that creates the instance with a {@link StandaloneRewardedVideoEventHandler}
+ * for integration without any primary ad server.
+ *
+ * @param context Android context
+ * @param configId configuration id for on Prebid Server
+ */
+ public RewardedAdUnit(
+ Context context,
+ String configId
+ ) {
+ this(context, configId, new StandaloneRewardedVideoEventHandler());
+ }
+ /**
+ * Constructor that initializes a RewardedAdUnit with a custom event handler.
+ * It supports GAM integration (use {@code GamRewardedEventHandler})
+ *
+ * @param context the Android context
+ * @param configId the configuration id on Prebid Server
+ * @param eventHandler the event handler for primary ad server responsible for managing rewarded ad
+ */
public RewardedAdUnit(
Context context,
String configId,
@@ -122,40 +96,35 @@ public RewardedAdUnit(
init(adUnitConfiguration);
}
- public RewardedAdUnit(
- Context context,
- String configId
- ) {
- this(context, configId, new StandaloneRewardedVideoEventHandler());
- }
-
+ /**
+ * Executes ad loading if no request is running.
+ */
@Override
public void loadAd() {
super.loadAd();
- userReward = null;
+ RewardManager rewardManager = config.getRewardManager();
+ rewardManager.clear();
+ rewardManager.setRewardListener(controllerListener::onUserEarnedReward);
}
+ /**
+ * Cleans up resources when destroyed. It has to be called if the ad unit is no longer needed.
+ */
@Override
public void destroy() {
super.destroy();
+ config.getRewardManager().clear();
if (eventHandler != null) {
eventHandler.destroy();
}
}
- //region ==================== getters and setters
- public void setRewardedAdUnitListener(
- @Nullable
- RewardedAdUnitListener rewardedAdUnitListener
- ) {
- this.rewardedAdUnitListener = rewardedAdUnitListener;
- }
-
- @Nullable
- public Object getUserReward() {
- return userReward;
+ /**
+ * Sets the listener for the rewarded ad events.
+ */
+ public void setRewardedAdUnitListener(@Nullable RewardedAdUnitListener userListener) {
+ this.userListener = userListener;
}
- //endregion ==================== getters and setters
@Override
void requestAdWithBid(
@@ -171,39 +140,107 @@ void showGamAd() {
}
@Override
- void notifyAdEventListener(AdListenerEvent adListenerEvent) {
- if (rewardedAdUnitListener == null) {
+ void notifyAdEventListener(AdListenerEvent event) {
+ if (userListener == null) {
LogUtil.debug(
TAG,
- "notifyAdEventListener: Failed. AdUnitListener is null. Passed listener event: " + adListenerEvent
+ "notifyAdEventListener: Failed. AdUnitListener is null. Passed listener event: " + event
);
return;
}
- switch (adListenerEvent) {
+ switch (event) {
case AD_CLOSE:
- rewardedAdUnitListener.onAdClosed(RewardedAdUnit.this);
+ userListener.onAdClosed(RewardedAdUnit.this);
break;
case AD_LOADED:
- rewardedAdUnitListener.onAdLoaded(RewardedAdUnit.this);
+ userListener.onAdLoaded(RewardedAdUnit.this);
break;
case AD_DISPLAYED:
- rewardedAdUnitListener.onAdDisplayed(RewardedAdUnit.this);
+ userListener.onAdDisplayed(RewardedAdUnit.this);
break;
case AD_CLICKED:
- rewardedAdUnitListener.onAdClicked(RewardedAdUnit.this);
+ userListener.onAdClicked(RewardedAdUnit.this);
break;
case USER_RECEIVED_PREBID_REWARD:
- rewardedAdUnitListener.onUserEarnedReward(RewardedAdUnit.this);
+ Reward reward = getReward();
+ LogUtil.debug(TAG, "Internal reward listener: " + reward);
+ userListener.onUserEarnedReward(RewardedAdUnit.this, reward);
break;
}
}
@Override
void notifyErrorListener(AdException exception) {
- if (rewardedAdUnitListener != null) {
- rewardedAdUnitListener.onAdFailed(RewardedAdUnit.this, exception);
+ if (userListener != null) {
+ userListener.onAdFailed(RewardedAdUnit.this, exception);
+ }
+ }
+
+ @Nullable
+ private Reward getReward() {
+ Reward reward = config.getRewardManager().getRewardedExt().getReward();
+ if (reward != null) {
+ return reward;
}
+
+ return eventHandler.getReward();
+ }
+
+ private RewardedVideoEventListener createRewardedListener() {
+ return new RewardedVideoEventListener() {
+ @Override
+ public void onPrebidSdkWin() {
+ if (isBidInvalid()) {
+ changeInterstitialAdUnitState(InterstitialAdUnitState.READY_FOR_LOAD);
+ notifyErrorListener(new AdException(
+ AdException.INTERNAL_ERROR,
+ "WinnerBid is null when executing onPrebidSdkWin."
+ ));
+ return;
+ }
+
+ loadPrebidAd();
+ }
+
+ @Override
+ public void onAdServerWin(Object userReward) {
+ changeInterstitialAdUnitState(InterstitialAdUnitState.READY_TO_DISPLAY_GAM);
+ notifyAdEventListener(AdListenerEvent.AD_LOADED);
+ }
+
+ @Override
+ public void onAdFailed(AdException exception) {
+ if (isBidInvalid()) {
+ changeInterstitialAdUnitState(InterstitialAdUnitState.READY_FOR_LOAD);
+ notifyErrorListener(exception);
+ return;
+ }
+
+ onPrebidSdkWin();
+ }
+
+ @Override
+ public void onAdClicked() {
+ notifyAdEventListener(AdListenerEvent.AD_CLICKED);
+ }
+
+ @Override
+ public void onAdClosed() {
+ notifyAdEventListener(AdListenerEvent.AD_CLOSE);
+ }
+
+ @Override
+ public void onAdDisplayed() {
+ changeInterstitialAdUnitState(InterstitialAdUnitState.READY_FOR_LOAD);
+ notifyAdEventListener(AdListenerEvent.AD_DISPLAYED);
+ }
+
+ @Override
+ public void onUserEarnedReward() {
+ notifyUserReward();
+ }
+ };
}
}
diff --git a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/api/rendering/listeners/RewardedAdUnitListener.java b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/api/rendering/listeners/RewardedAdUnitListener.java
index cbb06a25e..0370aa695 100644
--- a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/api/rendering/listeners/RewardedAdUnitListener.java
+++ b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/api/rendering/listeners/RewardedAdUnitListener.java
@@ -16,8 +16,10 @@
package org.prebid.mobile.api.rendering.listeners;
+import androidx.annotation.Nullable;
import org.prebid.mobile.api.exceptions.AdException;
import org.prebid.mobile.api.rendering.RewardedAdUnit;
+import org.prebid.mobile.rendering.interstitial.rewarded.Reward;
/**
* Listener interface representing RewardedAdUnit events.
@@ -63,7 +65,8 @@ public interface RewardedAdUnitListener {
/**
* Executed when user receives reward.
*
- * @param rewardedAdUnit view of the corresponding event. Contains reward instance inside. Prebid reward is always null.
+ * @param rewardedAdUnit the rewarded ad unit
+ * @param reward the reward object. It can be null if it is not set in the ad server.
*/
- void onUserEarnedReward(RewardedAdUnit rewardedAdUnit);
+ void onUserEarnedReward(RewardedAdUnit rewardedAdUnit, @Nullable Reward reward);
}
diff --git a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/configuration/AdUnitConfiguration.java b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/configuration/AdUnitConfiguration.java
index 3ca8c41b7..1213e0627 100644
--- a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/configuration/AdUnitConfiguration.java
+++ b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/configuration/AdUnitConfiguration.java
@@ -3,30 +3,19 @@
import androidx.annotation.FloatRange;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-
-import org.prebid.mobile.AdSize;
-import org.prebid.mobile.BannerParameters;
-import org.prebid.mobile.ContentObject;
-import org.prebid.mobile.DataObject;
-import org.prebid.mobile.LogUtil;
-import org.prebid.mobile.VideoParameters;
+import org.prebid.mobile.*;
import org.prebid.mobile.api.data.AdFormat;
import org.prebid.mobile.api.data.AdUnitFormat;
import org.prebid.mobile.api.data.Position;
import org.prebid.mobile.rendering.bidding.data.bid.BidResponse;
import org.prebid.mobile.rendering.interstitial.InterstitialSizes;
+import org.prebid.mobile.rendering.interstitial.rewarded.RewardManager;
import org.prebid.mobile.rendering.models.AdPosition;
import org.prebid.mobile.rendering.models.PlacementType;
import org.prebid.mobile.rendering.utils.helpers.Utils;
import org.prebid.mobile.rendering.video.ExoPlayerView;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.EnumSet;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
+import java.util.*;
public class AdUnitConfiguration {
@@ -39,6 +28,7 @@ public class AdUnitConfiguration {
private boolean isMuted = false;
private boolean isSoundButtonVisible = false;
private boolean isOriginalAdUnit = false;
+ private boolean hasEndCard = false;
private int videoSkipOffset = SKIP_OFFSET_NOT_ASSIGNED;
private int autoRefreshDelayInMillis = 0;
@@ -70,6 +60,7 @@ public class AdUnitConfiguration {
private BannerParameters bannerParameters;
private VideoParameters videoParameters;
private NativeAdUnitConfiguration nativeConfiguration;
+ private RewardManager rewardManager = new RewardManager();
private final EnumSet adFormats = EnumSet.noneOf(AdFormat.class);
private final HashSet adSizes = new HashSet<>();
@@ -551,6 +542,22 @@ public void setOrtbConfig(@Nullable String ortbConfig) {
this.ortbConfig = ortbConfig;
}
+ public boolean getHasEndCard() {
+ return hasEndCard;
+ }
+
+ public void setHasEndCard(boolean hasEndCard) {
+ this.hasEndCard = hasEndCard;
+ }
+
+ public RewardManager getRewardManager() {
+ return rewardManager;
+ }
+
+ public void setRewardManager(RewardManager rewardManager) {
+ this.rewardManager = rewardManager;
+ }
+
@Override
public boolean equals(Object o) {
if (this == o) {
diff --git a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/bidding/data/bid/Bid.java b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/bidding/data/bid/Bid.java
index df727a7b5..2db8932a9 100644
--- a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/bidding/data/bid/Bid.java
+++ b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/bidding/data/bid/Bid.java
@@ -18,12 +18,13 @@
import android.util.Base64;
-
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-
import org.json.JSONArray;
import org.json.JSONObject;
import org.prebid.mobile.api.data.BidInfo;
+import org.prebid.mobile.rendering.interstitial.rewarded.RewardedExt;
+import org.prebid.mobile.rendering.interstitial.rewarded.RewardedExtParser;
import org.prebid.mobile.rendering.models.internal.MacrosModel;
import org.prebid.mobile.rendering.models.openrtb.bidRequests.MobileSdkPassThrough;
import org.prebid.mobile.rendering.utils.helpers.MacrosResolutionHelper;
@@ -135,6 +136,9 @@ public class Bid {
private MobileSdkPassThrough mobileSdkPassThrough;
+ @NonNull
+ private RewardedExt rewardedExt = RewardedExt.defaultExt();
+
protected Bid() {
}
@@ -302,6 +306,7 @@ public static Bid fromJSONObject(JSONObject jsonObject) {
setEvents(bid, prebidObject);
bid.prebid = prebidObject;
bid.mobileSdkPassThrough = MobileSdkPassThrough.create(ext);
+ bid.rewardedExt = RewardedExtParser.parse(ext);
}
substituteMacros(bid);
@@ -372,4 +377,8 @@ private static void setEvents(Bid bid, Prebid prebidObject) {
}
}
+ @NonNull
+ public RewardedExt getRewardedExt() {
+ return rewardedExt;
+ }
}
diff --git a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/bidding/display/InterstitialController.java b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/bidding/display/InterstitialController.java
index e6522d76e..39b5a23c7 100644
--- a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/bidding/display/InterstitialController.java
+++ b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/bidding/display/InterstitialController.java
@@ -17,7 +17,7 @@
package org.prebid.mobile.rendering.bidding.display;
import android.content.Context;
-
+import androidx.annotation.Nullable;
import org.prebid.mobile.LogUtil;
import org.prebid.mobile.api.data.AdFormat;
import org.prebid.mobile.api.exceptions.AdException;
@@ -27,6 +27,7 @@
import org.prebid.mobile.rendering.bidding.data.bid.BidResponse;
import org.prebid.mobile.rendering.bidding.interfaces.InterstitialControllerListener;
import org.prebid.mobile.rendering.bidding.interfaces.InterstitialViewListener;
+import org.prebid.mobile.rendering.interstitial.rewarded.Reward;
import org.prebid.mobile.rendering.models.AdDetails;
import org.prebid.mobile.rendering.models.openrtb.bidRequests.MobileSdkPassThrough;
import org.prebid.mobile.rendering.networking.WinNotifier;
@@ -40,6 +41,8 @@ public class InterstitialController implements PrebidMobileInterstitialControlle
private final InterstitialView bidInterstitialView;
private InterstitialControllerListener listener;
private AdFormat adUnitIdentifierType;
+ private AdUnitConfiguration config;
+ private Runnable rewardListener;
private InterstitialViewListener interstitialViewListener = new InterstitialViewListener() {
@Override
@@ -94,6 +97,12 @@ public void onAdClosed(InterstitialView interstitialView) {
LogUtil.debug(TAG, "onAdClosed");
if (listener != null) {
listener.onInterstitialClosed();
+
+ if (config == null) return;
+ boolean userIsNotRewarded = !config.getRewardManager().getUserRewardedAlready();
+ if (userIsNotRewarded) {
+ listener.onUserEarnedReward();
+ }
}
}
};
@@ -109,6 +118,7 @@ public InterstitialController(Context context, InterstitialControllerListener li
}
public void loadAd(AdUnitConfiguration adUnitConfiguration, BidResponse bidResponse) {
+ config = adUnitConfiguration;
adUnitConfiguration.modifyUsingBidResponse(bidResponse);
setRenderingControlSettings(adUnitConfiguration, bidResponse);
WinNotifier winNotifier = new WinNotifier();
@@ -131,9 +141,12 @@ public void loadAd(String responseId, boolean isRewarded) {
}
return;
}
- AdUnitConfiguration adUnitConfiguration = new AdUnitConfiguration();
- adUnitConfiguration.setRewarded(isRewarded);
- loadAd(adUnitConfiguration, bidResponse);
+ config = new AdUnitConfiguration();
+ config.setRewarded(isRewarded);
+ if (isRewarded) {
+ config.getRewardManager().setRewardListener(rewardListener);
+ }
+ loadAd(config, bidResponse);
}
public void show() {
@@ -163,6 +176,17 @@ public void destroy() {
interstitialViewListener = null;
}
+ public void setRewardListener(Runnable rewardListener) {
+ this.rewardListener = rewardListener;
+ }
+
+ @Nullable
+ public Reward getReward() {
+ if (config == null) return null;
+
+ return config.getRewardManager().getRewardedExt().getReward();
+ }
+
private void setRenderingControlSettings(
AdUnitConfiguration adUnitConfiguration,
BidResponse bidResponse
diff --git a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/bidding/interfaces/InterstitialControllerListener.java b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/bidding/interfaces/InterstitialControllerListener.java
index 199cd84e1..46a857353 100644
--- a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/bidding/interfaces/InterstitialControllerListener.java
+++ b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/bidding/interfaces/InterstitialControllerListener.java
@@ -28,4 +28,8 @@ public interface InterstitialControllerListener {
void onInterstitialDisplayed();
void onInterstitialClosed();
+
+ default void onUserEarnedReward() {
+
+ }
}
diff --git a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/bidding/interfaces/RewardedEventHandler.java b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/bidding/interfaces/RewardedEventHandler.java
index 765fa96db..108c86e13 100644
--- a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/bidding/interfaces/RewardedEventHandler.java
+++ b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/bidding/interfaces/RewardedEventHandler.java
@@ -20,6 +20,7 @@
import androidx.annotation.Nullable;
import org.prebid.mobile.rendering.bidding.data.bid.Bid;
import org.prebid.mobile.rendering.bidding.listeners.RewardedVideoEventListener;
+import org.prebid.mobile.rendering.interstitial.rewarded.Reward;
public interface RewardedEventHandler {
void setRewardedEventListener(
@@ -34,4 +35,8 @@ void requestAdWithBid(@Nullable
void trackImpression();
void destroy();
+
+ default Reward getReward() {
+ return null;
+ }
}
diff --git a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/interstitial/AdInterstitialDialog.java b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/interstitial/AdInterstitialDialog.java
index 80578b8d5..8903492ce 100644
--- a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/interstitial/AdInterstitialDialog.java
+++ b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/interstitial/AdInterstitialDialog.java
@@ -17,20 +17,41 @@
package org.prebid.mobile.rendering.interstitial;
import android.annotation.SuppressLint;
+import android.app.Fragment;
+import android.app.FragmentManager;
import android.content.Context;
+import android.os.CountDownTimer;
+import android.os.Handler;
+import android.os.Looper;
import android.util.Log;
+import android.view.View;
import android.widget.FrameLayout;
import android.widget.RelativeLayout;
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
import org.prebid.mobile.LogUtil;
+import org.prebid.mobile.configuration.AdUnitConfiguration;
+import org.prebid.mobile.rendering.interstitial.rewarded.RewardManager;
+import org.prebid.mobile.rendering.interstitial.rewarded.RewardedClosingRules;
+import org.prebid.mobile.rendering.interstitial.rewarded.RewardedCompletionRules;
+import org.prebid.mobile.rendering.interstitial.rewarded.RewardedExt;
import org.prebid.mobile.rendering.views.interstitial.InterstitialManager;
import org.prebid.mobile.rendering.views.webview.WebViewBase;
import org.prebid.mobile.rendering.views.webview.mraid.JSInterface;
import org.prebid.mobile.rendering.views.webview.mraid.Views;
+import java.lang.ref.WeakReference;
+
@SuppressLint("NewApi")
public class AdInterstitialDialog extends AdBaseDialog {
+
private static final String TAG = AdInterstitialDialog.class.getSimpleName();
+ @Nullable
+ private RewardedCustomTimer timer;
+ @Nullable
+ private FragmentManager.FragmentLifecycleCallbacks lifecycleListener;
+
/**
* @param context activity context.
* @param webViewBaseLocal webview with ad.
@@ -48,7 +69,7 @@ public AdInterstitialDialog(Context context, WebViewBase webViewBaseLocal,
this.adViewContainer.setBackgroundColor(this.interstitialManager.getInterstitialDisplayProperties()
.getPubBackGroundOpacity());
}
-
+ setUpCloseButtonTask();
setListeners();
webViewBase.setDialog(this);
}
@@ -86,4 +107,192 @@ public void nullifyDialog() {
cancel();
cleanup();
}
+
+
+ @VisibleForTesting
+ protected void setUpCloseButtonTask() {
+ if (interstitialManager.getInterstitialDisplayProperties() == null || interstitialManager.getInterstitialDisplayProperties().config == null) {
+ return;
+ }
+
+ AdUnitConfiguration config = interstitialManager.getInterstitialDisplayProperties().config;
+ RewardManager rewardManager = config.getRewardManager();
+ if (config != null && config.isRewarded()) {
+ if (rewardManager.getUserRewardedAlready()) {
+ return;
+ }
+
+ setBackgroundListener();
+ changeCloseViewVisibility(View.GONE);
+
+ RewardedExt rewardedExt = rewardManager.getRewardedExt();
+ int defaultRewardTime = RewardedCompletionRules.DEFAULT_BANNER_TIME_MS;
+ int postRewardTime = rewardedExt.getClosingRules().getPostRewardTime() * 1000;
+
+ boolean autoClose = rewardedExt.getClosingRules().getAction() == RewardedClosingRules.Action.AUTO_CLOSE;
+ boolean isEndCard = config.getHasEndCard();
+ boolean hasRewardEventUrl = getBannerEvent(rewardedExt, isEndCard) != null;
+
+ if (hasRewardEventUrl) {
+ scheduleRewardListener(defaultRewardTime, 0, autoClose);
+ rewardManager.setAfterRewardListener(() -> scheduleCloseButtonDisplaying(postRewardTime, autoClose));
+ return;
+ }
+
+ int rewardTime = getBannerTime(rewardedExt, isEndCard) * 1000;
+ scheduleRewardListener(rewardTime, postRewardTime, autoClose);
+ }
+ }
+
+ protected void scheduleCloseButtonDisplaying(int closeButtonDelay, boolean autoClose) {
+ LogUtil.debug(TAG, "Scheduled close button displaying in " + closeButtonDelay + "ms with autoclose " + autoClose);
+ Handler handler = new Handler(Looper.getMainLooper());
+ handler.postDelayed(new DisplayCloseButtonRunnable(this, autoClose), closeButtonDelay);
+ destroyRewardedListeners();
+ }
+
+ protected void scheduleRewardListener(int rewardDelay, int afterRewardDelay, boolean autoClose) {
+ LogUtil.debug(TAG, "Scheduled reward in " + rewardDelay + "ms");
+
+ AdUnitConfiguration config = interstitialManager.getInterstitialDisplayProperties().config;
+ timer = new RewardedCustomTimer(rewardDelay, () -> {
+ new RewardRunnable(config).run();
+ scheduleCloseButtonDisplaying(afterRewardDelay, autoClose);
+ });
+ timer.start();
+ }
+
+ private void setBackgroundListener() {
+ try {
+ FragmentManager fragmentManager = getActivity().getFragmentManager();
+ if (fragmentManager == null) {
+ return;
+ }
+
+ lifecycleListener = createLifecycleListener();
+ fragmentManager.registerFragmentLifecycleCallbacks(lifecycleListener, true);
+ } catch (Throwable e) {
+ LogUtil.error(TAG, "Can't set up lifecycle listener for background rewarded tracking.");
+ }
+ }
+
+ @Nullable
+ private String getBannerEvent(RewardedExt rewardedExt, boolean isEndCard) {
+ if (isEndCard) {
+ return rewardedExt.getCompletionRules().getEndCardEvent();
+ }
+ return rewardedExt.getCompletionRules().getBannerEvent();
+ }
+
+ private int getBannerTime(RewardedExt rewardedExt, boolean isEndCard) {
+ if (isEndCard) {
+ return rewardedExt.getCompletionRules().getEndCardTime();
+ }
+ return rewardedExt.getCompletionRules().getBannerTime();
+ }
+
+ private void destroyRewardedListeners() {
+ FragmentManager fragmentManager = getActivity().getFragmentManager();
+ if (fragmentManager != null && lifecycleListener != null) {
+ fragmentManager.unregisterFragmentLifecycleCallbacks(lifecycleListener);
+ }
+
+ if (timer != null) {
+ timer = null;
+ }
+ }
+
+ private FragmentManager.FragmentLifecycleCallbacks createLifecycleListener() {
+ return new FragmentManager.FragmentLifecycleCallbacks() {
+ @Override
+ public void onFragmentStopped(FragmentManager fm, Fragment f) {
+ super.onFragmentStopped(fm, f);
+
+ if (timer != null) {
+ timer.cancel();
+ }
+ }
+
+ @Override
+ public void onFragmentStarted(FragmentManager fm, Fragment f) {
+ super.onFragmentStarted(fm, f);
+
+ if (timer != null) {
+ timer = timer.copy();
+ timer.start();
+ }
+ }
+ };
+ }
+
+ private static class RewardedCustomTimer extends CountDownTimer {
+
+ long lastTime = -1;
+ Runnable onFinish;
+
+ public RewardedCustomTimer(long millisInFuture, Runnable onFinish) {
+ super(millisInFuture, 1000);
+ this.onFinish = onFinish;
+
+ LogUtil.debug("RewardedCustomTimer", "Created new timer with " + millisInFuture);
+ }
+
+ @Override
+ public void onTick(long millisUntilFinished) {
+ lastTime = millisUntilFinished;
+ }
+
+ @Override
+ public void onFinish() {
+ onFinish.run();
+ }
+
+
+ public RewardedCustomTimer copy() {
+ return new RewardedCustomTimer(lastTime, onFinish);
+ }
+
+ }
+
+ private static class DisplayCloseButtonRunnable implements Runnable {
+
+ private final boolean autoClose;
+ private final WeakReference dialogReference;
+
+ public DisplayCloseButtonRunnable(AdBaseDialog dialog, boolean autoClose) {
+ this.dialogReference = new WeakReference<>(dialog);
+ this.autoClose = autoClose;
+ }
+
+ @Override
+ public void run() {
+ AdBaseDialog dialog = dialogReference.get();
+ if (dialog == null) return;
+
+ dialog.changeCloseViewVisibility(View.VISIBLE);
+ if (autoClose) {
+ dialog.handleCloseClick();
+ }
+ }
+ }
+
+ private static class RewardRunnable implements Runnable {
+
+ private final WeakReference configReference;
+
+ public RewardRunnable(AdUnitConfiguration config) {
+ this.configReference = new WeakReference<>(config);
+ }
+
+ @Override
+ public void run() {
+ AdUnitConfiguration config = configReference.get();
+ if (config == null) return;
+
+ config.getRewardManager().notifyRewardListener();
+ }
+ }
+
+
+
}
diff --git a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/interstitial/InterstitialLayoutConfigurator.java b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/interstitial/InterstitialLayoutConfigurator.java
index ba224c794..1ff7b296f 100644
--- a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/interstitial/InterstitialLayoutConfigurator.java
+++ b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/interstitial/InterstitialLayoutConfigurator.java
@@ -45,5 +45,7 @@ public static void configureDisplayProperties(AdUnitConfiguration adConfiguratio
displayProperties.skipDelay = adConfiguration.getSkipDelay();
displayProperties.skipButtonArea = adConfiguration.getSkipButtonArea();
displayProperties.skipButtonPosition = adConfiguration.getSkipButtonPosition();
+
+ displayProperties.config = adConfiguration;
}
}
diff --git a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/interstitial/rewarded/Reward.java b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/interstitial/rewarded/Reward.java
new file mode 100644
index 000000000..1e2340550
--- /dev/null
+++ b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/interstitial/rewarded/Reward.java
@@ -0,0 +1,71 @@
+/*
+ * © 2024 SMARTYADS. LDA doing business as “TEQBLAZE”.
+ * All rights reserved. You may not use this file except in compliance with the applicable license granted to
+ * you by SMARTYADS, LDA doing business as “TEQBLAZE” (the "License"). Unless required by applicable law or
+ * agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Specific authorizations and restrictions
+ * shall be provided for in the License.
+ */
+
+package org.prebid.mobile.rendering.interstitial.rewarded;
+
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import org.json.JSONObject;
+
+import java.util.Objects;
+
+/**
+ * Reward for rewarded ad.
+ * Bid response JSON object: {@code seatbid.bid[].ext.rwdd.reward}.
+ */
+public class Reward {
+
+ private int count = 0;
+
+ @NonNull
+ private String type;
+
+ @Nullable
+ private JSONObject ext;
+
+ public Reward(@NonNull String type, int count, @Nullable JSONObject ext) {
+ this.type = type;
+ this.count = count;
+ this.ext = ext;
+ }
+
+ @NonNull
+ public String getType() {
+ return type;
+ }
+
+ public int getCount() {
+ return count;
+ }
+
+ @Nullable
+ public JSONObject getExt() {
+ return ext;
+ }
+
+ @Override
+ public String toString() {
+ return "Reward {" + "count=" + count + ", type='" + type + '\'' + ", ext=" + ext + '}';
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ Reward reward = (Reward) o;
+ return count == reward.count && Objects.equals(type, reward.type) && Objects.equals(ext, reward.ext);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(count, type, ext);
+ }
+
+}
diff --git a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/interstitial/rewarded/RewardManager.java b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/interstitial/rewarded/RewardManager.java
new file mode 100644
index 000000000..9d70e7e77
--- /dev/null
+++ b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/interstitial/rewarded/RewardManager.java
@@ -0,0 +1,78 @@
+/*
+ * © 2024 SMARTYADS. LDA doing business as “TEQBLAZE”.
+ * All rights reserved. You may not use this file except in compliance with the applicable license granted to
+ * you by SMARTYADS, LDA doing business as “TEQBLAZE” (the "License"). Unless required by applicable law or
+ * agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Specific authorizations and restrictions
+ * shall be provided for in the License.
+ */
+
+package org.prebid.mobile.rendering.interstitial.rewarded;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+public class RewardManager {
+
+ private boolean userRewardedAlready = false;
+
+ @Nullable
+ private Runnable rewardListener;
+ @Nullable
+ private Runnable afterRewardListener;
+ @NonNull
+ private RewardedExt rewardedExt = RewardedExt.defaultExt();
+
+ public void notifyRewardListener() {
+ if (rewardListener != null && !userRewardedAlready) {
+ userRewardedAlready = true;
+ rewardListener.run();
+ if (afterRewardListener != null) {
+ afterRewardListener.run();
+ afterRewardListener = null;
+ }
+ }
+ }
+
+ public void clear() {
+ rewardListener = null;
+ afterRewardListener = null;
+ userRewardedAlready = false;
+ rewardedExt = RewardedExt.defaultExt();
+ }
+
+ public boolean getUserRewardedAlready() {
+ return userRewardedAlready;
+ }
+
+ public void setUserRewardedAlready(boolean userRewardedAlready) {
+ this.userRewardedAlready = userRewardedAlready;
+ }
+
+ @Nullable
+ public Runnable getRewardListener() {
+ return rewardListener;
+ }
+
+ public void setRewardListener(@Nullable Runnable rewardListener) {
+ this.rewardListener = rewardListener;
+ }
+
+ @Nullable
+ public Runnable getAfterRewardListener() {
+ return afterRewardListener;
+ }
+
+ public void setAfterRewardListener(@Nullable Runnable afterRewardListener) {
+ this.afterRewardListener = afterRewardListener;
+ }
+
+ @NonNull
+ public RewardedExt getRewardedExt() {
+ return rewardedExt;
+ }
+
+ public void setRewardedExt(@NonNull RewardedExt rewardedExt) {
+ this.rewardedExt = rewardedExt;
+ }
+}
diff --git a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/interstitial/rewarded/RewardedClosingRules.java b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/interstitial/rewarded/RewardedClosingRules.java
new file mode 100644
index 000000000..e42d78f72
--- /dev/null
+++ b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/interstitial/rewarded/RewardedClosingRules.java
@@ -0,0 +1,47 @@
+/*
+ * © 2024 SMARTYADS. LDA doing business as “TEQBLAZE”.
+ * All rights reserved. You may not use this file except in compliance with the applicable license granted to
+ * you by SMARTYADS, LDA doing business as “TEQBLAZE” (the "License"). Unless required by applicable law or
+ * agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Specific authorizations and restrictions
+ * shall be provided for in the License.
+ */
+
+package org.prebid.mobile.rendering.interstitial.rewarded;
+
+import androidx.annotation.Nullable;
+
+/**
+ * Additional rules for closing rewarded ad.
+ * Bid response JSON object: {@code seatbid.bid[].ext.rwdd.close}.
+ */
+public class RewardedClosingRules {
+
+ private int postRewardTime = 0;
+ private Action action = Action.CLOSE_BUTTON;
+
+ public RewardedClosingRules() {
+ }
+
+ public RewardedClosingRules(@Nullable Integer postRewardTime, @Nullable Action action) {
+ if (postRewardTime != null) {
+ this.postRewardTime = postRewardTime;
+ }
+ if (action != null) {
+ this.action = action;
+ }
+ }
+
+ public int getPostRewardTime() {
+ return postRewardTime;
+ }
+
+ public Action getAction() {
+ return action;
+ }
+
+ public enum Action {
+ AUTO_CLOSE, CLOSE_BUTTON
+ }
+
+}
diff --git a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/interstitial/rewarded/RewardedCompletionRules.java b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/interstitial/rewarded/RewardedCompletionRules.java
new file mode 100644
index 000000000..97da42170
--- /dev/null
+++ b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/interstitial/rewarded/RewardedCompletionRules.java
@@ -0,0 +1,129 @@
+/*
+ * © 2024 SMARTYADS. LDA doing business as “TEQBLAZE”.
+ * All rights reserved. You may not use this file except in compliance with the applicable license granted to
+ * you by SMARTYADS, LDA doing business as “TEQBLAZE” (the "License"). Unless required by applicable law or
+ * agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Specific authorizations and restrictions
+ * shall be provided for in the License.
+ */
+
+package org.prebid.mobile.rendering.interstitial.rewarded;
+
+import androidx.annotation.Nullable;
+
+/**
+ * Rules for completion rewarded ad.
+ * Bid response JSON object: {@code seatbid.bid[].ext.rwdd.completion}.
+ */
+public class RewardedCompletionRules {
+
+ public static final int DEFAULT_BANNER_TIME_MS = 120_000;
+
+ public RewardedCompletionRules() {
+ }
+
+ public RewardedCompletionRules(
+ @Nullable Integer bannerTime,
+ @Nullable Integer videoTime,
+ @Nullable Integer endCardTime,
+ @Nullable String bannerEvent,
+ @Nullable PlaybackEvent videoEvent,
+ @Nullable String endCardEvent
+ ) {
+ if (bannerTime != null) {
+ this.bannerTime = bannerTime;
+ }
+ if (endCardTime != null) {
+ this.endCardTime = endCardTime;
+ }
+ this.videoEvent = videoEvent;
+ this.videoTime = videoTime;
+ this.bannerEvent = bannerEvent;
+ this.endCardEvent = endCardEvent;
+ }
+
+ private int bannerTime = DEFAULT_BANNER_TIME_MS / 1000;
+ private int endCardTime = DEFAULT_BANNER_TIME_MS / 1000;
+ @Nullable
+ private Integer videoTime;
+
+ @Nullable
+ private String bannerEvent;
+ @Nullable
+ private PlaybackEvent videoEvent;
+ @Nullable
+ private String endCardEvent;
+
+ public int getBannerTime() {
+ return bannerTime;
+ }
+
+ @Nullable
+ public Integer getVideoTime() {
+ return videoTime;
+ }
+
+ public int getEndCardTime() {
+ return endCardTime;
+ }
+
+ @Nullable
+ public String getBannerEvent() {
+ return bannerEvent;
+ }
+
+ @Nullable
+ public PlaybackEvent getVideoEvent() {
+ return videoEvent;
+ }
+
+ @Nullable
+ public String getEndCardEvent() {
+ return endCardEvent;
+ }
+
+ public static PlaybackEvent getDefaultPlaybackEvent() {
+ return PlaybackEvent.COMPLETE;
+ }
+
+ public enum PlaybackEvent {
+ START, FIRST_QUARTILE, MIDPOINT, THIRD_QUARTILE, COMPLETE;
+
+ @Nullable
+ public static PlaybackEvent fromString(String playbackEventString) {
+ if (playbackEventString.isEmpty()) return null;
+
+ if (playbackEventString.equalsIgnoreCase("start")) {
+ return START;
+ } else if (playbackEventString.equalsIgnoreCase("firstquartile")) {
+ return FIRST_QUARTILE;
+ } else if (playbackEventString.equalsIgnoreCase("midpoint")) {
+ return MIDPOINT;
+ } else if (playbackEventString.equalsIgnoreCase("thirdquartile")) {
+ return THIRD_QUARTILE;
+ } else if (playbackEventString.equalsIgnoreCase("complete")) {
+ return COMPLETE;
+ }
+
+ return null;
+ }
+
+ public long getCompletionTime(long duration) {
+ if (this == START) {
+ return 0;
+ } else if (this == FIRST_QUARTILE) {
+ return (int) (duration * 0.25);
+ } else if (this == MIDPOINT) {
+ return (int) (duration * 0.50);
+ } else if (this == THIRD_QUARTILE) {
+ return (int) (duration * 0.75);
+ } else if (this == COMPLETE) {
+ return duration;
+ }
+
+ return 0;
+ }
+
+ }
+
+}
diff --git a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/interstitial/rewarded/RewardedExt.java b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/interstitial/rewarded/RewardedExt.java
new file mode 100644
index 000000000..9720d63f1
--- /dev/null
+++ b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/interstitial/rewarded/RewardedExt.java
@@ -0,0 +1,57 @@
+/*
+ * © 2024 SMARTYADS. LDA doing business as “TEQBLAZE”.
+ * All rights reserved. You may not use this file except in compliance with the applicable license granted to
+ * you by SMARTYADS, LDA doing business as “TEQBLAZE” (the "License"). Unless required by applicable law or
+ * agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Specific authorizations and restrictions
+ * shall be provided for in the License.
+ */
+
+package org.prebid.mobile.rendering.interstitial.rewarded;
+
+import androidx.annotation.Nullable;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Represents rewarded ext object. Bid response JSON object: {@code seatbid.bid[].ext.rwdd}.
+ * It's responsible for the reward and the completion rules.
+ */
+public class RewardedExt {
+
+ @Nullable
+ private final Reward reward;
+ @NotNull
+ private final RewardedCompletionRules completionRules;
+ @NotNull
+ private final RewardedClosingRules closingRules;
+
+ public RewardedExt(
+ @Nullable Reward reward,
+ @NotNull RewardedCompletionRules completionRules,
+ @NotNull RewardedClosingRules closingData
+ ) {
+ this.reward = reward;
+ this.completionRules = completionRules;
+ this.closingRules = closingData;
+ }
+
+ @Nullable
+ public Reward getReward() {
+ return reward;
+ }
+
+ @NotNull
+ public RewardedCompletionRules getCompletionRules() {
+ return completionRules;
+ }
+
+ @NotNull
+ public RewardedClosingRules getClosingRules() {
+ return closingRules;
+ }
+
+ public static RewardedExt defaultExt() {
+ return new RewardedExt(null, new RewardedCompletionRules(), new RewardedClosingRules());
+ }
+
+}
diff --git a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/interstitial/rewarded/RewardedExtParser.java b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/interstitial/rewarded/RewardedExtParser.java
new file mode 100644
index 000000000..4767ade99
--- /dev/null
+++ b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/interstitial/rewarded/RewardedExtParser.java
@@ -0,0 +1,165 @@
+/*
+ * © 2024 SMARTYADS. LDA doing business as “TEQBLAZE”.
+ * All rights reserved. You may not use this file except in compliance with the applicable license granted to
+ * you by SMARTYADS, LDA doing business as “TEQBLAZE” (the "License"). Unless required by applicable law or
+ * agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Specific authorizations and restrictions
+ * shall be provided for in the License.
+ */
+
+package org.prebid.mobile.rendering.interstitial.rewarded;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import org.json.JSONArray;
+import org.json.JSONObject;
+import org.prebid.mobile.LogUtil;
+
+
+/**
+ * Parser for rewarded ext JSON object. {@link RewardedExt}
+ */
+public class RewardedExtParser {
+
+ private static final String TAG = "RewardedExtParser";
+
+ private RewardedExtParser() {
+ }
+
+ @NonNull
+ public static RewardedExt parse(@Nullable JSONObject bidExtJson) {
+ JSONObject rootRewardedJson = getRootRewardedJson(bidExtJson);
+ if (rootRewardedJson == null) return RewardedExt.defaultExt();
+
+ JSONObject rewardJson = rootRewardedJson.optJSONObject("reward");
+ Reward reward = parseReward(rewardJson);
+
+ JSONObject completionJson = rootRewardedJson.optJSONObject("completion");
+ RewardedCompletionRules completionRules = parseCompletionRules(completionJson);
+
+ JSONObject closingJson = rootRewardedJson.optJSONObject("close");
+ RewardedClosingRules closingRules = parseClosingRules(closingJson);
+
+ return new RewardedExt(reward, completionRules, closingRules);
+ }
+
+ @Nullable
+ private static JSONObject getRootRewardedJson(@Nullable JSONObject bidExtJson) {
+ if (bidExtJson == null) return null;
+
+ JSONObject prebidJson = bidExtJson.optJSONObject("prebid");
+ if (prebidJson == null) return null;
+
+ JSONArray passThroughArray = prebidJson.optJSONArray("passthrough");
+ if (passThroughArray == null || passThroughArray.length() < 1) return null;
+
+ for (int i = 0; i < passThroughArray.length(); i++) {
+ JSONObject passThroughObject = passThroughArray.optJSONObject(i);
+ if (passThroughObject == null) continue;
+
+ String type = passThroughObject.optString("type");
+ if (type.equals("prebidmobilesdk")) {
+ return passThroughObject.optJSONObject("rwdd");
+ }
+ }
+ return null;
+ }
+
+
+ @Nullable
+ private static Reward parseReward(@Nullable JSONObject rewardJson) {
+ if (rewardJson == null) {
+ LogUtil.warning(TAG, "No 'reward' object for the rewarded ad.");
+ return null;
+ }
+
+ String type = rewardJson.optString("type");
+ int count = rewardJson.optInt("count", -1);
+
+ if (type.isEmpty() || count < 0) {
+ LogUtil.warning(TAG, "No required fields (type, count) in `reward` object for the rewarded ad.");
+ return null;
+ }
+
+ return new Reward(type, count, rewardJson.optJSONObject("ext"));
+ }
+
+ @NonNull
+ private static RewardedCompletionRules parseCompletionRules(@Nullable JSONObject completionJson) {
+ if (completionJson == null) return new RewardedCompletionRules();
+
+ Integer bannerTime = null;
+ Integer videoTime = null;
+ Integer endCardTime = null;
+ String bannerEvent = null;
+ RewardedCompletionRules.PlaybackEvent videoEvent = null;
+ String endCardEvent = null;
+
+ JSONObject bannerJson = completionJson.optJSONObject("banner");
+ if (bannerJson != null) {
+ int time = bannerJson.optInt("time", -1);
+ if (time > -1) {
+ bannerTime = time;
+ }
+
+ String event = bannerJson.optString("event");
+ if (!event.isEmpty()) {
+ bannerEvent = event;
+ }
+ }
+
+ JSONObject videoJson = completionJson.optJSONObject("video");
+ if (videoJson != null) {
+ int time = videoJson.optInt("time", -1);
+ if (time > -1) {
+ videoTime = time;
+ }
+
+ String event = videoJson.optString("playbackevent");
+ if (!event.isEmpty()) {
+ videoEvent = RewardedCompletionRules.PlaybackEvent.fromString(event);
+ }
+
+ JSONObject endCardJson = videoJson.optJSONObject("endcard");
+ if (endCardJson != null) {
+ int cardTime = endCardJson.optInt("time", -1);
+ if (cardTime > -1) {
+ endCardTime = cardTime;
+ }
+
+ String cardEvent = endCardJson.optString("event");
+ if (!cardEvent.isEmpty()) {
+ endCardEvent = cardEvent;
+ }
+ }
+ }
+
+ return new RewardedCompletionRules(bannerTime, videoTime, endCardTime, bannerEvent, videoEvent, endCardEvent);
+ }
+
+ @NonNull
+ private static RewardedClosingRules parseClosingRules(@Nullable JSONObject closingJson) {
+ if (closingJson == null) return new RewardedClosingRules();
+
+ Integer postRewardTime = null;
+ RewardedClosingRules.Action action = null;
+
+ int time = closingJson.optInt("postrewardtime", -1);
+ if (time > -1) {
+ postRewardTime = time;
+ }
+
+ String actionString = closingJson.optString("action");
+ if (!actionString.isEmpty()) {
+ if (actionString.equals("closebutton")) {
+ action = RewardedClosingRules.Action.CLOSE_BUTTON;
+ }
+ if (actionString.equals("autoclose")) {
+ action = RewardedClosingRules.Action.AUTO_CLOSE;
+ }
+ }
+
+ return new RewardedClosingRules(postRewardTime, action);
+ }
+
+}
diff --git a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/models/CreativeModelMakerBids.java b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/models/CreativeModelMakerBids.java
index e88485a7e..34399c5a1 100644
--- a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/models/CreativeModelMakerBids.java
+++ b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/models/CreativeModelMakerBids.java
@@ -18,9 +18,7 @@
import android.content.Context;
import android.text.TextUtils;
-
import androidx.annotation.NonNull;
-
import org.prebid.mobile.LogUtil;
import org.prebid.mobile.api.data.AdFormat;
import org.prebid.mobile.api.exceptions.AdException;
@@ -79,6 +77,10 @@ public void makeModels(
return;
}
+ if (adConfiguration.isRewarded()) {
+ adConfiguration.getRewardManager().setRewardedExt(winningBid.getRewardedExt());
+ }
+
if (bidResponse.isVideo()) {
makeVideoModels(adConfiguration, winningBid.getAdm());
} else {
diff --git a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/models/CreativeModelsMakerVast.java b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/models/CreativeModelsMakerVast.java
index 87180fab2..cad0b910a 100644
--- a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/models/CreativeModelsMakerVast.java
+++ b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/models/CreativeModelsMakerVast.java
@@ -205,13 +205,23 @@ private void makeModelsContinued() {
endCardModel.setWidth(Integer.parseInt(companionAd.getWidth()));
endCardModel.setHeight(Integer.parseInt(companionAd.getHeight()));
- endCardModel.setAdConfiguration(new AdUnitConfiguration());
- endCardModel.getAdConfiguration().setAdFormat(AdFormat.INTERSTITIAL);
+
+
+ AdUnitConfiguration endCardConfig = new AdUnitConfiguration();
+ endCardConfig.setRewardManager(adConfiguration.getRewardManager());
+ endCardConfig.setAdFormat(AdFormat.INTERSTITIAL);
+ endCardConfig.setRewarded(adConfiguration.isRewarded());
+ endCardConfig.getRewardManager().setRewardedExt(adConfiguration.getRewardManager().getRewardedExt());
+ endCardConfig.setHasEndCard(true);
+ endCardModel.setAdConfiguration(endCardConfig);
+
+
endCardModel.setRequireImpressionUrl(false);
result.creativeModels.add(endCardModel);
// Flag that video creative has a corresponding end card
videoModel.setHasEndCard(true);
+ adConfiguration.setHasEndCard(true);
}
adConfiguration.setInterstitialSize(videoModel.getWidth() + "x" + videoModel.getHeight());
listener.onCreativeModelReady(result);
diff --git a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/models/HTMLCreative.java b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/models/HTMLCreative.java
index 1f04c2908..e38318b37 100644
--- a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/models/HTMLCreative.java
+++ b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/models/HTMLCreative.java
@@ -27,6 +27,7 @@
import org.prebid.mobile.api.exceptions.AdException;
import org.prebid.mobile.configuration.AdUnitConfiguration;
import org.prebid.mobile.rendering.interstitial.InterstitialManagerDisplayDelegate;
+import org.prebid.mobile.rendering.interstitial.rewarded.RewardedExt;
import org.prebid.mobile.rendering.listeners.CreativeViewListener;
import org.prebid.mobile.rendering.listeners.WebViewDelegate;
import org.prebid.mobile.rendering.models.internal.MraidEvent;
@@ -37,10 +38,7 @@
import org.prebid.mobile.rendering.session.manager.OmAdSessionManager;
import org.prebid.mobile.rendering.utils.exposure.ViewExposure;
import org.prebid.mobile.rendering.views.interstitial.InterstitialManager;
-import org.prebid.mobile.rendering.views.webview.PrebidWebViewBanner;
-import org.prebid.mobile.rendering.views.webview.PrebidWebViewBase;
-import org.prebid.mobile.rendering.views.webview.PrebidWebViewInterstitial;
-import org.prebid.mobile.rendering.views.webview.WebViewBase;
+import org.prebid.mobile.rendering.views.webview.*;
import java.util.EnumSet;
@@ -132,6 +130,7 @@ public void load() throws AdException {
html = injectingScriptContent(html);
prebidWebView.loadHTML(html, width, height);
setCreativeView(prebidWebView);
+ rewardedTracking(prebidWebView, getCreativeModel().getAdConfiguration());
}
isEndCard = model.hasEndCard();
@@ -334,6 +333,26 @@ public void handleMRAIDEventsInCreative(final MraidEvent mraidEvent, final WebVi
mraidController.handleMraidEvent(mraidEvent, this, oldWebViewBase, twoPartNewWebViewBase);
}
+
+ protected static void rewardedTracking(PrebidWebViewBase webView, AdUnitConfiguration config) {
+ if (!config.isRewarded()) {
+ return;
+ }
+
+ RewardedExt rewardedExt = config.getRewardManager().getRewardedExt();
+ String bannerEvent;
+ if (config.getHasEndCard()) {
+ bannerEvent = rewardedExt.getCompletionRules().getEndCardEvent();
+ } else {
+ bannerEvent = rewardedExt.getCompletionRules().getBannerEvent();
+ }
+ if (bannerEvent == null) {
+ return;
+ }
+
+ webView.setActionUrl(new ActionUrl(bannerEvent, () -> config.getRewardManager().notifyRewardListener()));
+ }
+
/**
* Injects OM script content into HTML
*
diff --git a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/models/InterstitialDisplayPropertiesInternal.java b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/models/InterstitialDisplayPropertiesInternal.java
index e747d282c..03db74ee9 100644
--- a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/models/InterstitialDisplayPropertiesInternal.java
+++ b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/models/InterstitialDisplayPropertiesInternal.java
@@ -18,6 +18,7 @@
import android.content.pm.ActivityInfo;
import org.prebid.mobile.api.data.Position;
+import org.prebid.mobile.configuration.AdUnitConfiguration;
public class InterstitialDisplayPropertiesInternal extends InterstitialDisplayPropertiesPublic {
@@ -34,6 +35,7 @@ public class InterstitialDisplayPropertiesInternal extends InterstitialDisplayPr
public Position closeButtonPosition = Position.TOP_RIGHT;
public Position skipButtonPosition = Position.TOP_RIGHT;
+ public AdUnitConfiguration config;
public void resetExpandValues() {
expandHeight = 0;
diff --git a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/models/openrtb/bidRequests/Imp.java b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/models/openrtb/bidRequests/Imp.java
index ed9355ba2..3c016b3ee 100644
--- a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/models/openrtb/bidRequests/Imp.java
+++ b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/models/openrtb/bidRequests/Imp.java
@@ -27,6 +27,7 @@ public class Imp extends BaseBid {
public String displaymanager = null;
public String displaymanagerver = null;
public Integer instl = null;
+ public Integer rewarded = null;
public String tagid = null;
public Integer secure = null;
public Banner banner = null;
@@ -46,6 +47,7 @@ public JSONObject getJsonObject() throws JSONException {
toJSON(jsonObject, "displaymanagerver", displaymanagerver);
toJSON(jsonObject, "instl", instl);
toJSON(jsonObject, "tagid", tagid);
+ toJSON(jsonObject, "rwdd", rewarded);
toJSON(jsonObject, "clickbrowser", clickBrowser);
toJSON(jsonObject, "secure", secure);
diff --git a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/networking/parameters/BasicParameterBuilder.java b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/networking/parameters/BasicParameterBuilder.java
index 84ce89725..1af1bd041 100644
--- a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/networking/parameters/BasicParameterBuilder.java
+++ b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/networking/parameters/BasicParameterBuilder.java
@@ -16,26 +16,15 @@
package org.prebid.mobile.rendering.networking.parameters;
-import static org.prebid.mobile.PrebidMobile.SDK_VERSION;
-
import android.content.res.Configuration;
import android.content.res.Resources;
import android.text.TextUtils;
import android.util.Pair;
-
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-
import org.json.JSONArray;
import org.json.JSONObject;
-import org.prebid.mobile.AdSize;
-import org.prebid.mobile.BannerParameters;
-import org.prebid.mobile.DataObject;
-import org.prebid.mobile.ExternalUserId;
-import org.prebid.mobile.PrebidMobile;
-import org.prebid.mobile.Signals;
-import org.prebid.mobile.TargetingParams;
-import org.prebid.mobile.VideoParameters;
+import org.prebid.mobile.*;
import org.prebid.mobile.api.data.AdFormat;
import org.prebid.mobile.configuration.AdUnitConfiguration;
import org.prebid.mobile.rendering.bidding.data.bid.Prebid;
@@ -50,13 +39,9 @@
import org.prebid.mobile.rendering.session.manager.OmAdSessionManager;
import org.prebid.mobile.rendering.utils.helpers.Utils;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.UUID;
+import java.util.*;
+
+import static org.prebid.mobile.PrebidMobile.SDK_VERSION;
public class BasicParameterBuilder extends ParameterBuilder {
@@ -386,6 +371,9 @@ private void setCommonImpValues(Imp imp, String uuid) {
if (!adConfiguration.isAdType(AdFormat.VAST)) {
imp.secure = 1;
}
+ if (adConfiguration.isRewarded()) {
+ imp.rewarded = 1;
+ }
imp.getExt().put("prebid", Prebid.getJsonObjectForImp(adConfiguration));
String gpid = adConfiguration.getGpid();
diff --git a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/video/AdViewProgressUpdateTask.java b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/video/AdViewProgressUpdateTask.java
index 730c924d1..81d89e658 100644
--- a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/video/AdViewProgressUpdateTask.java
+++ b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/video/AdViewProgressUpdateTask.java
@@ -21,8 +21,13 @@
import android.os.Looper;
import android.util.Log;
import android.view.View;
+import androidx.annotation.Nullable;
import org.prebid.mobile.LogUtil;
import org.prebid.mobile.api.exceptions.AdException;
+import org.prebid.mobile.configuration.AdUnitConfiguration;
+import org.prebid.mobile.rendering.interstitial.rewarded.RewardedClosingRules;
+import org.prebid.mobile.rendering.interstitial.rewarded.RewardedCompletionRules;
+import org.prebid.mobile.rendering.interstitial.rewarded.RewardedExt;
import org.prebid.mobile.rendering.listeners.VideoCreativeViewListener;
import org.prebid.mobile.rendering.models.AbstractCreative;
@@ -36,26 +41,35 @@ public class AdViewProgressUpdateTask extends AsyncTask {
private static String TAG = AdViewProgressUpdateTask.class.getSimpleName();
private long current = 0;
private WeakReference creativeViewWeakReference;
- private long duration;
+ private long videoDuration;
private VideoCreativeViewListener trackEventListener;
- private boolean firstQuartile, midpoint, thirdQuartile;
+ private boolean start, firstQuartile, midpoint, thirdQuartile;
private long vastVideoDuration = -1;
private Handler mainHandler;
+ private AdUnitConfiguration config;
+ @Nullable
+ private Integer percentageForReward;
+ @Nullable
+ private Integer autoCloseTime;
private long lastTime;
public AdViewProgressUpdateTask(
VideoCreativeViewListener trackEventListener,
- int duration
+ int videoDuration,
+ AdUnitConfiguration config
) throws AdException {
if (trackEventListener == null) {
throw new AdException(AdException.INTERNAL_ERROR, "VideoViewListener is null");
}
+ this.config = config;
this.trackEventListener = trackEventListener;
AbstractCreative creative = (AbstractCreative) trackEventListener;
creativeViewWeakReference = new WeakReference<>(creative.getCreativeView());
- this.duration = duration;
+ this.videoDuration = videoDuration;
mainHandler = new Handler(Looper.getMainLooper());
+ percentageForReward = getVideoLengthPercentageForReward(videoDuration, config);
+ autoCloseTime = getAutoCloseTime();
}
@Override
@@ -87,13 +101,18 @@ protected Void doInBackground(Void... params) {
if (vastVideoDuration != -1 && newCurrent >= vastVideoDuration) {
LogUtil.debug(
VideoCreativeView.class.getName(),
- "VAST duration reached, video interrupted. VAST duration:" + vastVideoDuration + " ms, Video duration: " + duration + " ms"
+ "VAST duration reached, video interrupted. VAST duration:" + vastVideoDuration + " ms, Video duration: " + videoDuration + " ms"
);
videoView.forceStop();
}
+ if (autoCloseTime != null && newCurrent >= autoCloseTime) {
+ LogUtil.debug("Auto close time reached. Auto close time: " + autoCloseTime + " ms");
+ videoView.forceStop();
+ }
+
if (newCurrent == 0 && current > 0) {
- current = duration;
+ current = videoDuration;
} else {
current = newCurrent;
}
@@ -106,10 +125,10 @@ protected Void doInBackground(Void... params) {
}
try {
- if (duration > 0) {
- publishProgress((current * 100 / duration), duration);
+ if (videoDuration > 0) {
+ publishProgress((current * 100 / videoDuration), videoDuration);
}
- if (current >= duration) {
+ if (current >= videoDuration) {
break;
}
} catch (Exception e) {
@@ -118,7 +137,7 @@ protected Void doInBackground(Void... params) {
}
lastTime = System.currentTimeMillis();
}
- } while (current <= duration && !isCancelled());
+ } while (current <= videoDuration && !isCancelled());
}
catch (Exception e) {
LogUtil.error(TAG, "Failed to update video progress: " + Log.getStackTraceString(e));
@@ -139,23 +158,30 @@ protected void onProgressUpdate(Long... values) {
return;
}
super.onProgressUpdate(values);
- // PbLog.debug(TAG, "progress: " + values[0]);
- //TODO - uncomment when we have to show the countdown on video
- //trackEventListener.countdown(values[1]);
+ Long completionPercentage = values[0];
+
+ if (percentageForReward != null && completionPercentage >= percentageForReward) {
+ config.getRewardManager().notifyRewardListener();
+ percentageForReward = null;
+ }
+
+ if (!start && completionPercentage >= 1) {
+ start = true;
+ }
- if (!firstQuartile && values[0] >= 25) {
- LogUtil.debug(TAG, "firstQuartile: " + values[0]);
+ if (!firstQuartile && completionPercentage >= 25) {
+ LogUtil.debug(TAG, "firstQuartile: " + completionPercentage);
firstQuartile = true;
trackEventListener.onEvent(VideoAdEvent.Event.AD_FIRSTQUARTILE);
}
- if (!midpoint && values[0] >= 50) {
- LogUtil.debug(TAG, "midpoint: " + values[0]);
+ if (!midpoint && completionPercentage >= 50) {
+ LogUtil.debug(TAG, "midpoint: " + completionPercentage);
midpoint = true;
trackEventListener.onEvent(VideoAdEvent.Event.AD_MIDPOINT);
}
- if (!thirdQuartile && values[0] >= 75) {
- LogUtil.debug(TAG, "thirdQuartile: " + values[0]);
+ if (!thirdQuartile && completionPercentage >= 75) {
+ LogUtil.debug(TAG, "thirdQuartile: " + completionPercentage);
thirdQuartile = true;
trackEventListener.onEvent(VideoAdEvent.Event.AD_THIRDQUARTILE);
}
@@ -180,4 +206,70 @@ public boolean getThirdQuartile() {
public void setVastVideoDuration(long vastVideoDuration) {
this.vastVideoDuration = vastVideoDuration;
}
+
+
+ /**
+ * Returns video length percentage for receiving reward.
+ */
+ @Nullable
+ protected static Integer getVideoLengthPercentageForReward(int videoDuration, AdUnitConfiguration config) {
+ boolean hasEndCard = !config.isRewarded() || config.getHasEndCard();
+ if (hasEndCard) {
+ return null;
+ }
+
+ RewardedExt rewardedExt = config.getRewardManager().getRewardedExt();
+ RewardedCompletionRules.PlaybackEvent playbackEvent = rewardedExt.getCompletionRules().getVideoEvent();
+ if (playbackEvent != null) {
+ if (playbackEvent == RewardedCompletionRules.PlaybackEvent.COMPLETE) {
+ return 100;
+ } else if (playbackEvent == RewardedCompletionRules.PlaybackEvent.THIRD_QUARTILE) {
+ return 75;
+ } else if (playbackEvent == RewardedCompletionRules.PlaybackEvent.MIDPOINT) {
+ return 50;
+ } else if (playbackEvent == RewardedCompletionRules.PlaybackEvent.FIRST_QUARTILE) {
+ return 25;
+ } else if (playbackEvent == RewardedCompletionRules.PlaybackEvent.START) {
+ return 1;
+ }
+ }
+
+ Integer secondsToReward = rewardedExt.getCompletionRules().getVideoTime();
+ if (secondsToReward != null && videoDuration != 0) {
+ int percentage = (int) (secondsToReward * 1000 / ((double) videoDuration) * 100);
+ if (percentage > 100 || percentage < 0) {
+ percentage = 100;
+ }
+ return percentage;
+ }
+
+ return null;
+ }
+
+ private Integer getAutoCloseTime() {
+ boolean hasEndCard = !config.isRewarded() || config.getHasEndCard();
+ if (hasEndCard) {
+ return null;
+ }
+
+ RewardedExt rewardedExt = config.getRewardManager().getRewardedExt();
+ RewardedClosingRules.Action action = rewardedExt.getClosingRules().getAction();
+ if (action != RewardedClosingRules.Action.AUTO_CLOSE) {
+ return null;
+ }
+
+ Integer percentageForReward = getVideoLengthPercentageForReward((int) videoDuration, config);
+ if (percentageForReward == null) {
+ return null;
+ }
+
+ int postRewardTime = rewardedExt.getClosingRules().getPostRewardTime() * 1000;
+ double autoCloseTime = percentageForReward / 100.0 * videoDuration + postRewardTime;
+ if (autoCloseTime > videoDuration) {
+ return null;
+ }
+
+ return (int) autoCloseTime;
+ }
+
}
diff --git a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/video/ExoPlayerView.java b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/video/ExoPlayerView.java
index a8820658b..d35889053 100644
--- a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/video/ExoPlayerView.java
+++ b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/video/ExoPlayerView.java
@@ -19,22 +19,16 @@
import android.content.Context;
import android.net.Uri;
import android.widget.RelativeLayout;
-
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
-
-import com.google.android.exoplayer2.ExoPlayer;
-import com.google.android.exoplayer2.MediaItem;
-import com.google.android.exoplayer2.PlaybackException;
-import com.google.android.exoplayer2.Player;
-import com.google.android.exoplayer2.SimpleExoPlayer;
+import com.google.android.exoplayer2.*;
import com.google.android.exoplayer2.source.ProgressiveMediaSource;
import com.google.android.exoplayer2.ui.PlayerView;
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
import com.google.android.exoplayer2.util.Util;
-
import org.prebid.mobile.LogUtil;
import org.prebid.mobile.api.exceptions.AdException;
+import org.prebid.mobile.configuration.AdUnitConfiguration;
import org.prebid.mobile.rendering.listeners.VideoCreativeViewListener;
import org.prebid.mobile.rendering.video.vast.VASTErrorCodes;
@@ -45,6 +39,7 @@ public class ExoPlayerView extends PlayerView implements VideoPlayerView {
@NonNull private final VideoCreativeViewListener videoCreativeViewListener;
private AdViewProgressUpdateTask adViewProgressUpdateTask;
+ private AdUnitConfiguration config;
private ExoPlayer player;
private Uri videoUri;
@@ -139,6 +134,10 @@ public float getVolume() {
return player.getVolume();
}
+ void setAdUnitConfiguration(AdUnitConfiguration config) {
+ this.config = config;
+ }
+
@Override
public void resume() {
LogUtil.debug(TAG, "resume() called");
@@ -218,7 +217,8 @@ private void initUpdateTask() {
try {
adViewProgressUpdateTask = new AdViewProgressUpdateTask(
videoCreativeViewListener,
- (int) player.getDuration()
+ (int) player.getDuration(),
+ config
);
adViewProgressUpdateTask.setVastVideoDuration(vastVideoDuration);
adViewProgressUpdateTask.execute();
diff --git a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/video/VideoCreative.java b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/video/VideoCreative.java
index d7f06625f..a1bd4d866 100644
--- a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/video/VideoCreative.java
+++ b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/video/VideoCreative.java
@@ -21,10 +21,8 @@
import android.os.AsyncTask;
import android.text.TextUtils;
import android.view.View;
-
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
-
import org.prebid.mobile.ContentObject;
import org.prebid.mobile.LogUtil;
import org.prebid.mobile.api.exceptions.AdException;
@@ -334,7 +332,7 @@ private void createCreativeView() throws AdException {
final Context context = contextReference.get();
if (context != null) {
final AdUnitConfiguration adConfiguration = model.getAdConfiguration();
- videoCreativeView = new VideoCreativeView(context, this);
+ videoCreativeView = new VideoCreativeView(context, this, adConfiguration);
videoCreativeView.setBroadcastId(adConfiguration.getBroadcastId());
// Get the preloaded video from device file storage
@@ -397,7 +395,9 @@ protected void showCallToAction() {
&& Utils.isNotBlank(model.getVastClickthroughUrl())
&& !model.getAdConfiguration().isRewarded()
) {
- videoCreativeView.showCallToAction();
+ videoCreativeView.showCallToAction(true);
+ } else if (model.getAdConfiguration().isRewarded()) {
+ videoCreativeView.showCallToAction(false);
}
}
diff --git a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/video/VideoCreativeView.java b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/video/VideoCreativeView.java
index e8c7d39e8..b8a0c4cff 100644
--- a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/video/VideoCreativeView.java
+++ b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/video/VideoCreativeView.java
@@ -24,6 +24,7 @@
import org.prebid.mobile.LogUtil;
import org.prebid.mobile.api.data.AdFormat;
import org.prebid.mobile.api.exceptions.AdException;
+import org.prebid.mobile.configuration.AdUnitConfiguration;
import org.prebid.mobile.core.R;
import org.prebid.mobile.rendering.listeners.VideoCreativeViewListener;
import org.prebid.mobile.rendering.models.ViewPool;
@@ -48,6 +49,7 @@ public class VideoCreativeView extends RelativeLayout {
@Nullable private View callToActionView;
private ExoPlayerView exoPlayerView;
private VolumeControlView volumeControlView;
+ private AdUnitConfiguration config;
private String callToActionUrl;
private boolean urlHandleInProgress;
@@ -57,14 +59,19 @@ public class VideoCreativeView extends RelativeLayout {
public VideoCreativeView(
Context context,
- VideoCreativeViewListener videoCreativeViewListener
-
+ VideoCreativeViewListener videoCreativeViewListener,
+ AdUnitConfiguration config
) throws AdException {
super(context);
+ this.config = config;
this.videoCreativeViewListener = videoCreativeViewListener;
init();
}
+ public void setAdUnitConfiguration(AdUnitConfiguration config) {
+ this.config = config;
+ }
+
public void setVideoUri(Uri videoUri) {
if (videoUri == null) {
LogUtil.error(TAG, "setVideoUri: Failed. Provided uri is null.");
@@ -129,8 +136,8 @@ public void unMute() {
updateVolumeControlView(VolumeControlView.VolumeState.UN_MUTED);
}
- public void showCallToAction() {
- addCallToActionView();
+ public void showCallToAction(boolean visibility) {
+ addCallToActionView(visibility);
}
public void hideCallToAction() {
@@ -191,10 +198,11 @@ private void init() throws AdException {
null
);
+ exoPlayerView.setAdUnitConfiguration(config);
addView(exoPlayerView);
}
- private void addCallToActionView() {
+ private void addCallToActionView(boolean visibility) {
callToActionView = inflate(getContext(), R.layout.lyt_call_to_action, null);
callToActionView.setOnClickListener(v -> handleCallToActionClick());
@@ -208,6 +216,10 @@ private void addCallToActionView() {
layoutParams.addRule(ALIGN_PARENT_RIGHT);
layoutParams.setMargins(margin, margin, margin, margin);
+ if (!visibility) {
+ callToActionView.setVisibility(View.GONE);
+ }
+
addView(callToActionView, layoutParams);
InsetsUtils.addCutoutAndNavigationInsets(callToActionView);
}
diff --git a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/views/interstitial/InterstitialVideo.java b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/views/interstitial/InterstitialVideo.java
index 6db1e4eb0..ae87230ca 100644
--- a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/views/interstitial/InterstitialVideo.java
+++ b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/views/interstitial/InterstitialVideo.java
@@ -31,15 +31,16 @@
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.TextView;
-
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
-
import org.prebid.mobile.LogUtil;
import org.prebid.mobile.configuration.AdUnitConfiguration;
import org.prebid.mobile.core.R;
import org.prebid.mobile.rendering.interstitial.AdBaseDialog;
+import org.prebid.mobile.rendering.interstitial.rewarded.RewardedCompletionRules;
+import org.prebid.mobile.rendering.interstitial.rewarded.RewardedExt;
import org.prebid.mobile.rendering.models.InterstitialDisplayPropertiesInternal;
+import org.prebid.mobile.rendering.sdk.PrebidContextHolder;
import org.prebid.mobile.rendering.utils.helpers.InsetsUtils;
import org.prebid.mobile.rendering.utils.helpers.Utils;
import org.prebid.mobile.rendering.views.base.BaseAdView;
@@ -68,12 +69,12 @@ public class InterstitialVideo extends AdBaseDialog {
// "If these are pure JVM unit tests (i.e. run on your computer's JVM and not on an Android emulator/device), then you have no real implementations of methods on any Android classes.
// You are using a mockable jar which just contains empty classes and methods with "final" removed so you can mock them, but they don't really work like when running normal Android."
private final WeakReference contextReference;
- private final AdUnitConfiguration adConfiguration;
+ private final AdUnitConfiguration config;
private Handler handler;
private Timer timer;
- private TimerTask currentTimerTask = null;
+ private TimerTask showCloseButtonTask = null;
private int currentTimerTaskHash = 0;
// Flag used by caller to close manually; More intuitive and reliable way to show
@@ -90,13 +91,12 @@ public InterstitialVideo(
Context context,
FrameLayout adView,
InterstitialManager interstitialManager,
- AdUnitConfiguration adConfiguration
+ AdUnitConfiguration config
) {
super(context, interstitialManager);
-
contextReference = new WeakReference<>(context);
- this.adConfiguration = adConfiguration;
- isRewarded = adConfiguration.isRewarded();
+ this.config = config;
+ isRewarded = this.config.isRewarded();
adViewContainer = adView;
init();
}
@@ -150,10 +150,10 @@ public void scheduleShowButtonTask() {
long videoLength = getDuration(adViewContainer);
if (videoLength <= skipDelay) {
- scheduleTimer(videoLength);
+ scheduleAllTimers(videoLength);
showCloseBtnOnComplete = true;
} else {
- scheduleTimer(skipDelay);
+ scheduleAllTimers(skipDelay);
}
}
@@ -176,7 +176,7 @@ public void scheduleShowCloseBtnTask(
// Clamp close delay value
long upperBound = Math.min(videoLength, CLOSE_DELAY_MAX_IN_MS);
long closeDelayTimeInMs = Utils.clampInMillis((int) delayInMs, 0, (int) upperBound);
- scheduleTimer(closeDelayTimeInMs);
+ scheduleAllTimers(closeDelayTimeInMs);
}
}
@@ -278,7 +278,7 @@ private long getCloseDelayInMs(
}
private void createCurrentTimerTask() {
- currentTimerTask = new TimerTask() {
+ showCloseButtonTask = new TimerTask() {
@Override
public void run() {
if (currentTimerTaskHash != this.hashCode()) {
@@ -300,7 +300,7 @@ public void run() {
}
};
- currentTimerTaskHash = currentTimerTask.hashCode();
+ currentTimerTaskHash = showCloseButtonTask.hashCode();
}
protected long getDuration(View view) {
@@ -309,9 +309,9 @@ protected long getDuration(View view) {
private void stopTimer() {
if (timer != null) {
- if (currentTimerTask != null) {
- currentTimerTask.cancel();
- currentTimerTask = null;
+ if (showCloseButtonTask != null) {
+ showCloseButtonTask.cancel();
+ showCloseButtonTask = null;
}
timer.cancel();
@@ -340,7 +340,7 @@ private void handleAdViewShow() {
}
@VisibleForTesting
- protected void scheduleTimer(long delayInMs) {
+ protected void scheduleAllTimers(long delayInMs) {
LogUtil.debug(TAG, "Scheduling timer at: " + delayInMs);
stopTimer();
@@ -350,13 +350,25 @@ protected void scheduleTimer(long delayInMs) {
createCurrentTimerTask();
if (delayInMs >= 0) {
- // we should convert it to milliseconds, so timer.schedule properly gets the delay in millisconds?
- timer.schedule(currentTimerTask, (delayInMs));
+ long delayToShowCloseButton = delayInMs;
+
+ if (isRewarded) {
+ delayToShowCloseButton = getDelayToShowCloseButton((int) delayInMs, config);
+ }
+
+ timer.schedule(showCloseButtonTask, delayToShowCloseButton);
}
// Show timer until close
if (isRewarded) {
- showDurationTimer(delayInMs);
+ long progressBarDuration = delayInMs;
+
+ Integer completionTime = getTimeToReward((int) delayInMs, config);
+ if (completionTime != null) {
+ progressBarDuration = completionTime;
+ }
+
+ showDurationTimer(progressBarDuration);
} else {
startTimer(delayInMs);
}
@@ -388,6 +400,9 @@ protected void showDurationTimer(long durationInMillis) {
return;
}
+ int paddingTop = (int) (50 * PrebidContextHolder.getContext().getResources().getDisplayMetrics().density);
+ lytCountDownCircle.setPadding(0, 0, 0, paddingTop);
+
final ProgressBar pbProgress = lytCountDownCircle.findViewById(R.id.Progress);
pbProgress.setMax((int) durationInMillis);
@@ -423,6 +438,11 @@ public void onFinish() {
return;
}
adViewContainer.removeView(lytCountDownCircle);
+
+ if (isRewarded && !hasEndCard) {
+ View learnMore = adViewContainer.findViewById(R.id.tv_learn_more);
+ learnMore.setVisibility(View.VISIBLE);
+ }
}
};
countDownTimer.start();
@@ -450,4 +470,35 @@ protected int getSkipDelayMs() {
return CLOSE_DELAY_DEFAULT_IN_MS;
}
+ @Nullable
+ @VisibleForTesting
+ protected static Integer getTimeToReward(int durationMs, AdUnitConfiguration config) {
+ RewardedExt rewardedExt = config.getRewardManager().getRewardedExt();
+
+ RewardedCompletionRules rules = rewardedExt.getCompletionRules();
+ if (rules.getVideoEvent() != null) {
+ return (int) rules.getVideoEvent().getCompletionTime(durationMs);
+ }
+
+ if (rules.getVideoTime() != null) {
+ return rules.getVideoTime() * 1000;
+ }
+
+ return null;
+ }
+
+ @VisibleForTesting
+ protected static Integer getDelayToShowCloseButton(int duration, AdUnitConfiguration config) {
+ RewardedExt rewardedExt = config.getRewardManager().getRewardedExt();
+
+ int completionTime = duration;
+ Integer rewardedConfigTime = getTimeToReward(duration, config);
+ if (rewardedConfigTime != null) {
+ completionTime = rewardedConfigTime;
+ }
+
+ int postRewardTime = rewardedExt.getClosingRules().getPostRewardTime();
+ return Math.min(duration, completionTime + (postRewardTime * 1000));
+ }
+
}
diff --git a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/views/video/VideoDialog.java b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/views/video/VideoDialog.java
index b64c41b59..0c4d58457 100644
--- a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/views/video/VideoDialog.java
+++ b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/views/video/VideoDialog.java
@@ -96,7 +96,7 @@ private void handleShowAction() {
adView.unMute();
changeCloseViewVisibility(View.VISIBLE);
- adView.showCallToAction();
+ adView.showCallToAction(true);
adViewManager.updateAdView(adViewContainer);
}
diff --git a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/views/webview/ActionUrl.java b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/views/webview/ActionUrl.java
new file mode 100644
index 000000000..1c62b54c3
--- /dev/null
+++ b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/views/webview/ActionUrl.java
@@ -0,0 +1,13 @@
+package org.prebid.mobile.rendering.views.webview;
+
+public class ActionUrl {
+
+ public String url;
+ Runnable action;
+
+ public ActionUrl(String url, Runnable action) {
+ this.url = url;
+ this.action = action;
+ }
+
+}
diff --git a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/views/webview/AdWebView.java b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/views/webview/AdWebView.java
index 6d472cd95..c879bd627 100644
--- a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/views/webview/AdWebView.java
+++ b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/views/webview/AdWebView.java
@@ -159,4 +159,10 @@ public double densityScalingFactor() {
public void setDomain(String domain) {
this.domain = domain;
}
+
+ public void setActionUrl(ActionUrl actionUrl) {
+ if (adWebViewClient != null) {
+ adWebViewClient.registerSpecialUrl(actionUrl);
+ }
+ }
}
diff --git a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/views/webview/AdWebViewClient.java b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/views/webview/AdWebViewClient.java
index fe7d2a6df..ab0eb8f56 100644
--- a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/views/webview/AdWebViewClient.java
+++ b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/views/webview/AdWebViewClient.java
@@ -25,6 +25,7 @@
import android.webkit.WebViewClient;
import org.prebid.mobile.LogUtil;
+import java.util.ArrayList;
import java.util.HashSet;
import static android.webkit.WebView.HitTestResult.SRC_ANCHOR_TYPE;
@@ -42,6 +43,7 @@ public class AdWebViewClient extends WebViewClient {
private HashSet urls = new HashSet<>();
private String pageUrl;
+ private ArrayList actionUrls = new ArrayList<>();
public interface AdAssetsLoadedListener {
@@ -141,6 +143,10 @@ public boolean shouldOverrideUrlLoading(WebView view, String url) {
}
try {
+ if (checkSpecialUrls(url)) {
+ return true;
+ }
+
WebViewBase webViewBase = ((WebViewBase) view);
if (!webViewBase.isClicked()) {
@@ -171,6 +177,24 @@ public boolean shouldOverrideKeyEvent(WebView view, KeyEvent event) {
return super.shouldOverrideKeyEvent(view, event);
}
+ public void registerSpecialUrl(ActionUrl actionUrl) {
+ if (actionUrl == null || actionUrl.action == null || actionUrl.url == null) return;
+
+ actionUrls.add(actionUrl);
+ }
+
+ private boolean checkSpecialUrls(String url) {
+ for (ActionUrl actionUrl : actionUrls) {
+ if (url.equals(actionUrl.url)) {
+ actionUrl.action.run();
+ actionUrls.remove(actionUrl);
+ return true;
+ }
+ }
+ return false;
+ }
+
+
private void reloadUrl(WebView view, String s) {
view.stopLoading();
diff --git a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/views/webview/PrebidWebViewBase.java b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/views/webview/PrebidWebViewBase.java
index 7a6d87b7b..4bcb75bda 100644
--- a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/views/webview/PrebidWebViewBase.java
+++ b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/rendering/views/webview/PrebidWebViewBase.java
@@ -27,9 +27,6 @@
import android.view.animation.AnimationUtils;
import android.webkit.WebView;
import android.widget.FrameLayout;
-
-import androidx.annotation.Nullable;
-
import org.prebid.mobile.LogUtil;
import org.prebid.mobile.rendering.listeners.WebViewDelegate;
import org.prebid.mobile.rendering.models.HTMLCreative;
@@ -300,6 +297,10 @@ protected void runOnUiThread(Runnable runnable) {
handler.post(runnable);
}
+ public void setActionUrl(ActionUrl actionUrl) {
+ webView.setActionUrl(actionUrl);
+ }
+
private static final class WebViewCleanupRunnable implements Runnable {
private static final String TAG = WebViewCleanupRunnable.class.getSimpleName();
diff --git a/PrebidMobile/PrebidMobile-core/src/test/java/org/prebid/mobile/api/rendering/InterstitialAdUnitTest.java b/PrebidMobile/PrebidMobile-core/src/test/java/org/prebid/mobile/api/rendering/InterstitialAdUnitTest.java
index bc38ceff2..429a1178f 100644
--- a/PrebidMobile/PrebidMobile-core/src/test/java/org/prebid/mobile/api/rendering/InterstitialAdUnitTest.java
+++ b/PrebidMobile/PrebidMobile-core/src/test/java/org/prebid/mobile/api/rendering/InterstitialAdUnitTest.java
@@ -16,28 +16,8 @@
package org.prebid.mobile.api.rendering;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoInteractions;
-import static org.mockito.Mockito.when;
-import static org.prebid.mobile.api.rendering.BaseInterstitialAdUnit.InterstitialAdUnitState.LOADING;
-import static org.prebid.mobile.api.rendering.BaseInterstitialAdUnit.InterstitialAdUnitState.PREBID_LOADING;
-import static org.prebid.mobile.api.rendering.BaseInterstitialAdUnit.InterstitialAdUnitState.READY_FOR_LOAD;
-import static org.prebid.mobile.api.rendering.BaseInterstitialAdUnit.InterstitialAdUnitState.READY_TO_DISPLAY_GAM;
-import static org.prebid.mobile.api.rendering.BaseInterstitialAdUnit.InterstitialAdUnitState.READY_TO_DISPLAY_PREBID;
-import static org.prebid.mobile.api.rendering.pluginrenderer.PrebidMobilePluginRegister.PREBID_MOBILE_RENDERER_NAME;
-
import android.app.Activity;
import android.content.Context;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -67,6 +47,11 @@
import java.util.EnumSet;
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.*;
+import static org.prebid.mobile.api.rendering.BaseInterstitialAdUnit.InterstitialAdUnitState.*;
+import static org.prebid.mobile.api.rendering.pluginrenderer.PrebidMobilePluginRegister.PREBID_MOBILE_RENDERER_NAME;
+
@RunWith(RobolectricTestRunner.class)
public class InterstitialAdUnitTest {
@@ -98,7 +83,7 @@ public void setup() {
WhiteBox.setInternalState(interstitialAdUnit, "bidLoader", mockBidLoader);
WhiteBox.setInternalState(interstitialAdUnit, "interstitialController", mockInterstitialController);
- final AdUnitConfiguration adUnitConfig = interstitialAdUnit.adUnitConfig;
+ final AdUnitConfiguration adUnitConfig = interstitialAdUnit.config;
assertEquals(AdPosition.FULLSCREEN.getValue(), adUnitConfig.getAdPositionValue());
}
@@ -109,7 +94,7 @@ public void createInterstitialAdUnit_BothDefaultAdUnitFormats() {
CONFIGURATION_ID
);
- EnumSet adFormats = interstitialAdUnit.adUnitConfig.getAdFormats();
+ EnumSet adFormats = interstitialAdUnit.config.getAdFormats();
assertEquals(EnumSet.of(AdFormat.INTERSTITIAL, AdFormat.VAST), adFormats);
}
@@ -121,7 +106,7 @@ public void createInterstitialAdUnitOtherConstructor_BothDefaultAdUnitFormats()
mock(InterstitialEventHandler.class)
);
- EnumSet adFormats = interstitialAdUnit.adUnitConfig.getAdFormats();
+ EnumSet adFormats = interstitialAdUnit.config.getAdFormats();
assertEquals(EnumSet.of(AdFormat.INTERSTITIAL, AdFormat.VAST), adFormats);
}
@@ -139,7 +124,7 @@ public void createInterstitialAdUnitNoEventHandler_InstanceCreatedStandaloneEven
assertNotNull(interstitialAdUnit);
assertTrue(eventHandler instanceof StandaloneInterstitialEventHandler);
assertNotNull(bidLoader);
- assertEquals(EnumSet.of(AdFormat.INTERSTITIAL, AdFormat.VAST), interstitialAdUnit.adUnitConfig.getAdFormats());
+ assertEquals(EnumSet.of(AdFormat.INTERSTITIAL, AdFormat.VAST), interstitialAdUnit.config.getAdFormats());
}
@Test
@@ -150,7 +135,7 @@ public void createInterstitialAdUnitWithBannerParameter_AdFormatMustBeInterstiti
EnumSet.of(AdUnitFormat.BANNER)
);
- assertEquals(EnumSet.of(AdFormat.INTERSTITIAL), interstitialAdUnit.adUnitConfig.getAdFormats());
+ assertEquals(EnumSet.of(AdFormat.INTERSTITIAL), interstitialAdUnit.config.getAdFormats());
}
@Test
diff --git a/PrebidMobile/PrebidMobile-core/src/test/java/org/prebid/mobile/api/rendering/RewardedAdUnitTest.java b/PrebidMobile/PrebidMobile-core/src/test/java/org/prebid/mobile/api/rendering/RewardedAdUnitTest.java
index 3ee931967..82267926a 100644
--- a/PrebidMobile/PrebidMobile-core/src/test/java/org/prebid/mobile/api/rendering/RewardedAdUnitTest.java
+++ b/PrebidMobile/PrebidMobile-core/src/test/java/org/prebid/mobile/api/rendering/RewardedAdUnitTest.java
@@ -25,8 +25,8 @@
import org.mockito.MockitoAnnotations;
import org.prebid.mobile.PrebidMobile;
import org.prebid.mobile.api.exceptions.AdException;
-import org.prebid.mobile.api.rendering.pluginrenderer.PrebidMobilePluginRenderer;
import org.prebid.mobile.api.rendering.listeners.RewardedAdUnitListener;
+import org.prebid.mobile.api.rendering.pluginrenderer.PrebidMobilePluginRenderer;
import org.prebid.mobile.configuration.AdUnitConfiguration;
import org.prebid.mobile.rendering.bidding.data.bid.Bid;
import org.prebid.mobile.rendering.bidding.data.bid.BidResponse;
@@ -77,7 +77,7 @@ public void setup() {
WhiteBox.setInternalState(rewardedAdUnit, "bidLoader", mockBidLoader);
WhiteBox.setInternalState(rewardedAdUnit, "interstitialController", mockInterstitialController);
- final AdUnitConfiguration adUnitConfig = rewardedAdUnit.adUnitConfig;
+ final AdUnitConfiguration adUnitConfig = rewardedAdUnit.config;
assertEquals(AdPosition.FULLSCREEN.getValue(), adUnitConfig.getAdPositionValue());
}
diff --git a/PrebidMobile/PrebidMobile-core/src/test/java/org/prebid/mobile/rendering/interstitial/AdInterstitialDialogTest.java b/PrebidMobile/PrebidMobile-core/src/test/java/org/prebid/mobile/rendering/interstitial/AdInterstitialDialogTest.java
index 9fc4a320d..68b2024c9 100644
--- a/PrebidMobile/PrebidMobile-core/src/test/java/org/prebid/mobile/rendering/interstitial/AdInterstitialDialogTest.java
+++ b/PrebidMobile/PrebidMobile-core/src/test/java/org/prebid/mobile/rendering/interstitial/AdInterstitialDialogTest.java
@@ -18,10 +18,17 @@
import android.app.Activity;
import android.content.Context;
+import android.view.View;
import android.widget.FrameLayout;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.prebid.mobile.configuration.AdUnitConfiguration;
+import org.prebid.mobile.rendering.interstitial.rewarded.RewardManager;
+import org.prebid.mobile.rendering.interstitial.rewarded.RewardedClosingRules;
+import org.prebid.mobile.rendering.interstitial.rewarded.RewardedCompletionRules;
+import org.prebid.mobile.rendering.interstitial.rewarded.RewardedExt;
+import org.prebid.mobile.rendering.models.InterstitialDisplayPropertiesInternal;
import org.prebid.mobile.rendering.views.interstitial.InterstitialManager;
import org.prebid.mobile.rendering.views.webview.WebViewBase;
import org.prebid.mobile.rendering.views.webview.mraid.BaseJSInterface;
@@ -95,4 +102,112 @@ public void cancelTest() {
verify(mockBaseJSInterface).onStateChange(JSInterface.STATE_DEFAULT);
verify(mockWebViewBase).detachFromParent();
}
+
+
+ @Test
+ public void setUpCloseButton_noConfig() {
+ AdInterstitialDialog spySubject = adInterstitialDialog;
+
+ spySubject.setUpCloseButtonTask();
+
+ verify(spySubject, never()).changeCloseViewVisibility(View.GONE);
+ verify(spySubject, never()).scheduleCloseButtonDisplaying(anyInt(), anyBoolean());
+ verify(spySubject, never()).scheduleRewardListener(anyInt(), anyInt(), anyBoolean());
+ }
+
+ @Test
+ public void setUpCloseButton_userAlreadyRewarded() {
+ AdInterstitialDialog spySubject = adInterstitialDialog;
+
+ InterstitialDisplayPropertiesInternal mockProperties = mock(InterstitialDisplayPropertiesInternal.class);
+ when(mockInterstitialManager.getInterstitialDisplayProperties()).thenReturn(mockProperties);
+
+ RewardManager mockRewardManager = mock(RewardManager.class);
+ when(mockRewardManager.getUserRewardedAlready()).thenReturn(true);
+
+ AdUnitConfiguration mockConfig = mock(AdUnitConfiguration.class);
+ when(mockConfig.isRewarded()).thenReturn(true);
+ when(mockConfig.getRewardManager()).thenReturn(mockRewardManager);
+ mockProperties.config = mockConfig;
+
+ spySubject.setUpCloseButtonTask();
+
+ verify(spySubject, never()).changeCloseViewVisibility(View.GONE);
+ verify(spySubject, never()).scheduleCloseButtonDisplaying(anyInt(), anyBoolean());
+ verify(spySubject, never()).scheduleRewardListener(anyInt(), anyInt(), anyBoolean());
+ }
+
+ @Test
+ public void setUpCloseButton_default() {
+ AdInterstitialDialog spySubject = adInterstitialDialog;
+ InterstitialDisplayPropertiesInternal mockProperties = mock(InterstitialDisplayPropertiesInternal.class);
+
+ AdUnitConfiguration mockConfig = mock(AdUnitConfiguration.class);
+ when(mockConfig.isRewarded()).thenReturn(true);
+ RewardedCompletionRules completionRules = new RewardedCompletionRules();
+ RewardedClosingRules closingRules = new RewardedClosingRules();
+ RewardedExt rewardedExt = new RewardedExt(null, completionRules, closingRules);
+ mockProperties.config = mockConfig;
+
+ RewardManager mockRewardManager = mock(RewardManager.class);
+ when(mockRewardManager.getRewardedExt()).thenReturn(rewardedExt);
+ when(mockConfig.getRewardManager()).thenReturn(mockRewardManager);
+
+ when(mockInterstitialManager.getInterstitialDisplayProperties()).thenReturn(mockProperties);
+
+ spySubject.setUpCloseButtonTask();
+
+ verify(spySubject).changeCloseViewVisibility(View.GONE);
+ verify(spySubject).scheduleRewardListener(RewardedCompletionRules.DEFAULT_BANNER_TIME_MS, 0, false);
+ }
+
+ @Test
+ public void setUpCloseButton_rewardEventUrl() {
+ AdInterstitialDialog spySubject = adInterstitialDialog;
+ InterstitialDisplayPropertiesInternal mockProperties = mock(InterstitialDisplayPropertiesInternal.class);
+
+ AdUnitConfiguration mockConfig = mock(AdUnitConfiguration.class);
+ when(mockConfig.isRewarded()).thenReturn(true);
+ RewardedCompletionRules completionRules = new RewardedCompletionRules(null, null, null, "rwdd://yes", null, null);
+ RewardedClosingRules closingRules = new RewardedClosingRules();
+ RewardedExt rewardedExt = new RewardedExt(null, completionRules, closingRules);
+ mockProperties.config = mockConfig;
+
+ RewardManager mockRewardManager = mock(RewardManager.class);
+ when(mockRewardManager.getRewardedExt()).thenReturn(rewardedExt);
+ when(mockConfig.getRewardManager()).thenReturn(mockRewardManager);
+
+ when(mockInterstitialManager.getInterstitialDisplayProperties()).thenReturn(mockProperties);
+
+ spySubject.setUpCloseButtonTask();
+
+ verify(spySubject).changeCloseViewVisibility(View.GONE);
+ verify(spySubject).scheduleRewardListener(RewardedCompletionRules.DEFAULT_BANNER_TIME_MS, 0, false);
+ verify(mockRewardManager).setAfterRewardListener(any());
+ }
+
+ @Test
+ public void setUpCloseButton_noRewardEventUrl() {
+ AdInterstitialDialog spySubject = adInterstitialDialog;
+ InterstitialDisplayPropertiesInternal mockProperties = mock(InterstitialDisplayPropertiesInternal.class);
+
+ AdUnitConfiguration mockConfig = mock(AdUnitConfiguration.class);
+ when(mockConfig.isRewarded()).thenReturn(true);
+ RewardedCompletionRules completionRules = new RewardedCompletionRules(15, null, null, null, null, null);
+ RewardedClosingRules closingRules = new RewardedClosingRules();
+ RewardedExt rewardedExt = new RewardedExt(null, completionRules, closingRules);
+ mockProperties.config = mockConfig;
+
+ RewardManager mockRewardManager = mock(RewardManager.class);
+ when(mockRewardManager.getRewardedExt()).thenReturn(rewardedExt);
+ when(mockConfig.getRewardManager()).thenReturn(mockRewardManager);
+
+ when(mockInterstitialManager.getInterstitialDisplayProperties()).thenReturn(mockProperties);
+
+ spySubject.setUpCloseButtonTask();
+
+ verify(spySubject).changeCloseViewVisibility(View.GONE);
+ verify(spySubject).scheduleRewardListener(15_000, 0, false);
+ }
+
}
\ No newline at end of file
diff --git a/PrebidMobile/PrebidMobile-core/src/test/java/org/prebid/mobile/rendering/interstitial/rewarded/RewardedExtParserTest.java b/PrebidMobile/PrebidMobile-core/src/test/java/org/prebid/mobile/rendering/interstitial/rewarded/RewardedExtParserTest.java
new file mode 100644
index 000000000..d87d86b6f
--- /dev/null
+++ b/PrebidMobile/PrebidMobile-core/src/test/java/org/prebid/mobile/rendering/interstitial/rewarded/RewardedExtParserTest.java
@@ -0,0 +1,153 @@
+package org.prebid.mobile.rendering.interstitial.rewarded;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.junit.Test;
+import org.prebid.mobile.test.utils.ResourceUtils;
+
+import static org.junit.Assert.*;
+
+public class RewardedExtParserTest {
+
+ @Test
+ public void parse_emptyRewardedJson_default() throws JSONException {
+ String json = "{}";
+ JSONObject jsonObject = new JSONObject(json);
+
+ RewardedExt ext = RewardedExtParser.parse(jsonObject);
+
+ // Reward
+ assertNull(ext.getReward());
+
+ // Completion rules
+ RewardedCompletionRules completionRules = ext.getCompletionRules();
+ assertNotNull(completionRules);
+
+ assertEquals(120, completionRules.getBannerTime());
+ assertEquals(120, completionRules.getEndCardTime());
+ assertNull(completionRules.getVideoTime());
+
+ assertNull(completionRules.getVideoEvent());
+ assertNull(completionRules.getBannerEvent());
+ assertNull(completionRules.getEndCardEvent());
+
+ // Closing rules
+ RewardedClosingRules closingRules = ext.getClosingRules();
+ assertNotNull(closingRules);
+
+ assertEquals(0, closingRules.getPostRewardTime());
+ assertEquals(RewardedClosingRules.Action.CLOSE_BUTTON, closingRules.getAction());
+ }
+
+ @Test
+ public void parse_fullRewardedJson_fromJson() throws Throwable {
+ String json = ResourceUtils.convertResourceToString("RewardedExt/full.json");
+ JSONObject jsonObject = new JSONObject(json);
+ RewardedExt ext = RewardedExtParser.parse(jsonObject);
+
+ // Reward
+ Reward reward = ext.getReward();
+ assertNotNull(reward);
+ assertEquals(11, reward.getCount());
+ assertEquals("SuperDollars", reward.getType());
+ assertNull(reward.getExt());
+
+ // Completion rules
+ RewardedCompletionRules completionRules = ext.getCompletionRules();
+ assertNotNull(completionRules);
+
+ assertEquals(12, completionRules.getBannerTime());
+ assertEquals(Integer.valueOf(13), completionRules.getVideoTime());
+ assertEquals(14, completionRules.getEndCardTime());
+
+ assertEquals("banner_event", completionRules.getBannerEvent());
+ assertEquals(RewardedCompletionRules.PlaybackEvent.COMPLETE, completionRules.getVideoEvent());
+ assertEquals("endcard_event", completionRules.getEndCardEvent());
+
+ // Closing rules
+ RewardedClosingRules closingRules = ext.getClosingRules();
+ assertNotNull(closingRules);
+
+ assertEquals(15, closingRules.getPostRewardTime());
+ assertEquals(RewardedClosingRules.Action.AUTO_CLOSE, closingRules.getAction());
+ }
+
+
+ @Test
+ public void parse_rewardWithExt() throws Throwable {
+ String json = ResourceUtils.convertResourceToString("RewardedExt/reward_with_ext.json");
+ JSONObject jsonObject = new JSONObject(json);
+ RewardedExt ext = RewardedExtParser.parse(jsonObject);
+
+ // Reward
+ Reward reward = ext.getReward();
+ assertNotNull(reward);
+ assertEquals(11, reward.getCount());
+ assertEquals("SuperDollars", reward.getType());
+ assertNotNull(reward.getExt());
+ assertEquals("{\"additional_data\":\"test\"}", reward.getExt().toString());
+ }
+
+ @Test
+ public void parse_closeAction_autoClose() throws Throwable {
+ String json = ResourceUtils.convertResourceToString("RewardedExt/close_type_1.json");
+ JSONObject jsonObject = new JSONObject(json);
+ RewardedExt ext = RewardedExtParser.parse(jsonObject);
+
+ assertEquals(RewardedClosingRules.Action.AUTO_CLOSE, ext.getClosingRules().getAction());
+ }
+
+ @Test
+ public void parse_closeAction_closeButton() throws Throwable {
+ String json = ResourceUtils.convertResourceToString("RewardedExt/close_type_2.json");
+ JSONObject jsonObject = new JSONObject(json);
+ RewardedExt ext = RewardedExtParser.parse(jsonObject);
+
+ assertEquals(RewardedClosingRules.Action.CLOSE_BUTTON, ext.getClosingRules().getAction());
+ }
+
+ @Test
+ public void parse_playbackEvent_start() throws Throwable {
+ String json = ResourceUtils.convertResourceToString("RewardedExt/playbackevent_start.json");
+ JSONObject jsonObject = new JSONObject(json);
+ RewardedExt ext = RewardedExtParser.parse(jsonObject);
+
+ assertEquals(RewardedCompletionRules.PlaybackEvent.START, ext.getCompletionRules().getVideoEvent());
+ }
+
+ @Test
+ public void parse_playbackEvent_firstQuartile() throws Throwable {
+ String json = ResourceUtils.convertResourceToString("RewardedExt/playbackevent_firstquartile.json");
+ JSONObject jsonObject = new JSONObject(json);
+ RewardedExt ext = RewardedExtParser.parse(jsonObject);
+
+ assertEquals(RewardedCompletionRules.PlaybackEvent.FIRST_QUARTILE, ext.getCompletionRules().getVideoEvent());
+ }
+
+ @Test
+ public void parse_playbackEvent_midpoint() throws Throwable {
+ String json = ResourceUtils.convertResourceToString("RewardedExt/playbackevent_midpoint.json");
+ JSONObject jsonObject = new JSONObject(json);
+ RewardedExt ext = RewardedExtParser.parse(jsonObject);
+
+ assertEquals(RewardedCompletionRules.PlaybackEvent.MIDPOINT, ext.getCompletionRules().getVideoEvent());
+ }
+
+ @Test
+ public void parse_playbackEvent_thirdQuartile() throws Throwable {
+ String json = ResourceUtils.convertResourceToString("RewardedExt/playbackevent_thirdquartile.json");
+ JSONObject jsonObject = new JSONObject(json);
+ RewardedExt ext = RewardedExtParser.parse(jsonObject);
+
+ assertEquals(RewardedCompletionRules.PlaybackEvent.THIRD_QUARTILE, ext.getCompletionRules().getVideoEvent());
+ }
+
+ @Test
+ public void parse_playbackEvent_complete() throws Throwable {
+ String json = ResourceUtils.convertResourceToString("RewardedExt/playbackevent_complete.json");
+ JSONObject jsonObject = new JSONObject(json);
+ RewardedExt ext = RewardedExtParser.parse(jsonObject);
+
+ assertEquals(RewardedCompletionRules.PlaybackEvent.COMPLETE, ext.getCompletionRules().getVideoEvent());
+ }
+}
\ No newline at end of file
diff --git a/PrebidMobile/PrebidMobile-core/src/test/java/org/prebid/mobile/rendering/models/HTMLCreativeTest.java b/PrebidMobile/PrebidMobile-core/src/test/java/org/prebid/mobile/rendering/models/HTMLCreativeTest.java
index 63fbb2bfa..c8dca77e8 100644
--- a/PrebidMobile/PrebidMobile-core/src/test/java/org/prebid/mobile/rendering/models/HTMLCreativeTest.java
+++ b/PrebidMobile/PrebidMobile-core/src/test/java/org/prebid/mobile/rendering/models/HTMLCreativeTest.java
@@ -23,12 +23,17 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.prebid.mobile.api.data.AdFormat;
import org.prebid.mobile.api.exceptions.AdException;
import org.prebid.mobile.configuration.AdUnitConfiguration;
+import org.prebid.mobile.rendering.interstitial.rewarded.RewardManager;
+import org.prebid.mobile.rendering.interstitial.rewarded.RewardedClosingRules;
+import org.prebid.mobile.rendering.interstitial.rewarded.RewardedCompletionRules;
+import org.prebid.mobile.rendering.interstitial.rewarded.RewardedExt;
import org.prebid.mobile.rendering.listeners.CreativeResolutionListener;
import org.prebid.mobile.rendering.listeners.CreativeViewListener;
import org.prebid.mobile.rendering.models.internal.InternalFriendlyObstruction;
@@ -40,6 +45,7 @@
import org.prebid.mobile.rendering.session.manager.OmAdSessionManager;
import org.prebid.mobile.rendering.utils.exposure.ViewExposure;
import org.prebid.mobile.rendering.views.interstitial.InterstitialManager;
+import org.prebid.mobile.rendering.views.webview.ActionUrl;
import org.prebid.mobile.rendering.views.webview.PrebidWebViewBanner;
import org.prebid.mobile.rendering.views.webview.PrebidWebViewBase;
import org.prebid.mobile.rendering.views.webview.WebViewBase;
@@ -380,4 +386,79 @@ public void htmlAdLoaded_TrackEvent() {
verify(mockModel).trackDisplayAdEvent(TrackingEvent.Events.LOADED);
}
+
+
+ @Test
+ public void rewardedTracking_ignored() {
+ PrebidWebViewBase mockWebView = mock(PrebidWebViewBase.class);
+ AdUnitConfiguration config = new AdUnitConfiguration();
+ config.setRewarded(false);
+
+ HTMLCreative.rewardedTracking(mockWebView, config);
+
+ verify(mockWebView, never()).setActionUrl(any());
+ }
+
+ @Test
+ public void rewardedTracking_nullUrlEvent() {
+ PrebidWebViewBase mockWebView = mock(PrebidWebViewBase.class);
+ AdUnitConfiguration config = new AdUnitConfiguration();
+ config.setRewarded(true);
+ config.setHasEndCard(false);
+
+ RewardedExt rewardedExt = new RewardedExt(
+ null,
+ new RewardedCompletionRules(null, null, null, null, null, null),
+ new RewardedClosingRules()
+ );
+ RewardManager rewardManager = new RewardManager();
+ rewardManager.setRewardedExt(rewardedExt);
+ config.setRewardManager(rewardManager);
+
+
+ HTMLCreative.rewardedTracking(mockWebView, config);
+
+ verify(mockWebView, never()).setActionUrl(any());
+ }
+
+ @Test
+ public void rewardedTracking_withoutEndCard() {
+ PrebidWebViewBase mockWebView = mock(PrebidWebViewBase.class);
+ AdUnitConfiguration config = new AdUnitConfiguration();
+ config.setRewarded(true);
+ config.setHasEndCard(false);
+ RewardedExt rewardedExt = new RewardedExt(null, new RewardedCompletionRules(null, null, null, "bannerEvent", null, "endCardEvent"), new RewardedClosingRules());
+
+ RewardManager rewardManager = new RewardManager();
+ rewardManager.setRewardedExt(rewardedExt);
+ config.setRewardManager(rewardManager);
+
+ HTMLCreative.rewardedTracking(mockWebView, config);
+
+ ArgumentCaptor captor = ArgumentCaptor.forClass(ActionUrl.class);
+ verify(mockWebView).setActionUrl(captor.capture());
+
+ assertEquals("bannerEvent", captor.getValue().url);
+ }
+
+ @Test
+ public void rewardedTracking_withEndCard() {
+ PrebidWebViewBase mockWebView = mock(PrebidWebViewBase.class);
+ AdUnitConfiguration config = new AdUnitConfiguration();
+ config.setRewarded(true);
+ config.setHasEndCard(true);
+ RewardedExt rewardedExt = new RewardedExt(null, new RewardedCompletionRules(null, null, null, "bannerEvent", null, "endCardEvent"), new RewardedClosingRules());
+
+ RewardManager rewardManager = new RewardManager();
+ rewardManager.setRewardedExt(rewardedExt);
+ config.setRewardManager(rewardManager);
+
+ HTMLCreative.rewardedTracking(mockWebView, config);
+
+ ArgumentCaptor captor = ArgumentCaptor.forClass(ActionUrl.class);
+ verify(mockWebView).setActionUrl(captor.capture());
+
+ assertEquals("endCardEvent", captor.getValue().url);
+ }
+
}
diff --git a/PrebidMobile/PrebidMobile-core/src/test/java/org/prebid/mobile/rendering/models/openrtb/bidRequests/ImpTest.java b/PrebidMobile/PrebidMobile-core/src/test/java/org/prebid/mobile/rendering/models/openrtb/bidRequests/ImpTest.java
index 33eddc32c..fe5c8c889 100644
--- a/PrebidMobile/PrebidMobile-core/src/test/java/org/prebid/mobile/rendering/models/openrtb/bidRequests/ImpTest.java
+++ b/PrebidMobile/PrebidMobile-core/src/test/java/org/prebid/mobile/rendering/models/openrtb/bidRequests/ImpTest.java
@@ -16,8 +16,6 @@
package org.prebid.mobile.rendering.models.openrtb.bidRequests;
-import static org.junit.Assert.assertEquals;
-
import org.json.JSONException;
import org.json.JSONObject;
import org.junit.Test;
@@ -25,6 +23,8 @@
import org.prebid.mobile.rendering.models.openrtb.bidRequests.imps.Pmp;
import org.prebid.mobile.rendering.models.openrtb.bidRequests.imps.Video;
+import static org.junit.Assert.assertEquals;
+
public class ImpTest {
@Test
@@ -41,9 +41,10 @@ public void getJsonObject() throws Exception {
imp.video = new Video();
imp.pmp = new Pmp();
imp.clickBrowser = 0;
+ imp.rewarded = 1;
JSONObject actualObj = imp.getJsonObject();
- String expectedString = "{\"clickbrowser\":0,\"pmp\":{},\"tagid\":\"tagid\",\"displaymanager\":\"prebid\",\"displaymanagerver\":\"1.0\",\"banner\":{},\"video\":{},\"secure\":0,\"instl\":1}";
+ String expectedString = "{\"rwdd\":1,\"clickbrowser\":0,\"pmp\":{},\"tagid\":\"tagid\",\"displaymanager\":\"prebid\",\"displaymanagerver\":\"1.0\",\"banner\":{},\"video\":{},\"secure\":0,\"instl\":1}";
assertEquals("got: " + actualObj.toString(), expectedString, actualObj.toString());
imp.getJsonObject();
}
diff --git a/PrebidMobile/PrebidMobile-core/src/test/java/org/prebid/mobile/rendering/networking/parameters/BasicParameterBuilderTest.java b/PrebidMobile/PrebidMobile-core/src/test/java/org/prebid/mobile/rendering/networking/parameters/BasicParameterBuilderTest.java
index 4a3ae0bc7..fbb78c2a4 100644
--- a/PrebidMobile/PrebidMobile-core/src/test/java/org/prebid/mobile/rendering/networking/parameters/BasicParameterBuilderTest.java
+++ b/PrebidMobile/PrebidMobile-core/src/test/java/org/prebid/mobile/rendering/networking/parameters/BasicParameterBuilderTest.java
@@ -16,24 +16,10 @@
package org.prebid.mobile.rendering.networking.parameters;
-import static org.junit.Assert.assertArrayEquals;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-import static org.prebid.mobile.rendering.networking.parameters.BasicParameterBuilder.KEY_OM_PARTNER_NAME;
-import static org.prebid.mobile.rendering.networking.parameters.BasicParameterBuilder.KEY_OM_PARTNER_VERSION;
-import static org.prebid.mobile.rendering.networking.parameters.BasicParameterBuilder.VIDEO_INTERSTITIAL_PLAYBACK_END;
-
import android.app.Activity;
import android.content.Context;
import android.content.res.Configuration;
-
import com.google.common.collect.Sets;
-
import org.assertj.core.util.Lists;
import org.json.JSONArray;
import org.json.JSONException;
@@ -42,16 +28,7 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.prebid.mobile.AdSize;
-import org.prebid.mobile.BannerAdUnit;
-import org.prebid.mobile.BannerParameters;
-import org.prebid.mobile.DataObject;
-import org.prebid.mobile.ExternalUserId;
-import org.prebid.mobile.NativeTitleAsset;
-import org.prebid.mobile.PrebidMobile;
-import org.prebid.mobile.Signals;
-import org.prebid.mobile.TargetingParams;
-import org.prebid.mobile.VideoParameters;
+import org.prebid.mobile.*;
import org.prebid.mobile.api.data.AdFormat;
import org.prebid.mobile.api.data.AdUnitFormat;
import org.prebid.mobile.api.rendering.pluginrenderer.PrebidMobilePluginRegister;
@@ -62,11 +39,7 @@
import org.prebid.mobile.rendering.models.AdPosition;
import org.prebid.mobile.rendering.models.PlacementType;
import org.prebid.mobile.rendering.models.openrtb.BidRequest;
-import org.prebid.mobile.rendering.models.openrtb.bidRequests.Ext;
-import org.prebid.mobile.rendering.models.openrtb.bidRequests.Imp;
-import org.prebid.mobile.rendering.models.openrtb.bidRequests.Native;
-import org.prebid.mobile.rendering.models.openrtb.bidRequests.PluginRendererList;
-import org.prebid.mobile.rendering.models.openrtb.bidRequests.User;
+import org.prebid.mobile.rendering.models.openrtb.bidRequests.*;
import org.prebid.mobile.rendering.models.openrtb.bidRequests.devices.Geo;
import org.prebid.mobile.rendering.models.openrtb.bidRequests.imps.Banner;
import org.prebid.mobile.rendering.models.openrtb.bidRequests.imps.Video;
@@ -80,13 +53,10 @@
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Calendar;
-import java.util.EnumSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
+import java.util.*;
+
+import static org.junit.Assert.*;
+import static org.prebid.mobile.rendering.networking.parameters.BasicParameterBuilder.*;
@RunWith(RobolectricTestRunner.class)
@Config(sdk = 19, qualifiers = "w1920dp-h1080dp")
@@ -1060,6 +1030,34 @@ public void instlParameter_interstitial() {
}
}
+
+ @Test
+ public void setRewarded_rwdd1() throws JSONException {
+ AdUnitConfiguration config = new AdUnitConfiguration();
+ config.setRewarded(true);
+
+ JSONObject impJson = getImpJson(config);
+
+ assertEquals(1, impJson.optInt("rwdd"));
+ }
+
+ @Test
+ public void notRewarded_rwdd0() throws JSONException {
+ AdUnitConfiguration config = new AdUnitConfiguration();
+
+ JSONObject impJson = getImpJson(config);
+
+ assertEquals(-1, (impJson.optInt("rwdd", -1)));
+ }
+
+ private JSONObject getImpJson(AdUnitConfiguration config) throws JSONException {
+ AdRequestInput adRequestInput = new AdRequestInput();
+ BasicParameterBuilder builder = new BasicParameterBuilder(config, context.getResources(), false);
+ builder.appendBuilderParameters(adRequestInput);
+
+ return adRequestInput.getBidRequest().getImp().get(0).getJsonObject();
+ }
+
private BidRequest getActualRequest(AdUnitConfiguration config) {
BasicParameterBuilder builder = new BasicParameterBuilder(
config,
diff --git a/PrebidMobile/PrebidMobile-core/src/test/java/org/prebid/mobile/rendering/video/AdViewProgressUpdateTaskTest.java b/PrebidMobile/PrebidMobile-core/src/test/java/org/prebid/mobile/rendering/video/AdViewProgressUpdateTaskTest.java
index e7ce1d288..2662dcddc 100644
--- a/PrebidMobile/PrebidMobile-core/src/test/java/org/prebid/mobile/rendering/video/AdViewProgressUpdateTaskTest.java
+++ b/PrebidMobile/PrebidMobile-core/src/test/java/org/prebid/mobile/rendering/video/AdViewProgressUpdateTaskTest.java
@@ -21,6 +21,11 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.prebid.mobile.api.exceptions.AdException;
+import org.prebid.mobile.configuration.AdUnitConfiguration;
+import org.prebid.mobile.rendering.interstitial.rewarded.RewardManager;
+import org.prebid.mobile.rendering.interstitial.rewarded.RewardedClosingRules;
+import org.prebid.mobile.rendering.interstitial.rewarded.RewardedCompletionRules;
+import org.prebid.mobile.rendering.interstitial.rewarded.RewardedExt;
import org.prebid.mobile.test.utils.WhiteBox;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
@@ -46,7 +51,7 @@ public void setup() throws AdException {
when(mockVideoCreative.getCreativeView()).thenReturn(mockCreativeView);
- adViewProgressTask = spy(new AdViewProgressUpdateTask(mockVideoCreative, duration));
+ adViewProgressTask = spy(new AdViewProgressUpdateTask(mockVideoCreative, duration, new AdUnitConfiguration()));
}
@Test
@@ -151,4 +156,84 @@ public void whenThirdQuartilePassedAndNoEventsTracked_TrackAllEvents() {
verify(mockVideoCreative).onEvent(VideoAdEvent.Event.AD_MIDPOINT);
verify(mockVideoCreative).onEvent(VideoAdEvent.Event.AD_THIRDQUARTILE);
}
+
+
+ @Test
+ public void getVideoLengthPercentageForReward_notRewarded() {
+ int videoDuration = 10_000;
+ AdUnitConfiguration config = new AdUnitConfiguration();
+ config.setRewarded(false);
+
+ Integer result = AdViewProgressUpdateTask.getVideoLengthPercentageForReward(videoDuration, config);
+
+ assertNull(result);
+ }
+
+ @Test
+ public void getVideoLengthPercentageForReward_withEndCard() {
+ int videoDuration = 10_000;
+ AdUnitConfiguration config = new AdUnitConfiguration();
+ config.setRewarded(true);
+ config.setHasEndCard(true);
+
+ Integer result = AdViewProgressUpdateTask.getVideoLengthPercentageForReward(videoDuration, config);
+
+ assertNull(result);
+ }
+
+ @Test
+ public void getVideoLengthPercentageForReward_1() {
+ testReward(10_000, null, RewardedCompletionRules.PlaybackEvent.START, 1);
+ }
+
+ @Test
+ public void getVideoLengthPercentageForReward_2() {
+ testReward(10_000, null, RewardedCompletionRules.PlaybackEvent.FIRST_QUARTILE, 25);
+ }
+
+ @Test
+ public void getVideoLengthPercentageForReward_3() {
+ testReward(10_000, null, RewardedCompletionRules.PlaybackEvent.MIDPOINT, 50);
+ }
+
+ @Test
+ public void getVideoLengthPercentageForReward_4() {
+ testReward(10_000, null, RewardedCompletionRules.PlaybackEvent.THIRD_QUARTILE, 75);
+ }
+
+ @Test
+ public void getVideoLengthPercentageForReward_5() {
+ testReward(10_000, null, RewardedCompletionRules.PlaybackEvent.COMPLETE, 100);
+ }
+
+ @Test
+ public void getVideoLengthPercentageForReward_time() {
+ testReward(10_000, 7, null, 70);
+ }
+
+ @Test
+ public void getVideoLengthPercentageForReward_wrongTime_1() {
+ testReward(10_000, 25, null, 100);
+ }
+
+ @Test
+ public void getVideoLengthPercentageForReward_wrongTime_2() {
+ testReward(10_000, -1, null, 100);
+ }
+
+ private void testReward(int videoDuration, Integer videoTime, RewardedCompletionRules.PlaybackEvent event, Integer expected) {
+ AdUnitConfiguration config = new AdUnitConfiguration();
+ config.setRewarded(true);
+ config.setHasEndCard(false);
+
+ RewardedExt rewardedExt = new RewardedExt(null, new RewardedCompletionRules(null, videoTime, null, null, event, null), new RewardedClosingRules());
+ RewardManager rewardManager = new RewardManager();
+ rewardManager.setRewardedExt(rewardedExt);
+ config.setRewardManager(rewardManager);
+
+ Integer result = AdViewProgressUpdateTask.getVideoLengthPercentageForReward(videoDuration, config);
+
+ assertEquals(expected, result);
+ }
+
}
\ No newline at end of file
diff --git a/PrebidMobile/PrebidMobile-core/src/test/java/org/prebid/mobile/rendering/video/VideoCreativeTest.java b/PrebidMobile/PrebidMobile-core/src/test/java/org/prebid/mobile/rendering/video/VideoCreativeTest.java
index 8f2c1f3f7..be893c2ea 100644
--- a/PrebidMobile/PrebidMobile-core/src/test/java/org/prebid/mobile/rendering/video/VideoCreativeTest.java
+++ b/PrebidMobile/PrebidMobile-core/src/test/java/org/prebid/mobile/rendering/video/VideoCreativeTest.java
@@ -83,7 +83,7 @@ public void displayTest() throws Exception {
@Test
public void initNativeAdSessionManagerSuccessTest() throws Exception {
- videoCreative.videoCreativeView = new VideoCreativeView(context, videoCreative);
+ videoCreative.videoCreativeView = new VideoCreativeView(context, videoCreative, new AdUnitConfiguration());
VideoCreative spyVideoCreative = spy(videoCreative);
spyVideoCreative.createOmAdSession();
diff --git a/PrebidMobile/PrebidMobile-core/src/test/java/org/prebid/mobile/rendering/video/VideoCreativeViewTest.java b/PrebidMobile/PrebidMobile-core/src/test/java/org/prebid/mobile/rendering/video/VideoCreativeViewTest.java
index 4ce5b9e10..6b50b9bbd 100644
--- a/PrebidMobile/PrebidMobile-core/src/test/java/org/prebid/mobile/rendering/video/VideoCreativeViewTest.java
+++ b/PrebidMobile/PrebidMobile-core/src/test/java/org/prebid/mobile/rendering/video/VideoCreativeViewTest.java
@@ -24,6 +24,7 @@
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.prebid.mobile.api.exceptions.AdException;
+import org.prebid.mobile.configuration.AdUnitConfiguration;
import org.prebid.mobile.test.utils.WhiteBox;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
@@ -46,7 +47,7 @@ public void setUp() throws AdException {
Context context = Robolectric.buildActivity(Activity.class).create().get();
mockCreative = Mockito.mock(VideoCreative.class);
- videoCreativeView = new VideoCreativeView(context, mockCreative);
+ videoCreativeView = new VideoCreativeView(context, mockCreative, new AdUnitConfiguration());
}
@Test
diff --git a/PrebidMobile/PrebidMobile-core/src/test/java/org/prebid/mobile/rendering/views/interstitial/InterstitialVideoTest.java b/PrebidMobile/PrebidMobile-core/src/test/java/org/prebid/mobile/rendering/views/interstitial/InterstitialVideoTest.java
index 2b667e297..26b5c4dac 100644
--- a/PrebidMobile/PrebidMobile-core/src/test/java/org/prebid/mobile/rendering/views/interstitial/InterstitialVideoTest.java
+++ b/PrebidMobile/PrebidMobile-core/src/test/java/org/prebid/mobile/rendering/views/interstitial/InterstitialVideoTest.java
@@ -27,6 +27,10 @@
import org.prebid.mobile.api.rendering.InterstitialView;
import org.prebid.mobile.configuration.AdUnitConfiguration;
import org.prebid.mobile.reflection.Reflection;
+import org.prebid.mobile.rendering.interstitial.rewarded.RewardManager;
+import org.prebid.mobile.rendering.interstitial.rewarded.RewardedClosingRules;
+import org.prebid.mobile.rendering.interstitial.rewarded.RewardedCompletionRules;
+import org.prebid.mobile.rendering.interstitial.rewarded.RewardedExt;
import org.prebid.mobile.rendering.models.AbstractCreative;
import org.prebid.mobile.rendering.models.InterstitialDisplayPropertiesInternal;
import org.prebid.mobile.rendering.video.VideoCreativeModel;
@@ -37,8 +41,7 @@
import java.util.Timer;
import java.util.TimerTask;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
public class InterstitialVideoTest {
@@ -85,7 +88,7 @@ public void scheduleShowCloseBtnTask_WithDefinedOffset_TimerIsScheduledWithOffse
spyInterstitialVideo.scheduleShowCloseBtnTask(mockAdView);
assertTrue(spyInterstitialVideo.shouldShowCloseButtonOnComplete());
- verify(spyInterstitialVideo, times(1)).scheduleTimer(eq(7000L));
+ verify(spyInterstitialVideo, times(1)).scheduleAllTimers(eq(7000L));
}
@Test
@@ -97,7 +100,7 @@ public void scheduleShowCloseBtnTask_ForShortVideo_NoTimerScheduled() {
spyInterstitialVideo.scheduleShowCloseBtnTask(mockAdView);
assertTrue(spyInterstitialVideo.shouldShowCloseButtonOnComplete());
- verify(spyInterstitialVideo, never()).scheduleTimer(anyLong());
+ verify(spyInterstitialVideo, never()).scheduleAllTimers(anyLong());
}
@Test
@@ -109,7 +112,7 @@ public void scheduleShowCloseBtnTask_ForZeroDuration_NoTimerScheduledAndShowClos
spyInterstitialVideo.scheduleShowCloseBtnTask(mockAdView);
assertTrue(spyInterstitialVideo.shouldShowCloseButtonOnComplete());
- verify(spyInterstitialVideo, never()).scheduleTimer(anyLong());
+ verify(spyInterstitialVideo, never()).scheduleAllTimers(anyLong());
}
@Test
@@ -118,7 +121,7 @@ public void whenGetMediaOffsetValue_ShowCloseButtonAfterPeriod() {
spyInterstitialVideo.scheduleShowCloseBtnTask(mockAdView);
- verify(spyInterstitialVideo).scheduleTimer(7000L);
+ verify(spyInterstitialVideo).scheduleAllTimers(7000L);
}
@Test
@@ -133,7 +136,7 @@ public void videoPausedTest() {
public void scheduleShowCloseBtnAfterPauseTest() throws IllegalAccessException {
Timer mockTimer = mock(Timer.class);
TimerTask mockTimerTask = mock(TimerTask.class);
- WhiteBox.field(InterstitialVideo.class, "currentTimerTask").set(spyInterstitialVideo, mockTimerTask);
+ WhiteBox.field(InterstitialVideo.class, "showCloseButtonTask").set(spyInterstitialVideo, mockTimerTask);
WhiteBox.field(InterstitialVideo.class, "timer").set(spyInterstitialVideo, mockTimer);
spyInterstitialVideo.pauseVideo();
@@ -141,7 +144,7 @@ public void scheduleShowCloseBtnAfterPauseTest() throws IllegalAccessException {
verify(mockTimer, times(1)).cancel();
verify(mockTimer, times(1)).purge();
verify(mockTimerTask, times(1)).cancel();
- verify(spyInterstitialVideo, never()).scheduleTimer(anyLong());
+ verify(spyInterstitialVideo, never()).scheduleAllTimers(anyLong());
}
@Test
@@ -152,7 +155,7 @@ public void scheduleShowCloseBtnAfterResumeTest() {
spyInterstitialVideo.resumeVideo();
assertFalse(spyInterstitialVideo.shouldShowCloseButtonOnComplete());
- verify(spyInterstitialVideo, times(1)).scheduleTimer(5 * 1000L);
+ verify(spyInterstitialVideo, times(1)).scheduleAllTimers(5 * 1000L);
}
@Test
@@ -205,7 +208,7 @@ public void whenAllOffsetsPresent_UseSscOffset() throws Exception {
when(mockAdView.getMediaOffset()).thenReturn(adViewManager.getSkipOffset());
spyInterstitialVideo.scheduleShowCloseBtnTask(mockAdView);
- verify(spyInterstitialVideo).scheduleTimer(10L * 1000);
+ verify(spyInterstitialVideo).scheduleAllTimers(10L * 1000);
}
@Test
@@ -229,7 +232,7 @@ public void whenVastAndSscOffsetPresent_UseSscOffset() throws Exception {
when(mockAdView.getMediaOffset()).thenReturn(adViewManager.getSkipOffset());
spyInterstitialVideo.scheduleShowCloseBtnTask(mockAdView);
- verify(spyInterstitialVideo).scheduleTimer(10L * 1000);
+ verify(spyInterstitialVideo).scheduleAllTimers(10L * 1000);
}
@Test
@@ -256,13 +259,13 @@ public void whenRemainingTimePresent_UseRemainingTime() throws Exception {
spyInterstitialVideo.setRemainingTimeInMs(3000);
spyInterstitialVideo.scheduleShowCloseBtnTask(mockAdView, 3000);
- verify(spyInterstitialVideo).scheduleTimer(3L * 1000);
+ verify(spyInterstitialVideo).scheduleAllTimers(3L * 1000);
}
@Test
public void whenNoOffsetPresent_UseDefaultOffset() {
spyInterstitialVideo.scheduleShowCloseBtnTask(mockAdView);
- verify(spyInterstitialVideo).scheduleTimer(10L * 1000);
+ verify(spyInterstitialVideo).scheduleAllTimers(10L * 1000);
}
private void mockMediaDuration(long duration) {
@@ -312,7 +315,7 @@ public void scheduleShowCloseBtnTask_VideoDurationLessThanSkipDelay_CallSchedule
spyInterstitialVideo.scheduleShowButtonTask();
- verify(spyInterstitialVideo).scheduleTimer(videoDuration);
+ verify(spyInterstitialVideo).scheduleAllTimers(videoDuration);
}
@Test
@@ -324,7 +327,137 @@ public void scheduleShowCloseBtnTask_VideoDurationBiggerThanSkipDelay_CallSchedu
spyInterstitialVideo.scheduleShowButtonTask();
- verify(spyInterstitialVideo).scheduleTimer(skipDelay);
+ verify(spyInterstitialVideo).scheduleAllTimers(skipDelay);
+ }
+
+
+ @Test
+ public void rewarded_getTimeToReward_default() {
+ RewardedExt rewardedExt = RewardedExt.defaultExt();
+ AdUnitConfiguration mockConfig = mockAdConfiguration;
+
+ RewardManager mockRewardManager = mock(RewardManager.class);
+ when(mockConfig.getRewardManager()).thenReturn(mockRewardManager);
+ when(mockRewardManager.getRewardedExt()).thenReturn(rewardedExt);
+
+ Integer timeToReward = InterstitialVideo.getTimeToReward(10_000, mockConfig);
+
+ assertNull(timeToReward);
+ }
+
+ @Test
+ public void rewarded_getTimeToReward_time() {
+ RewardedExt rewardedExt = mock(RewardedExt.class);
+ AdUnitConfiguration mockConfig = mockAdConfiguration;
+
+ RewardManager mockRewardManager = mock(RewardManager.class);
+ when(mockConfig.getRewardManager()).thenReturn(mockRewardManager);
+ when(mockRewardManager.getRewardedExt()).thenReturn(rewardedExt);
+
+ RewardedCompletionRules mockRules = mock(RewardedCompletionRules.class);
+ when(mockRules.getVideoEvent()).thenReturn(null);
+ when(mockRules.getVideoTime()).thenReturn(6);
+ when(rewardedExt.getCompletionRules()).thenReturn(mockRules);
+
+ Integer timeToReward = InterstitialVideo.getTimeToReward(10_000, mockConfig);
+
+ assertEquals(Integer.valueOf(6_000), timeToReward);
+ }
+
+ @Test
+ public void rewarded_getTimeToReward_completionEvent_1() {
+ testCompletionEvent(RewardedCompletionRules.PlaybackEvent.COMPLETE, 10_000);
+ }
+
+ @Test
+ public void rewarded_getTimeToReward_completionEvent_2() {
+ testCompletionEvent(RewardedCompletionRules.PlaybackEvent.THIRD_QUARTILE, 7_500);
+ }
+
+ @Test
+ public void rewarded_getTimeToReward_completionEvent_3() {
+ testCompletionEvent(RewardedCompletionRules.PlaybackEvent.MIDPOINT, 5_000);
+ }
+
+ @Test
+ public void rewarded_getTimeToReward_completionEvent_4() {
+ testCompletionEvent(RewardedCompletionRules.PlaybackEvent.FIRST_QUARTILE, 2_500);
+ }
+
+ @Test
+ public void rewarded_getTimeToReward_completionEvent_5() {
+ testCompletionEvent(RewardedCompletionRules.PlaybackEvent.START, 0);
+ }
+
+ private void testCompletionEvent(RewardedCompletionRules.PlaybackEvent event, int expected) {
+ RewardedExt rewardedExt = mock(RewardedExt.class);
+ AdUnitConfiguration mockConfig = mockAdConfiguration;
+ RewardManager mockRewardManager = mock(RewardManager.class);
+ when(mockConfig.getRewardManager()).thenReturn(mockRewardManager);
+ when(mockRewardManager.getRewardedExt()).thenReturn(rewardedExt);
+ when(rewardedExt.getCompletionRules()).thenReturn(new RewardedCompletionRules(11, 12, 13, "1", event, "2"));
+
+ Integer timeToReward = InterstitialVideo.getTimeToReward(10_000, mockConfig);
+
+ assertEquals(Integer.valueOf(expected), timeToReward);
+ }
+
+
+ @Test
+ public void rewarded_getDelayToShowCloseButton_1() {
+ int videoDuration = 10_000;
+ int rewardTimeSeconds = 5;
+ int postRewardTimeSeconds = 0;
+ int expected = 5_000;
+ testCloseButtonDelay(videoDuration, rewardTimeSeconds, postRewardTimeSeconds, expected);
+ }
+
+ @Test
+ public void rewarded_getDelayToShowCloseButton_2() {
+ int videoDuration = 10_000;
+ int rewardTimeSeconds = 15;
+ int postRewardTimeSeconds = 0;
+ int expected = 10_000;
+ testCloseButtonDelay(videoDuration, rewardTimeSeconds, postRewardTimeSeconds, expected);
+ }
+
+ @Test
+ public void rewarded_getDelayToShowCloseButton_3() {
+ int videoDuration = 10_000;
+ int rewardTimeSeconds = 5;
+ int postRewardTimeSeconds = 2;
+ int expected = 7_000;
+ testCloseButtonDelay(videoDuration, rewardTimeSeconds, postRewardTimeSeconds, expected);
+ }
+
+ @Test
+ public void rewarded_getDelayToShowCloseButton_4() {
+ int videoDuration = 10_000;
+ int rewardTimeSeconds = 5;
+ int postRewardTimeSeconds = 7;
+ int expected = 10_000;
+ testCloseButtonDelay(videoDuration, rewardTimeSeconds, postRewardTimeSeconds, expected);
+ }
+
+ private void testCloseButtonDelay(int videoDuration, int rewardTime, int postRewardTime, int expected) {
+ RewardedExt rewardedExt = mock(RewardedExt.class);
+ AdUnitConfiguration mockConfig = mockAdConfiguration;
+ RewardManager mockRewardManager = mock(RewardManager.class);
+ when(mockConfig.getRewardManager()).thenReturn(mockRewardManager);
+ when(mockRewardManager.getRewardedExt()).thenReturn(rewardedExt);
+
+ RewardedCompletionRules mockCompletionRules = mock(RewardedCompletionRules.class);
+ when(mockCompletionRules.getVideoEvent()).thenReturn(null);
+ when(mockCompletionRules.getVideoTime()).thenReturn(rewardTime);
+ RewardedClosingRules mockClosingRules = mock(RewardedClosingRules.class);
+ when(mockClosingRules.getPostRewardTime()).thenReturn(postRewardTime);
+
+ when(rewardedExt.getCompletionRules()).thenReturn(mockCompletionRules);
+ when(rewardedExt.getClosingRules()).thenReturn(mockClosingRules);
+
+ Integer timeToReward = InterstitialVideo.getDelayToShowCloseButton(videoDuration, mockConfig);
+
+ assertEquals(Integer.valueOf(expected), timeToReward);
}
}
\ No newline at end of file
diff --git a/PrebidMobile/PrebidMobile-core/src/test/resources/RewardedExt/close_type_1.json b/PrebidMobile/PrebidMobile-core/src/test/resources/RewardedExt/close_type_1.json
new file mode 100644
index 000000000..f74a82052
--- /dev/null
+++ b/PrebidMobile/PrebidMobile-core/src/test/resources/RewardedExt/close_type_1.json
@@ -0,0 +1,14 @@
+{
+ "prebid": {
+ "passthrough": [
+ {
+ "type": "prebidmobilesdk",
+ "rwdd": {
+ "close": {
+ "action": "autoclose"
+ }
+ }
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/PrebidMobile/PrebidMobile-core/src/test/resources/RewardedExt/close_type_2.json b/PrebidMobile/PrebidMobile-core/src/test/resources/RewardedExt/close_type_2.json
new file mode 100644
index 000000000..c0a67526a
--- /dev/null
+++ b/PrebidMobile/PrebidMobile-core/src/test/resources/RewardedExt/close_type_2.json
@@ -0,0 +1,14 @@
+{
+ "prebid": {
+ "passthrough": [
+ {
+ "type": "prebidmobilesdk",
+ "rwdd": {
+ "close": {
+ "action": "closebutton"
+ }
+ }
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/PrebidMobile/PrebidMobile-core/src/test/resources/RewardedExt/full.json b/PrebidMobile/PrebidMobile-core/src/test/resources/RewardedExt/full.json
new file mode 100644
index 000000000..4e592d6c0
--- /dev/null
+++ b/PrebidMobile/PrebidMobile-core/src/test/resources/RewardedExt/full.json
@@ -0,0 +1,33 @@
+{
+ "prebid": {
+ "passthrough": [
+ {
+ "type": "prebidmobilesdk",
+ "rwdd": {
+ "reward": {
+ "type": "SuperDollars",
+ "count": 11
+ },
+ "completion": {
+ "banner": {
+ "time": 12,
+ "event": "banner_event"
+ },
+ "video": {
+ "time": 13,
+ "playbackevent": "complete",
+ "endcard": {
+ "time": 14,
+ "event": "endcard_event"
+ }
+ }
+ },
+ "close": {
+ "postrewardtime": 15,
+ "action": "autoclose"
+ }
+ }
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/PrebidMobile/PrebidMobile-core/src/test/resources/RewardedExt/playbackevent_complete.json b/PrebidMobile/PrebidMobile-core/src/test/resources/RewardedExt/playbackevent_complete.json
new file mode 100644
index 000000000..0669590df
--- /dev/null
+++ b/PrebidMobile/PrebidMobile-core/src/test/resources/RewardedExt/playbackevent_complete.json
@@ -0,0 +1,16 @@
+{
+ "prebid": {
+ "passthrough": [
+ {
+ "type": "prebidmobilesdk",
+ "rwdd": {
+ "completion": {
+ "video": {
+ "playbackevent": "complete"
+ }
+ }
+ }
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/PrebidMobile/PrebidMobile-core/src/test/resources/RewardedExt/playbackevent_firstquartile.json b/PrebidMobile/PrebidMobile-core/src/test/resources/RewardedExt/playbackevent_firstquartile.json
new file mode 100644
index 000000000..eb558be14
--- /dev/null
+++ b/PrebidMobile/PrebidMobile-core/src/test/resources/RewardedExt/playbackevent_firstquartile.json
@@ -0,0 +1,16 @@
+{
+ "prebid": {
+ "passthrough": [
+ {
+ "type": "prebidmobilesdk",
+ "rwdd": {
+ "completion": {
+ "video": {
+ "playbackevent": "firstquartile"
+ }
+ }
+ }
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/PrebidMobile/PrebidMobile-core/src/test/resources/RewardedExt/playbackevent_midpoint.json b/PrebidMobile/PrebidMobile-core/src/test/resources/RewardedExt/playbackevent_midpoint.json
new file mode 100644
index 000000000..5359cf7e0
--- /dev/null
+++ b/PrebidMobile/PrebidMobile-core/src/test/resources/RewardedExt/playbackevent_midpoint.json
@@ -0,0 +1,16 @@
+{
+ "prebid": {
+ "passthrough": [
+ {
+ "type": "prebidmobilesdk",
+ "rwdd": {
+ "completion": {
+ "video": {
+ "playbackevent": "midpoint"
+ }
+ }
+ }
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/PrebidMobile/PrebidMobile-core/src/test/resources/RewardedExt/playbackevent_start.json b/PrebidMobile/PrebidMobile-core/src/test/resources/RewardedExt/playbackevent_start.json
new file mode 100644
index 000000000..aef5cde1b
--- /dev/null
+++ b/PrebidMobile/PrebidMobile-core/src/test/resources/RewardedExt/playbackevent_start.json
@@ -0,0 +1,16 @@
+{
+ "prebid": {
+ "passthrough": [
+ {
+ "type": "prebidmobilesdk",
+ "rwdd": {
+ "completion": {
+ "video": {
+ "playbackevent": "start"
+ }
+ }
+ }
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/PrebidMobile/PrebidMobile-core/src/test/resources/RewardedExt/playbackevent_thirdquartile.json b/PrebidMobile/PrebidMobile-core/src/test/resources/RewardedExt/playbackevent_thirdquartile.json
new file mode 100644
index 000000000..8a613c828
--- /dev/null
+++ b/PrebidMobile/PrebidMobile-core/src/test/resources/RewardedExt/playbackevent_thirdquartile.json
@@ -0,0 +1,16 @@
+{
+ "prebid": {
+ "passthrough": [
+ {
+ "type": "prebidmobilesdk",
+ "rwdd": {
+ "completion": {
+ "video": {
+ "playbackevent": "thirdquartile"
+ }
+ }
+ }
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/PrebidMobile/PrebidMobile-core/src/test/resources/RewardedExt/reward_with_ext.json b/PrebidMobile/PrebidMobile-core/src/test/resources/RewardedExt/reward_with_ext.json
new file mode 100644
index 000000000..c1f8c3a43
--- /dev/null
+++ b/PrebidMobile/PrebidMobile-core/src/test/resources/RewardedExt/reward_with_ext.json
@@ -0,0 +1,18 @@
+{
+ "prebid": {
+ "passthrough": [
+ {
+ "type": "prebidmobilesdk",
+ "rwdd": {
+ "reward": {
+ "type": "SuperDollars",
+ "count": 11,
+ "ext": {
+ "additional_data": "test"
+ }
+ }
+ }
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/PrebidMobile/PrebidMobile-gamEventHandlers/src/main/java/org/prebid/mobile/eventhandlers/GamRewardedEventHandler.java b/PrebidMobile/PrebidMobile-gamEventHandlers/src/main/java/org/prebid/mobile/eventhandlers/GamRewardedEventHandler.java
index ddab59222..43c050bd8 100644
--- a/PrebidMobile/PrebidMobile-gamEventHandlers/src/main/java/org/prebid/mobile/eventhandlers/GamRewardedEventHandler.java
+++ b/PrebidMobile/PrebidMobile-gamEventHandlers/src/main/java/org/prebid/mobile/eventhandlers/GamRewardedEventHandler.java
@@ -20,16 +20,16 @@
import android.app.Activity;
import android.os.Handler;
import android.os.Looper;
-
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-
+import com.google.android.gms.ads.rewarded.RewardItem;
import org.prebid.mobile.LogUtil;
import org.prebid.mobile.api.exceptions.AdException;
import org.prebid.mobile.eventhandlers.global.Constants;
import org.prebid.mobile.rendering.bidding.data.bid.Bid;
import org.prebid.mobile.rendering.bidding.interfaces.RewardedEventHandler;
import org.prebid.mobile.rendering.bidding.listeners.RewardedVideoEventListener;
+import org.prebid.mobile.rendering.interstitial.rewarded.Reward;
import java.lang.ref.WeakReference;
@@ -52,6 +52,12 @@ public class GamRewardedEventHandler implements RewardedEventHandler, GamAdEvent
private boolean isExpectingAppEvent;
private boolean didNotifiedBidWin;
+ /**
+ * Default constructor.
+ *
+ * @param activity Android activity
+ * @param gamAdUnitId the GAM ad unit id for the rewarded ad unit
+ */
public GamRewardedEventHandler(
Activity activity,
String gamAdUnitId
@@ -185,10 +191,19 @@ private void handleAppEventTimeout() {
listener.onAdServerWin(getRewardItem());
}
- private Object getRewardItem() {
+ private RewardItem getRewardItem() {
return rewardedAd != null ? rewardedAd.getRewardItem() : null;
}
+ @Nullable
+ @Override
+ public Reward getReward() {
+ RewardItem rewardItem = getRewardItem();
+ if (rewardItem == null) return null;
+
+ return new Reward(rewardItem.getType(), rewardItem.getAmount(), null);
+ }
+
private void notifyErrorListener(int errorCode) {
switch (errorCode) {
case Constants.ERROR_CODE_INTERNAL_ERROR:
diff --git a/PrebidMobile/PrebidMobile-gamEventHandlers/src/main/java/org/prebid/mobile/eventhandlers/RewardedAdWrapper.java b/PrebidMobile/PrebidMobile-gamEventHandlers/src/main/java/org/prebid/mobile/eventhandlers/RewardedAdWrapper.java
index f2d45b097..66b2646bd 100644
--- a/PrebidMobile/PrebidMobile-gamEventHandlers/src/main/java/org/prebid/mobile/eventhandlers/RewardedAdWrapper.java
+++ b/PrebidMobile/PrebidMobile-gamEventHandlers/src/main/java/org/prebid/mobile/eventhandlers/RewardedAdWrapper.java
@@ -16,16 +16,12 @@
package org.prebid.mobile.eventhandlers;
-import static org.prebid.mobile.eventhandlers.global.Constants.APP_EVENT;
-
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
-
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-
import com.google.android.gms.ads.AdError;
import com.google.android.gms.ads.FullScreenContentCallback;
import com.google.android.gms.ads.LoadAdError;
@@ -34,7 +30,6 @@
import com.google.android.gms.ads.rewarded.RewardItem;
import com.google.android.gms.ads.rewarded.RewardedAd;
import com.google.android.gms.ads.rewarded.RewardedAdLoadCallback;
-
import org.prebid.mobile.LogUtil;
import org.prebid.mobile.eventhandlers.utils.GamUtils;
import org.prebid.mobile.rendering.bidding.data.bid.Bid;
@@ -43,6 +38,8 @@
import java.util.HashMap;
import java.util.Map;
+import static org.prebid.mobile.eventhandlers.global.Constants.APP_EVENT;
+
/**
* Internal wrapper of rewarded ad from GAM SDK.
* To achieve safe integration between various GAM SDK versions we have to wrap all PublisherAdView method execution in try / catch.
@@ -173,7 +170,7 @@ public void show(Activity activity) {
}
}
- public Object getRewardItem() {
+ public RewardItem getRewardItem() {
return rewardedAd != null ? rewardedAd.getRewardItem() : null;
}
diff --git a/PrebidMobile/PrebidMobile-maxAdapters/src/main/java/com/applovin/mediation/adapters/PrebidMaxMediationAdapter.java b/PrebidMobile/PrebidMobile-maxAdapters/src/main/java/com/applovin/mediation/adapters/PrebidMaxMediationAdapter.java
index 32c73c260..39f5a35e8 100644
--- a/PrebidMobile/PrebidMobile-maxAdapters/src/main/java/com/applovin/mediation/adapters/PrebidMaxMediationAdapter.java
+++ b/PrebidMobile/PrebidMobile-maxAdapters/src/main/java/com/applovin/mediation/adapters/PrebidMaxMediationAdapter.java
@@ -3,7 +3,6 @@
import android.app.Activity;
import android.os.Handler;
import android.os.Looper;
-
import com.applovin.mediation.MaxAdFormat;
import com.applovin.mediation.adapter.MaxAdViewAdapter;
import com.applovin.mediation.adapter.MaxInterstitialAdapter;
@@ -18,8 +17,8 @@
import com.applovin.mediation.adapters.prebid.managers.MaxBannerManager;
import com.applovin.mediation.adapters.prebid.managers.MaxInterstitialManager;
import com.applovin.mediation.adapters.prebid.managers.MaxNativeManager;
+import com.applovin.mediation.adapters.prebid.managers.MaxRewardedManager;
import com.applovin.sdk.AppLovinSdk;
-
import org.prebid.mobile.PrebidMobile;
import org.prebid.mobile.TargetingParams;
@@ -34,6 +33,7 @@ public class PrebidMaxMediationAdapter extends MediationAdapterBase implements M
private MaxBannerManager maxBannerManager;
private MaxInterstitialManager maxInterstitialManager;
+ private MaxRewardedManager maxRewardedManager;
private MaxNativeManager maxNativeManager;
public PrebidMaxMediationAdapter(AppLovinSdk appLovinSdk) {
@@ -100,8 +100,8 @@ public void loadRewardedAd(
Activity activity,
MaxRewardedAdapterListener maxListener
) {
- maxInterstitialManager = new MaxInterstitialManager();
- maxInterstitialManager.loadAd(parameters, activity, maxListener);
+ maxRewardedManager = new MaxRewardedManager();
+ maxRewardedManager.loadAd(parameters, activity, maxListener);
}
@Override
@@ -120,7 +120,7 @@ public void showRewardedAd(
Activity activity,
MaxRewardedAdapterListener maxListener
) {
- maxInterstitialManager.showAd();
+ maxRewardedManager.showAd();
}
@@ -145,6 +145,10 @@ public void onDestroy() {
maxInterstitialManager.destroy();
}
+ if (maxRewardedManager != null) {
+ maxRewardedManager.destroy();
+ }
+
if (maxNativeManager != null) {
maxNativeManager.destroy();
}
diff --git a/PrebidMobile/PrebidMobile-maxAdapters/src/main/java/com/applovin/mediation/adapters/prebid/ListenersCreator.java b/PrebidMobile/PrebidMobile-maxAdapters/src/main/java/com/applovin/mediation/adapters/prebid/ListenersCreator.java
index f194205b8..ae9bb969f 100644
--- a/PrebidMobile/PrebidMobile-maxAdapters/src/main/java/com/applovin/mediation/adapters/prebid/ListenersCreator.java
+++ b/PrebidMobile/PrebidMobile-maxAdapters/src/main/java/com/applovin/mediation/adapters/prebid/ListenersCreator.java
@@ -1,10 +1,8 @@
package com.applovin.mediation.adapters.prebid;
-import com.applovin.impl.mediation.MaxRewardImpl;
import com.applovin.mediation.adapter.MaxAdapterError;
import com.applovin.mediation.adapter.listeners.MaxAdViewAdapterListener;
import com.applovin.mediation.adapter.listeners.MaxInterstitialAdapterListener;
-import com.applovin.mediation.adapter.listeners.MaxRewardedAdapterListener;
import org.prebid.mobile.api.exceptions.AdException;
import org.prebid.mobile.rendering.bidding.interfaces.InterstitialControllerListener;
import org.prebid.mobile.rendering.bidding.listeners.DisplayViewListener;
@@ -87,34 +85,4 @@ public void onInterstitialClosed() {
};
}
- public static InterstitialControllerListener createRewardedListener(MaxRewardedAdapterListener maxListener) {
- return new InterstitialControllerListener() {
- @Override
- public void onInterstitialReadyForDisplay() {
- maxListener.onRewardedAdLoaded();
- }
-
- @Override
- public void onInterstitialClicked() {
- maxListener.onRewardedAdClicked();
- }
-
- @Override
- public void onInterstitialFailedToLoad(AdException exception) {
- maxListener.onRewardedAdLoadFailed(new MaxAdapterError(2002, "Ad failed: " + exception.getMessage()));
- }
-
- @Override
- public void onInterstitialDisplayed() {
- maxListener.onRewardedAdDisplayed(); maxListener.onRewardedAdVideoStarted();
- }
-
- @Override
- public void onInterstitialClosed() {
- maxListener.onRewardedAdVideoCompleted(); maxListener.onRewardedAdHidden();
- maxListener.onUserRewarded(MaxRewardImpl.createDefault());
- }
- };
- }
-
}
diff --git a/PrebidMobile/PrebidMobile-maxAdapters/src/main/java/com/applovin/mediation/adapters/prebid/managers/MaxInterstitialManager.java b/PrebidMobile/PrebidMobile-maxAdapters/src/main/java/com/applovin/mediation/adapters/prebid/managers/MaxInterstitialManager.java
index fd44e7a72..91e84bb04 100644
--- a/PrebidMobile/PrebidMobile-maxAdapters/src/main/java/com/applovin/mediation/adapters/prebid/managers/MaxInterstitialManager.java
+++ b/PrebidMobile/PrebidMobile-maxAdapters/src/main/java/com/applovin/mediation/adapters/prebid/managers/MaxInterstitialManager.java
@@ -2,16 +2,12 @@
import android.app.Activity;
import android.util.Log;
-
import androidx.annotation.Nullable;
-
import com.applovin.mediation.adapter.MaxAdapterError;
import com.applovin.mediation.adapter.listeners.MaxInterstitialAdapterListener;
-import com.applovin.mediation.adapter.listeners.MaxRewardedAdapterListener;
import com.applovin.mediation.adapter.parameters.MaxAdapterResponseParameters;
import com.applovin.mediation.adapters.prebid.ListenersCreator;
import com.applovin.mediation.adapters.prebid.ParametersChecker;
-
import org.prebid.mobile.api.exceptions.AdException;
import org.prebid.mobile.rendering.bidding.display.InterstitialController;
import org.prebid.mobile.rendering.bidding.interfaces.InterstitialControllerListener;
@@ -20,10 +16,8 @@ public class MaxInterstitialManager {
private static final String TAG = MaxInterstitialManager.class.getSimpleName();
- private boolean isRewarded = false;
-
@Nullable
- private Object maxListener;
+ private MaxInterstitialAdapterListener maxListener;
@Nullable
private InterstitialController interstitialController;
@@ -33,17 +27,6 @@ public void loadAd(
MaxInterstitialAdapterListener maxListener
) {
this.maxListener = maxListener;
- isRewarded = false;
- loadAd(parameters, activity);
- }
-
- public void loadAd(
- MaxAdapterResponseParameters parameters,
- Activity activity,
- MaxRewardedAdapterListener maxListener
- ) {
- this.maxListener = maxListener;
- isRewarded = true;
loadAd(parameters, activity);
}
@@ -60,7 +43,7 @@ private void loadAd(
try {
InterstitialControllerListener listener = createPrebidListener();
interstitialController = new InterstitialController(activity, listener);
- interstitialController.loadAd(responseId, isRewarded);
+ interstitialController.loadAd(responseId, false);
} catch (AdException e) {
String error = "Exception in Prebid interstitial controller (" + e.getMessage() + ")";
Log.e(TAG, error);
@@ -72,16 +55,8 @@ private void loadAd(
public void showAd() {
if (interstitialController == null) {
MaxAdapterError error = new MaxAdapterError(2010, "InterstitialController is null");
- if (isRewarded) {
- MaxRewardedAdapterListener listener = getRewardedListener();
- if (listener != null) {
- listener.onRewardedAdDisplayFailed(error);
- }
- } else {
- MaxInterstitialAdapterListener listener = getInterstitialListener();
- if (listener != null) {
- listener.onInterstitialAdDisplayFailed(error);
- }
+ if (maxListener != null) {
+ maxListener.onInterstitialAdDisplayFailed(error);
}
return;
}
@@ -97,11 +72,7 @@ public void destroy() {
private InterstitialControllerListener createPrebidListener() {
if (maxListener != null) {
- if (isRewarded) {
- return ListenersCreator.createRewardedListener(getRewardedListener());
- } else {
- return ListenersCreator.createInterstitialListener(getInterstitialListener());
- }
+ return ListenersCreator.createInterstitialListener(maxListener);
} else {
Log.e(TAG, "Max interstitial listener must be not null!");
}
@@ -113,20 +84,8 @@ private void onError(
String error
) {
if (maxListener != null) {
- if (isRewarded) {
- getRewardedListener().onRewardedAdLoadFailed(new MaxAdapterError(code, error));
- } else {
- getInterstitialListener().onInterstitialAdLoadFailed(new MaxAdapterError(code, error));
- }
+ maxListener.onInterstitialAdLoadFailed(new MaxAdapterError(code, error));
} else Log.e(TAG, "Max interstitial listener must be not null!");
}
- private MaxInterstitialAdapterListener getInterstitialListener() {
- return ((MaxInterstitialAdapterListener) maxListener);
- }
-
- private MaxRewardedAdapterListener getRewardedListener() {
- return ((MaxRewardedAdapterListener) maxListener);
- }
-
}
diff --git a/PrebidMobile/PrebidMobile-maxAdapters/src/main/java/com/applovin/mediation/adapters/prebid/managers/MaxRewardedManager.java b/PrebidMobile/PrebidMobile-maxAdapters/src/main/java/com/applovin/mediation/adapters/prebid/managers/MaxRewardedManager.java
new file mode 100644
index 000000000..b4bb07e56
--- /dev/null
+++ b/PrebidMobile/PrebidMobile-maxAdapters/src/main/java/com/applovin/mediation/adapters/prebid/managers/MaxRewardedManager.java
@@ -0,0 +1,154 @@
+package com.applovin.mediation.adapters.prebid.managers;
+
+import android.app.Activity;
+import android.util.Log;
+import androidx.annotation.Nullable;
+import com.applovin.mediation.MaxReward;
+import com.applovin.mediation.adapter.MaxAdapterError;
+import com.applovin.mediation.adapter.listeners.MaxRewardedAdapterListener;
+import com.applovin.mediation.adapter.parameters.MaxAdapterResponseParameters;
+import com.applovin.mediation.adapters.prebid.ParametersChecker;
+import org.prebid.mobile.api.exceptions.AdException;
+import org.prebid.mobile.rendering.bidding.display.InterstitialController;
+import org.prebid.mobile.rendering.bidding.interfaces.InterstitialControllerListener;
+import org.prebid.mobile.rendering.interstitial.rewarded.Reward;
+
+public class MaxRewardedManager {
+
+ private static final String TAG = MaxRewardedManager.class.getSimpleName();
+
+ @Nullable
+ private MaxRewardedAdapterListener maxListener;
+ @Nullable
+ private InterstitialController interstitialController;
+ @Nullable
+ private InterstitialControllerListener prebidListener;
+
+ public void loadAd(
+ MaxAdapterResponseParameters parameters,
+ Activity activity,
+ MaxRewardedAdapterListener maxListener
+ ) {
+ this.maxListener = maxListener;
+ loadAd(parameters, activity);
+ }
+
+ private void loadAd(
+ MaxAdapterResponseParameters parameters,
+ Activity activity
+ ) {
+ String responseId = ParametersChecker.getResponseIdAndCheckKeywords(parameters, this::onError);
+ if (responseId == null) {
+ return;
+ }
+
+ activity.runOnUiThread(() -> {
+ try {
+ prebidListener = createRewardedListener();
+ interstitialController = new InterstitialController(activity, prebidListener);
+ if (prebidListener != null) {
+ interstitialController.setRewardListener(prebidListener::onUserEarnedReward);
+ }
+ interstitialController.loadAd(responseId, true);
+ } catch (AdException e) {
+ String error = "Exception in Prebid interstitial controller (" + e.getMessage() + ")";
+ Log.e(TAG, error);
+ onError(1006, error);
+ }
+ });
+ }
+
+ public void showAd() {
+ if (interstitialController == null) {
+ MaxAdapterError error = new MaxAdapterError(2010, "InterstitialController is null");
+ if (maxListener != null) {
+ maxListener.onRewardedAdDisplayFailed(error);
+ }
+ return;
+ }
+ interstitialController.show();
+ }
+
+ public void destroy() {
+ if (interstitialController != null) {
+ interstitialController.destroy();
+ }
+ }
+
+
+ private void onError(
+ int code,
+ String error
+ ) {
+ if (maxListener != null) {
+ maxListener.onRewardedAdLoadFailed(new MaxAdapterError(code, error));
+ } else Log.e(TAG, "Max interstitial listener must be not null!");
+ }
+
+
+ public InterstitialControllerListener createRewardedListener() {
+ return new InterstitialControllerListener() {
+ @Override
+ public void onInterstitialReadyForDisplay() {
+ if (maxListener != null) {
+ maxListener.onRewardedAdLoaded();
+ }
+ }
+
+ @Override
+ public void onInterstitialClicked() {
+ if (maxListener != null) {
+ maxListener.onRewardedAdClicked();
+ }
+ }
+
+ @Override
+ public void onInterstitialFailedToLoad(AdException exception) {
+ if (maxListener != null) {
+ maxListener.onRewardedAdLoadFailed(new MaxAdapterError(2002,
+ "Ad failed: " + exception.getMessage()
+ ));
+ }
+ }
+
+ @Override
+ public void onInterstitialDisplayed() {
+ if (maxListener != null) {
+ maxListener.onRewardedAdDisplayed();
+ }
+ }
+
+ @Override
+ public void onInterstitialClosed() {
+ if (maxListener != null) {
+ maxListener.onRewardedAdHidden();
+ }
+ }
+
+ @Override
+ public void onUserEarnedReward() {
+ if (maxListener != null && interstitialController != null) {
+ Reward reward = interstitialController.getReward();
+ maxListener.onUserRewarded(new MaxReward() {
+ @Override
+ public String getLabel() {
+ if (reward != null) {
+ return reward.getType();
+ }
+ return "";
+ }
+
+ @Override
+ public int getAmount() {
+ if (reward != null) {
+ return reward.getCount();
+ }
+ return 0;
+ }
+ });
+ }
+ }
+ };
+ }
+
+}