diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 25302c623..12ceee5d8 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -1,3 +1,9 @@ +## 7.11 +### Improvements/Bug Fixes ++ MS-4685 Added API to disable tracking cookies during auction. ++ MS-4714 Added improvements to Ad Expiry events for Native Ads. ++ MS-4699 Added Accept-Language parameter in the HTTP header for Ad Request. + ## 7.10 ### New Feature + MS-4659, MS-4674 Added support for User Id from external sources(Criteo, NetID, LiverRamp, The Trade Desk) [https://wiki.xandr.com/x/DAkYBg] diff --git a/instreamvideo/build.gradle b/instreamvideo/build.gradle index 798ce004b..0f13e9d3f 100644 --- a/instreamvideo/build.gradle +++ b/instreamvideo/build.gradle @@ -1,5 +1,5 @@ // Project Properties -version = "1.25" // Instream SDK version +version = "1.26" // Instream SDK version apply plugin: 'com.android.library' @@ -10,7 +10,7 @@ android { defaultConfig { minSdkVersion 14 targetSdkVersion 28 - versionCode 23 // An integer value that represents the version of the code, relative to other versions. Increase for each release. + versionCode 24 // An integer value that represents the version of the code, relative to other versions. Increase for each release. versionName version consumerProguardFiles 'proguard-project.txt' } diff --git a/sdk/build.gradle b/sdk/build.gradle index 62162a13e..b960d3ba9 100644 --- a/sdk/build.gradle +++ b/sdk/build.gradle @@ -1,5 +1,5 @@ // Project properties -version = "7.10" +version = "7.11" group='com.appnexus.opensdk' // Android build @@ -9,7 +9,7 @@ android { compileSdkVersion 28 buildToolsVersion '29.0.0' defaultConfig { - versionCode 80 // An integer value that represents the version of the code, relative to other versions. Increase for each release. + versionCode 81 // An integer value that represents the version of the code, relative to other versions. Increase for each release. versionName version consumerProguardFiles 'proguard-project.txt' minSdkVersion 14 diff --git a/sdk/src/com/appnexus/opensdk/ANNativeAdResponse.java b/sdk/src/com/appnexus/opensdk/ANNativeAdResponse.java index c1d17afe0..cc76439b2 100644 --- a/sdk/src/com/appnexus/opensdk/ANNativeAdResponse.java +++ b/sdk/src/com/appnexus/opensdk/ANNativeAdResponse.java @@ -42,6 +42,7 @@ import org.json.JSONException; import org.json.JSONObject; +import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -103,8 +104,7 @@ public void run() { registeredView = null; clickables = null; if (visibilityDetector != null) { - visibilityDetector.destroy(); - visibilityDetector = null; + visibilityDetector.destroy(viewWeakReference); } impressionTrackers = null; listener = null; @@ -140,6 +140,7 @@ public void run() { private ArrayList impressionTrackers; private ProgressDialog progressDialog; private ANClickThroughAction clickThroughAction = ANClickThroughAction.OPEN_SDK_BROWSER; + private WeakReference viewWeakReference; /** * Process the metadata of native response from ad server @@ -326,14 +327,15 @@ public void setCreativeId(String creativeId) { protected boolean registerView(final View view, final NativeAdEventListener listener) { if (!expired && view != null) { this.listener = listener; - visibilityDetector = VisibilityDetector.create(view); + viewWeakReference = new WeakReference<>(view); + visibilityDetector = VisibilityDetector.create(viewWeakReference); if (visibilityDetector == null) { return false; } impressionTrackers = new ArrayList(imp_trackers.size()); for (String url : imp_trackers) { - ImpressionTracker impressionTracker = ImpressionTracker.create(url, visibilityDetector, view.getContext(), anOmidAdSession, new ImpressionTrackerListener() { + ImpressionTracker impressionTracker = ImpressionTracker.create(viewWeakReference, url, visibilityDetector, view.getContext(), anOmidAdSession, new ImpressionTrackerListener() { @Override public void onImpressionTrackerFired() { if (listener != null) { diff --git a/sdk/src/com/appnexus/opensdk/AdView.java b/sdk/src/com/appnexus/opensdk/AdView.java index 6c964dc5e..c2ad3741f 100644 --- a/sdk/src/com/appnexus/opensdk/AdView.java +++ b/sdk/src/com/appnexus/opensdk/AdView.java @@ -45,6 +45,7 @@ import com.appnexus.opensdk.utils.HTTPGet; import com.appnexus.opensdk.utils.HTTPResponse; import com.appnexus.opensdk.utils.Settings; +import com.appnexus.opensdk.utils.Settings.CountImpression; import com.appnexus.opensdk.utils.ViewUtil; import java.lang.ref.WeakReference; @@ -168,6 +169,19 @@ boolean isMRAIDExpanded() { return isMRAIDExpanded; } + private void addVisibilityDetector(final WeakReference view) { + final VisibilityDetector visibilityDetector = VisibilityDetector.create(view); + visibilityDetector.addVisibilityListener(view, new VisibilityDetector.VisibilityListener() { + @Override + public void onVisibilityChanged(boolean visible) { + if (visible && impressionTrackers != null && impressionTrackers.size() > 0) { + fireImpressionTracker(); + visibilityDetector.destroy(view); + } + } + }); + } + @Override public boolean isReadyToStart() { if (!(getContext() instanceof Activity)) { @@ -1235,6 +1249,15 @@ private void handleBannerOrInterstitialAd(final AdResponse ad) { handler.post(new Runnable() { @Override public void run() { + if (ad.getResponseData() != null && ad.getResponseData().getImpressionURLs() != null && ad.getResponseData().getImpressionURLs().size() > 0) { + impressionTrackers = ad.getResponseData().getImpressionURLs(); + } + if (ad.getDisplayable() != null && ad.getMediaType().equals(MediaType.BANNER) && ad.getResponseData().getAdType().equalsIgnoreCase(UTConstants.AD_TYPE_BANNER)) { + if (getEffectiveImpressionCountingMethod() == CountImpression.ONE_PX) { + WeakReference weakReferenceView = new WeakReference(ad.getDisplayable().getView()); + addVisibilityDetector(weakReferenceView); + } + } setCreativeWidth(ad.getDisplayable().getCreativeWidth()); setCreativeHeight(ad.getDisplayable().getCreativeHeight()); setCreativeId(ad.getResponseData().getAdResponseInfo().getCreativeId()); @@ -1250,14 +1273,12 @@ public void run() { display(ad.getDisplayable()); } - if (ad.getResponseData() != null && ad.getResponseData().getImpressionURLs() != null && ad.getResponseData().getImpressionURLs().size() > 0) { - impressionTrackers = ad.getResponseData().getImpressionURLs(); - } - // Banner OnAdLoaded and if View is attached to window, or if the LazyLoad is enabled Impression is counted. if (getMediaType().equals(MediaType.BANNER)) { - if (isAdViewAttachedToWindow() || countBannerImpressionOnAdLoad || (isLazyLoadEnabled() && isWebviewActivated() && ad.getResponseData().getAdType().equalsIgnoreCase(UTConstants.AD_TYPE_BANNER))) { + if (getEffectiveImpressionCountingMethod() == CountImpression.ON_LOAD || + (getEffectiveImpressionCountingMethod() == CountImpression.LAZY_LOAD && isWebviewActivated() && ad.getResponseData().getAdType().equalsIgnoreCase(UTConstants.AD_TYPE_BANNER)) || + (getEffectiveImpressionCountingMethod() == CountImpression.DEFAULT && isAdViewAttachedToWindow())) { if (impressionTrackers != null && impressionTrackers.size() > 0) { fireImpressionTracker(); } @@ -1298,7 +1319,7 @@ protected void onAttachedToWindow() { super.onAttachedToWindow(); isAttachedToWindow = true; // OnAttaced to Window and Impresion tracker is non null then fire impression. - if (getMediaType().equals(MediaType.BANNER) && impressionTrackers != null && impressionTrackers.size() > 0) { + if (getEffectiveImpressionCountingMethod() == CountImpression.DEFAULT && getMediaType().equals(MediaType.BANNER) && impressionTrackers != null && impressionTrackers.size() > 0) { fireImpressionTracker(); } @@ -1333,6 +1354,7 @@ void fireImpressionTracker() { void fireImpressionTracker(final String trackerUrl) { + Clog.d("FIRE_IMPRESSION", getEffectiveImpressionCountingMethod().name()); HTTPGet impTracker = new HTTPGet() { @Override protected void onPostExecute(HTTPResponse response) { @@ -1573,7 +1595,7 @@ protected boolean loadLazyAd() { * This returns if the Webview for the Lazy Load has been activated or not * The webview once activated is de-activated by calling the deactivateWebviewForNextCall() {basically for AutoRefresh } */ - private boolean isWebviewActivated() { + protected boolean isWebviewActivated() { return activateWebview; } @@ -1592,4 +1614,19 @@ protected void deactivateWebviewForNextCall() { protected boolean isLastResponseSuccessful() { return getAdResponseInfo() != null && getAdResponseInfo().getAdType() == AdType.BANNER; } + + /** + * @return {@link CountImpression} Based on the boolean values set for the Impression Tracking + * */ + public CountImpression getEffectiveImpressionCountingMethod() { + if (countBannerImpressionOnAdLoad) { + return CountImpression.ON_LOAD; + } else if (SDKSettings.getCountImpressionOn1pxRendering()) { + return CountImpression.ONE_PX; + } else if (isLazyLoadEnabled()) { + return CountImpression.LAZY_LOAD; + } else { + return CountImpression.DEFAULT; + } + } } diff --git a/sdk/src/com/appnexus/opensdk/AdViewRequestManager.java b/sdk/src/com/appnexus/opensdk/AdViewRequestManager.java index b92d3a6fc..67b561071 100644 --- a/sdk/src/com/appnexus/opensdk/AdViewRequestManager.java +++ b/sdk/src/com/appnexus/opensdk/AdViewRequestManager.java @@ -29,6 +29,8 @@ import com.appnexus.opensdk.ut.adresponse.RTBVASTAdResponse; import com.appnexus.opensdk.ut.adresponse.SSMHTMLAdResponse; import com.appnexus.opensdk.utils.Clog; +import com.appnexus.opensdk.utils.Settings; +import com.appnexus.opensdk.utils.Settings.CountImpression; import com.appnexus.opensdk.utils.StringUtil; import java.lang.ref.WeakReference; @@ -180,7 +182,9 @@ public void onReceiveUTResponse(UTAdResponse response) { } private void fireImpressionTrackerEarly (AdView adView, BaseAdResponse response) { - if(adView.countBannerImpressionOnAdLoad){ + if(adView.getEffectiveImpressionCountingMethod() == CountImpression.ON_LOAD || + (adView.getEffectiveImpressionCountingMethod() == CountImpression.LAZY_LOAD && + adView.isWebviewActivated() && response.getAdType().equalsIgnoreCase(UTConstants.AD_TYPE_BANNER))){ if(response.getImpressionURLs() != null && response.getImpressionURLs().size() > 0){ adView.impressionTrackers = response.getImpressionURLs(); adView.fireImpressionTracker(); @@ -428,6 +432,9 @@ protected void loadLazyAd() { } initiateWebview(adOwner, currentAd); + if (adOwner instanceof AdView) { + fireImpressionTrackerEarly((AdView) adOwner, currentAd); + } } } \ No newline at end of file diff --git a/sdk/src/com/appnexus/opensdk/AdWebView.java b/sdk/src/com/appnexus/opensdk/AdWebView.java index e9198a580..963961c7a 100644 --- a/sdk/src/com/appnexus/opensdk/AdWebView.java +++ b/sdk/src/com/appnexus/opensdk/AdWebView.java @@ -193,6 +193,12 @@ public void loadAd(BaseAdResponse ad) { isVideoAd = (UTConstants.AD_TYPE_VIDEO.equalsIgnoreCase(ad.getAdType())); isNativeAd = (UTConstants.AD_TYPE_NATIVE.equalsIgnoreCase(ad.getAdType())); + if(adView.getEffectiveImpressionCountingMethod() == Settings.CountImpression.ON_LOAD || + (adView.getEffectiveImpressionCountingMethod() == Settings.CountImpression.LAZY_LOAD && + adView.isWebviewActivated() && ad.getAdType().equalsIgnoreCase(UTConstants.AD_TYPE_BANNER))) { + onAdImpression(); + } + if (isNativeAd) { isMRAIDEnabled = false; html = getNativeRendererContent(ad); diff --git a/sdk/src/com/appnexus/opensdk/BaseNativeAdResponse.java b/sdk/src/com/appnexus/opensdk/BaseNativeAdResponse.java index 50797ee29..329ca93ef 100644 --- a/sdk/src/com/appnexus/opensdk/BaseNativeAdResponse.java +++ b/sdk/src/com/appnexus/opensdk/BaseNativeAdResponse.java @@ -94,6 +94,8 @@ protected long getAboutToExpireTime(String contentSource, int memberId) { aboutToExpireTime = Settings.NATIVE_AD_RESPONSE_EXPIRATION_TIME_CSM_CSR; } else if (contentSource.equalsIgnoreCase("rtb") && memberId == 11217) { aboutToExpireTime = Settings.NATIVE_AD_RESPONSE_EXPIRATION_TIME_TRIPLELIFT; + } else if (contentSource.equalsIgnoreCase("rtb") && memberId == 12085) { + aboutToExpireTime = Settings.NATIVE_AD_RESPONSE_EXPIRATION_TIME_MSAN; } return aboutToExpireTime - getExpiryInterval(contentSource, memberId); @@ -106,6 +108,8 @@ protected long getExpiryInterval(String contentSource, int memberId) { Clog.e(Clog.baseLogTag, "expiryInterval can not be set less then zero, default interval will be used."); } else if((contentSource.equalsIgnoreCase("csm") || contentSource.equalsIgnoreCase("csr")) && expiryInterval >= Settings.NATIVE_AD_RESPONSE_EXPIRATION_TIME_CSM_CSR) { Clog.e(Clog.baseLogTag, "facebook expiryInterval can not be greater than 60 minutes, default interval will be used."); + } else if (contentSource.equalsIgnoreCase("rtb") && memberId == 12085 && expiryInterval >= Settings.NATIVE_AD_RESPONSE_EXPIRATION_TIME_MSAN){ + Clog.e(Clog.baseLogTag, "for RTB & member 12085 expiryInterval can not be greater than 10 minutes, default interval will be used."); } else if (contentSource.equalsIgnoreCase("rtb") && memberId == 11217 && expiryInterval >= Settings.NATIVE_AD_RESPONSE_EXPIRATION_TIME_TRIPLELIFT){ Clog.e(Clog.baseLogTag, "for RTB & member 11217 expiryInterval can not be greater than 5 minutes, default interval will be used."); } else if(expiryInterval >= Settings.NATIVE_AD_RESPONSE_EXPIRATION_TIME){ diff --git a/sdk/src/com/appnexus/opensdk/ImpressionTracker.java b/sdk/src/com/appnexus/opensdk/ImpressionTracker.java index df029a32a..51e6cc10b 100644 --- a/sdk/src/com/appnexus/opensdk/ImpressionTracker.java +++ b/sdk/src/com/appnexus/opensdk/ImpressionTracker.java @@ -18,6 +18,7 @@ import android.annotation.SuppressLint; import android.content.Context; +import android.view.View; import com.appnexus.opensdk.utils.Clog; import com.appnexus.opensdk.utils.HTTPGet; @@ -25,6 +26,8 @@ import com.appnexus.opensdk.utils.Settings; import com.appnexus.opensdk.viewability.ANOmidAdSession; +import java.lang.ref.WeakReference; + class ImpressionTracker { private String url; private VisibilityDetector visibilityDetector; @@ -33,18 +36,20 @@ class ImpressionTracker { private ImpressionListener listener; private ANOmidAdSession anOmidAdSession; private ImpressionTrackerListener impressionTrackerListener; + private WeakReference viewWeakReference; - static ImpressionTracker create(String url, VisibilityDetector visibilityDetector, Context context, ANOmidAdSession anOmidAdSession, ImpressionTrackerListener impressionTrackerListener) { + static ImpressionTracker create(WeakReference viewWeakReference, String url, VisibilityDetector visibilityDetector, Context context, ANOmidAdSession anOmidAdSession, ImpressionTrackerListener impressionTrackerListener) { if (visibilityDetector == null) { return null; } else { - ImpressionTracker impressionTracker = new ImpressionTracker(url, visibilityDetector, context, anOmidAdSession, impressionTrackerListener); - visibilityDetector.addVisibilityListener(impressionTracker.listener); + ImpressionTracker impressionTracker = new ImpressionTracker(viewWeakReference, url, visibilityDetector, context, anOmidAdSession, impressionTrackerListener); + visibilityDetector.addVisibilityListener(viewWeakReference, impressionTracker.listener); return impressionTracker; } } - private ImpressionTracker(String url, VisibilityDetector visibilityDetector, Context context, ANOmidAdSession anOmidAdSession, ImpressionTrackerListener impressionTrackerListener) { + private ImpressionTracker(WeakReference viewWeakReference, String url, VisibilityDetector visibilityDetector, Context context, ANOmidAdSession anOmidAdSession, ImpressionTrackerListener impressionTrackerListener) { + this.viewWeakReference = viewWeakReference; this.url = url; this.visibilityDetector = visibilityDetector; this.listener = new ImpressionListener(); @@ -73,7 +78,7 @@ protected String getUrl() { } }; asyncTask.execute(); - visibilityDetector.removeVisibilityListener(listener); + visibilityDetector.destroy(viewWeakReference); listener = null; } else { nm.addURL(url, context, new ImpressionTrackerListener() { @@ -97,13 +102,17 @@ class ImpressionListener implements VisibilityDetector.VisibilityListener { @Override public void onVisibilityChanged(boolean visible) { - if (visible) { - elapsedTime += VisibilityDetector.VISIBILITY_THROTTLE_MILLIS; - } else { - elapsedTime = 0; - } - if (elapsedTime >= Settings.NATIVE_AD_VISIBLE_PERIOD_MILLIS) { + if (visible && SDKSettings.getCountImpressionOn1pxRendering()) { ImpressionTracker.this.fire(); + } else { + if (visible) { + elapsedTime += VisibilityDetector.VISIBILITY_THROTTLE_MILLIS; + } else { + elapsedTime = 0; + } + if (elapsedTime >= Settings.NATIVE_AD_VISIBLE_PERIOD_MILLIS) { + ImpressionTracker.this.fire(); + } } } } diff --git a/sdk/src/com/appnexus/opensdk/SDKSettings.java b/sdk/src/com/appnexus/opensdk/SDKSettings.java index 0c3e0ec7d..c7b853071 100644 --- a/sdk/src/com/appnexus/opensdk/SDKSettings.java +++ b/sdk/src/com/appnexus/opensdk/SDKSettings.java @@ -33,6 +33,8 @@ import java.util.Map; import java.util.concurrent.Executor; +import static com.appnexus.opensdk.utils.Settings.countImpressionOn1pxRendering; + /** * Global static functions that apply to all SDK views and calls. */ @@ -100,6 +102,24 @@ public static void disableAAIDUsage(boolean disable) { Settings.getSettings().disableAAIDUsage = disable; } + /** + Do not track flag. Set this to true/false if you have information in the app about user opt-out. + If set to true, tracking cookies and AAID will be disabled for all future auctions. + Default value is false. + */ + public static void setDoNotTrack(boolean dnt) { + Settings.getSettings().doNotTrack = dnt; + } + + /** + * Returns true if Do not track is enabled. + * False otherwise. + */ + public static boolean getDoNotTrack() { + return Settings.getSettings().doNotTrack; + } + + /** * Returns true if the ad server calls will include location information * or false otherwise. @@ -452,5 +472,20 @@ public interface InitListener { void onInitFinished(); } + /** + * @return boolean that states the value of countImpressionOn1pxRendering + * set by using {@link #setCountImpressionOn1pxRendering(boolean)}. Default is false. + * */ + public static boolean getCountImpressionOn1pxRendering() { + return countImpressionOn1pxRendering; + } + + /** + * To enable the Impression counting on 1px display + * @param enable set true to enable, false to disable. Default is false. + * */ + public static void setCountImpressionOn1pxRendering(boolean enable) { + countImpressionOn1pxRendering = enable; + } } diff --git a/sdk/src/com/appnexus/opensdk/VisibilityDetector.java b/sdk/src/com/appnexus/opensdk/VisibilityDetector.java index e3f404ed4..4862d764b 100644 --- a/sdk/src/com/appnexus/opensdk/VisibilityDetector.java +++ b/sdk/src/com/appnexus/opensdk/VisibilityDetector.java @@ -17,12 +17,15 @@ package com.appnexus.opensdk; import android.graphics.Rect; +import android.os.Handler; import android.view.View; import com.appnexus.opensdk.utils.Clog; import com.appnexus.opensdk.utils.Settings; +import java.lang.ref.WeakReference; import java.util.ArrayList; +import java.util.HashMap; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; @@ -30,34 +33,52 @@ class VisibilityDetector { static final long VISIBILITY_THROTTLE_MILLIS = 250; private boolean scheduled = false; - private View mView; // not null - private ArrayList listeners; + private HashMap, ArrayList> viewListenerMap; private Runnable visibilityCheck; private ScheduledExecutorService tasker; + private static VisibilityDetector visibilityDetector; + private Handler mHandler; - static VisibilityDetector create(View view) { + static VisibilityDetector create(WeakReference view) { if (view == null) { Clog.d(Clog.nativeLogTag, "Unable to check visibility"); return null; } - return new VisibilityDetector(view); + if (visibilityDetector == null) { + visibilityDetector = new VisibilityDetector(); + } + + visibilityDetector.addViewForVisibility(view); + return visibilityDetector; + } + + private VisibilityDetector() { } - private VisibilityDetector(View view) { - this.mView = view; - this.listeners = new ArrayList(); - scheduleVisibilityCheck(); + private void addViewForVisibility(WeakReference view) { + if (mHandler == null) { + mHandler = new Handler(); + } + if (viewListenerMap == null) { + viewListenerMap = new HashMap(); + } + if (!viewListenerMap.containsKey(view)) { + this.viewListenerMap.put(view, new ArrayList()); + if (this.viewListenerMap.size() == 1) { + scheduleVisibilityCheck(); + } + } } - void addVisibilityListener(VisibilityListener listener) { - if (listener != null) { - listeners.add(listener); + void addVisibilityListener(WeakReference viewWeakReference, VisibilityListener listener) { + if (listener != null && viewListenerMap.containsKey(viewWeakReference) && !viewListenerMap.get(viewWeakReference).contains(listener)) { + viewListenerMap.get(viewWeakReference).add(listener); } } - boolean removeVisibilityListener(VisibilityListener listener) { - return listeners.remove(listener); + boolean removeVisibilityListener(WeakReference viewWeakReference) { + return viewListenerMap.remove(viewWeakReference) != null; } void scheduleVisibilityCheck(){ @@ -66,19 +87,29 @@ void scheduleVisibilityCheck(){ this.visibilityCheck = new Runnable() { @Override public void run() { - if (listeners != null) { - // copy listeners to a new array to avoid concurrentmodificationexception - ArrayList tempList = new ArrayList(); - for (VisibilityListener listener : listeners) { - tempList.add(listener); - } - if (isVisible()) { - for (VisibilityListener listener : tempList) { - listener.onVisibilityChanged(true); + HashMap, ArrayList> tempMap = new HashMap<>(viewListenerMap); + for (WeakReference viewWeakReference : tempMap.keySet()) { + ArrayList listeners = tempMap.get(viewWeakReference); + View view = viewWeakReference.get(); + if (listeners != null && listeners.size() > 0 && view != null) { + // copy listeners to a new array to avoid concurrentmodificationexception + ArrayList tempList = new ArrayList(); + for (VisibilityListener listener : listeners) { + tempList.add(listener); + } + if (isVisible(view)) { + for (VisibilityListener listener : tempList) { + listener.onVisibilityChanged(true); + } + } else { + for (VisibilityListener listener : tempList) { + listener.onVisibilityChanged(false); + } } } else { - for (VisibilityListener listener : tempList) { - listener.onVisibilityChanged(false); + viewListenerMap.remove(viewWeakReference); + if (viewListenerMap.size() == 0) { + } } } @@ -88,39 +119,47 @@ public void run() { tasker.scheduleAtFixedRate(new Runnable() { @Override public void run() { - mView.post(visibilityCheck); + mHandler.post(visibilityCheck); } }, 0, VISIBILITY_THROTTLE_MILLIS, TimeUnit.MILLISECONDS); } - boolean isVisible() { - if (mView == null || mView.getVisibility() != View.VISIBLE || mView.getParent() == null) { + boolean isVisible(View view) { + if (view == null || view.getVisibility() != View.VISIBLE || view.getParent() == null) { return false; } // holds the visible part of a view Rect clippedArea = new Rect(); - if (!mView.getGlobalVisibleRect(clippedArea)) { + if (!view.getGlobalVisibleRect(clippedArea)) { return false; } final int visibleViewArea = clippedArea.height() * clippedArea.width(); - final int totalArea = mView.getHeight() * mView.getWidth(); + final int totalArea = view.getHeight() * view.getWidth(); if (totalArea <= 0) { return false; } - return 100 * visibleViewArea >= Settings.MIN_PERCENTAGE_VIEWED * totalArea; + if (SDKSettings.getCountImpressionOn1pxRendering()) { + return visibleViewArea >= Settings.MIN_AREA_VIEWED_FOR_1PX; + } else { + return 100 * visibleViewArea >= Settings.MIN_PERCENTAGE_VIEWED * totalArea; + } } - void destroy() { - if (tasker != null) { - tasker.shutdownNow(); + void destroy(WeakReference view) { + if (viewListenerMap.containsKey(view)) { + removeVisibilityListener(view); + } + if (viewListenerMap.size() == 0) { + if (tasker != null) { + tasker.shutdownNow(); + } + scheduled = false; + mHandler.removeCallbacks(visibilityCheck); } - mView.removeCallbacks(visibilityCheck); - mView = null; - listeners = null; } interface VisibilityListener { diff --git a/sdk/src/com/appnexus/opensdk/ut/UTAdRequest.java b/sdk/src/com/appnexus/opensdk/ut/UTAdRequest.java index 05d99e2d8..346041c26 100644 --- a/sdk/src/com/appnexus/opensdk/ut/UTAdRequest.java +++ b/sdk/src/com/appnexus/opensdk/ut/UTAdRequest.java @@ -135,8 +135,9 @@ HashMap makeRequest() { conn.setDoInput(true); conn.setRequestProperty("Content-Type", "application/json"); conn.setRequestProperty("Accept", "application/json"); + conn.setRequestProperty("Accept-Language", Settings.getSettings().language); conn.setRequestProperty("User-Agent", Settings.getSettings().ua); - if (ANGDPRSettings.canIAccessDeviceData(requestParams.getContext())) { + if (ANGDPRSettings.canIAccessDeviceData(requestParams.getContext()) && !Settings.getSettings().doNotTrack) { String cookieString = WebviewUtil.getCookie(); if (!TextUtils.isEmpty(cookieString)) { conn.setRequestProperty("Cookie", cookieString); @@ -182,7 +183,7 @@ HashMap makeRequest() { Clog.i(Clog.httpRespLogTag, "RESPONSE - " + result); Map> headers = conn.getHeaderFields(); - if (ANGDPRSettings.canIAccessDeviceData(requestParams.getContext())) { + if (ANGDPRSettings.canIAccessDeviceData(requestParams.getContext()) && !Settings.getSettings().doNotTrack) { WebviewUtil.cookieSync(headers); } ANMultiAdRequest anMultiAdRequest = getMultiAdRequest(); diff --git a/sdk/src/com/appnexus/opensdk/ut/UTRequestParameters.java b/sdk/src/com/appnexus/opensdk/ut/UTRequestParameters.java index 7ed0f8c6c..7222c1daf 100644 --- a/sdk/src/com/appnexus/opensdk/ut/UTRequestParameters.java +++ b/sdk/src/com/appnexus/opensdk/ut/UTRequestParameters.java @@ -121,6 +121,7 @@ public class UTRequestParameters { private static final String USER_GENDER = "gender"; private static final String USER_EXTERNALUID = "external_uid"; // publisher first party id private static final String USER_LANGUAGE = "language"; + private static final String USER_DNT = "dnt"; private static final String DEVICE = "device"; private static final String GEO_OVERRIDE = "geoOverride"; private static final String COUNTRY_CODE = "countryCode"; @@ -839,6 +840,9 @@ private JSONObject getUserObject(UTRequestParameters utRequestParameters) { user.put(USER_EXTERNALUID, utRequestParameters.getExternalUid()); } + if(Settings.getSettings().doNotTrack){ + user.put(USER_DNT, true); + } } catch (JSONException e) { } @@ -1027,14 +1031,11 @@ private JSONObject getDeviceObject() { // limited ad tracking device.put(DEVICE_LMT, Settings.getSettings().limitTrackingEnabled); - if (ANGDPRSettings.canIAccessDeviceData(context)) { + if (ANGDPRSettings.canIAccessDeviceData(context) && !SDKSettings.isAAIDUsageDisabled() && !Settings.getSettings().doNotTrack) { if (!StringUtil.isEmpty(Settings.getSettings().aaid)) { - // device id - if(!SDKSettings.isAAIDUsageDisabled()) { - JSONObject device_id = new JSONObject(); - device_id.put(DEVICE_ID_AAID, Settings.getSettings().aaid); - device.put(DEVICE_DEVICE_ID, device_id); - } + JSONObject device_id = new JSONObject(); + device_id.put(DEVICE_ID_AAID, Settings.getSettings().aaid); + device.put(DEVICE_DEVICE_ID, device_id); } } // os diff --git a/sdk/src/com/appnexus/opensdk/utils/Settings.java b/sdk/src/com/appnexus/opensdk/utils/Settings.java index 94972f7eb..63273e06c 100644 --- a/sdk/src/com/appnexus/opensdk/utils/Settings.java +++ b/sdk/src/com/appnexus/opensdk/utils/Settings.java @@ -30,6 +30,21 @@ import java.util.Map; public class Settings { + + /** + * Fire Impression + * DEFAULT - When the Ad is displayed + * ON_LOAD - When content is loaded on the WebView + * ONE_PX - When 1px of content is displayed + * LAZY_LOAD - When the content is loaded on the Webview (LazyLoad is enabled) + * */ + public enum CountImpression { + DEFAULT, // When the Ad is displayed + ON_LOAD, // When content is loaded on the WebView + ONE_PX, // When 1px of content is displayed + LAZY_LOAD // When the content is loaded on the Webview (LazyLoad is enabled) + } + public String hidmd5 = null; public String hidsha1 = null; public String carrierName = null; @@ -66,6 +81,8 @@ public class Settings { public boolean disableAAIDUsage = false; + public boolean doNotTrack = false; + public HashMap externalMediationClasses = new HashMap(); public String countryCode; public String zip; @@ -76,6 +93,13 @@ public class Settings { public String publisherUserId = ""; public Map externalUserIds = new HashMap<>(); + /** + * This boolean is responsible for firing the Impression tracker on 1px visibility of the Ad View + * true - follow the 1px constraint to fire the Impression + * false - do not follow the 1px constraint to fire the Impression + * */ + public static boolean countImpressionOn1pxRendering = false; + // STATICS public static final int HTTP_CONNECTION_TIMEOUT = 15000; public static final int HTTP_SOCKET_TIMEOUT = 20000; @@ -91,6 +115,7 @@ public class Settings { public static final long NATIVE_AD_RESPONSE_EXPIRATION_TIME = 6 * 60 * 60 * 1000; // 6 hours public static final long NATIVE_AD_RESPONSE_EXPIRATION_TIME_CSM_CSR = 60 * 60 * 1000; // an hour public static final long NATIVE_AD_RESPONSE_EXPIRATION_TIME_TRIPLELIFT = 5 * 60 * 1000; // 5 minutes + public static final long NATIVE_AD_RESPONSE_EXPIRATION_TIME_MSAN = 10 * 60 * 1000; // 10 minutes public static final long NATIVE_AD_ABOUT_TO_EXPIRE_INTERVAL_DEFAULT = 60 * 1000; // 1 minute @@ -105,6 +130,8 @@ public class Settings { public static final int MIN_PERCENTAGE_VIEWED = 50; + public static final int MIN_AREA_VIEWED_FOR_1PX = 1; + public static final int VIDEO_AUTOPLAY_PERCENTAGE = 50; private static String COOKIE_DOMAIN = "https://mediation.adnxs.com"; diff --git a/sdk/test/com/appnexus/opensdk/BannerImpressionTests.java b/sdk/test/com/appnexus/opensdk/BannerImpressionTests.java index e56cb16af..148484c00 100644 --- a/sdk/test/com/appnexus/opensdk/BannerImpressionTests.java +++ b/sdk/test/com/appnexus/opensdk/BannerImpressionTests.java @@ -70,6 +70,11 @@ public void setup() { shadowConnectivityManager = (ShadowConnectivityManager) Shadow.extract(connectivityManager); } + @Override + public void tearDown() { + super.tearDown(); + SDKSettings.setCountImpressionOn1pxRendering(false); + } // 1. Loads the banner off screen // 2. Attaches banner to a screen @@ -135,6 +140,42 @@ public void test_3BannerImpressionFiringonAdLoad() { assertImpressionURL(2); } + // ============= ONE PX test cases ============== + + // 1. Loads the banner off screen + // 2. Attaches banner to a screen + // 3. Checks if impression_url is fired succesfully + @Test + public void test_1BannerImpressionFiringOnePx() { + SDKSettings.setCountImpressionOn1pxRendering(true); + + server.enqueue(new MockResponse().setResponseCode(200).setBody(TestResponsesUT.banner())); // This is for UT Request + server.enqueue(new MockResponse().setResponseCode(200).setBody(TestResponsesUT.blank())); // This is for Impression + + runBasicBannerTest(); + + attachBannerToView(); + + confirmNoImpressionULRinQueue(); + } + + // 1. Loads the banner off screen + // 2. Attaches banner to a screen + // 3. Checks if impression_url is fired succesfully + @Test + public void test_1BannerImpressionFiringPreferCountImpressionOnAdLoadOverOnePx() { + bannerAdView.setCountImpressionOnAdLoad(true); + SDKSettings.setCountImpressionOn1pxRendering(true); + + server.enqueue(new MockResponse().setResponseCode(200).setBody(TestResponsesUT.banner())); // This is for UT Request + server.enqueue(new MockResponse().setResponseCode(200).setBody(TestResponsesUT.blank())); // This is for Impression + + runBasicBannerTest(); + + attachBannerToView(); + + assertImpressionURL(2); + } //////////////// END TESTS //////////////////////// diff --git a/sdk/test/com/appnexus/opensdk/UTAdRequestTest.java b/sdk/test/com/appnexus/opensdk/UTAdRequestTest.java index 6a3c3ba45..fedeb685b 100644 --- a/sdk/test/com/appnexus/opensdk/UTAdRequestTest.java +++ b/sdk/test/com/appnexus/opensdk/UTAdRequestTest.java @@ -909,6 +909,37 @@ public void testPublisherUserID() throws Exception { assertTrue(userDoBoth.getString("external_uid").equals("test-publisheruserid-bar-xyz")); } + /** + * Test Do Not Track parameters in /ut request body + * + * @throws Exception + */ + @Test + public void testDoNotTrack() throws Exception { + + // Default params, dnt will not be present in the POST DATA + executionSteps(); + JSONObject postDataBefore = inspectPostData(); + JSONObject userBefore = postDataBefore.getJSONObject("user"); + assertFalse(userBefore.has("dnt")); + + // setDoNotTrack to false and make sure dnt is not present in the POST DATA + SDKSettings.setDoNotTrack(false); + executionSteps(); + JSONObject postDataAfterSetToFalse = inspectPostData(); + JSONObject userAfterSetToFalse = postDataAfterSetToFalse.getJSONObject("user"); + assertFalse(userAfterSetToFalse.has("dnt")); + + // setDoNotTrack to true and make sure dnt is present in the POST DATA + SDKSettings.setDoNotTrack(true); + executionSteps(); + JSONObject postDataAfterSetToTrue = inspectPostData(); + JSONObject userAfterSetToTrue = postDataAfterSetToTrue.getJSONObject("user"); + assertTrue(userAfterSetToTrue.has("dnt")); + assertTrue(userAfterSetToTrue.getBoolean("dnt")); + + } + /** * Test External User Id parameters in /ut request body * diff --git a/tests/AppNexusSDKTestApp/app/src/androidTest/java/appnexus/com/appnexussdktestapp/functional/banner/BannerOnePxTest.kt b/tests/AppNexusSDKTestApp/app/src/androidTest/java/appnexus/com/appnexussdktestapp/functional/banner/BannerOnePxTest.kt new file mode 100644 index 000000000..3359d315a --- /dev/null +++ b/tests/AppNexusSDKTestApp/app/src/androidTest/java/appnexus/com/appnexussdktestapp/functional/banner/BannerOnePxTest.kt @@ -0,0 +1,264 @@ +/* + * Copyright 2019 APPNEXUS INC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package appnexus.com.appnexussdktestapp.functional.banner + +import android.content.Intent +import android.content.res.Resources +import android.os.Handler +import android.os.Looper +import android.view.View.VISIBLE +import androidx.test.espresso.Espresso +import androidx.test.espresso.IdlingPolicies +import androidx.test.espresso.IdlingRegistry +import androidx.test.espresso.assertion.ViewAssertions +import androidx.test.espresso.matcher.ViewMatchers +import androidx.test.rule.ActivityTestRule +import androidx.test.runner.AndroidJUnit4 +import appnexus.com.appnexussdktestapp.BannerActivity +import appnexus.com.appnexussdktestapp.R +import appnexus.com.appnexussdktestapp.util.Utility.Companion.checkVisibilityDetectorMap +import com.appnexus.opensdk.SDKSettings +import com.microsoft.appcenter.espresso.Factory +import org.hamcrest.Matchers.not +import org.junit.* +import org.junit.runner.RunWith +import java.util.concurrent.TimeUnit + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class BannerOnePxTest { + val Int.dp: Int + get() = (this / Resources.getSystem().displayMetrics.density).toInt() + val Int.px: Int + get() = (this * Resources.getSystem().displayMetrics.density).toInt() + + @get:Rule + var reportHelper = Factory.getReportHelper() + + @Rule + @JvmField + var mActivityTestRule = ActivityTestRule(BannerActivity::class.java, false, false) + + lateinit var bannerActivity: BannerActivity + + @Before + fun setup() { + IdlingPolicies.setMasterPolicyTimeout(1, TimeUnit.MINUTES) + IdlingPolicies.setIdlingResourceTimeout(1, TimeUnit.MINUTES) + var intent = Intent() + mActivityTestRule.launchActivity(intent) + bannerActivity = mActivityTestRule.activity + IdlingRegistry.getInstance().register(bannerActivity.idlingResource) + } + + @After + fun destroy() { + SDKSettings.setCountImpressionOn1pxRendering(false) + IdlingRegistry.getInstance().unregister(bannerActivity.idlingResource) + reportHelper.label("Stopping App") + } + + /* + * Sanity Test for the Banner Ad of size 320x50 + * */ + @Test + fun bannerLoadSize320x50TestOnePx() { + + Thread.sleep(2000) + + SDKSettings.setCountImpressionOn1pxRendering(true) + + bannerActivity.shouldDisplay = false; + + bannerActivity.triggerAdLoad("14757567", 320, 50, creativeId = 166843001) + + Espresso.onView(ViewMatchers.withId(R.id.linearLayout)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView(ViewMatchers.withId(R.id.linearLayout)) + .check(ViewAssertions.matches(ViewMatchers.hasChildCount(1))) + Espresso.onView(ViewMatchers.withId(bannerActivity.banner_id)) + .check(ViewAssertions.matches(not(ViewMatchers.isDisplayed()))) + + checkVisibilityDetectorMap(1, bannerActivity) + + setVisibility(VISIBLE) + + Espresso.onView(ViewMatchers.withId(bannerActivity.banner_id)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + + Assert.assertTrue( + "Wrong Ad Width", + bannerActivity.banner.getChildAt(0).width.dp >= (bannerActivity.banner.adWidth - 1) || + bannerActivity.banner.getChildAt(0).width.dp <= (bannerActivity.banner.adWidth + 1) + ) + Assert.assertTrue( + "Wrong Ad Height", + bannerActivity.banner.getChildAt(0).height.dp >= (bannerActivity.banner.adHeight - 1) || + bannerActivity.banner.getChildAt(0).height.dp <= (bannerActivity.banner.adHeight + 1) + ) + + Thread.sleep(2000) + + checkVisibilityDetectorMap(0, bannerActivity) + + } + + /* + * Sanity Test for the Banner Ad of size 300x250 + * */ + @Test + fun bannerLoadSize300x250TestOnePx() { + + SDKSettings.setCountImpressionOn1pxRendering(true) + + bannerActivity.shouldDisplay = false; + + bannerActivity.triggerAdLoad("14847003", 300, 250, creativeId = 166843311) + + Espresso.onView(ViewMatchers.withId(R.id.linearLayout)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView(ViewMatchers.withId(R.id.linearLayout)) + .check(ViewAssertions.matches(ViewMatchers.hasChildCount(1))) + + Espresso.onView(ViewMatchers.withId(bannerActivity.banner_id)) + .check(ViewAssertions.matches(not(ViewMatchers.isDisplayed()))) + + checkVisibilityDetectorMap(1, bannerActivity) + + setVisibility(VISIBLE) + + Espresso.onView(ViewMatchers.withId(bannerActivity.banner_id)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Assert.assertTrue( + "Wrong Ad Width", + bannerActivity.banner.getChildAt(0).width.dp >= (bannerActivity.banner.adWidth - 1) || + bannerActivity.banner.getChildAt(0).width.dp <= (bannerActivity.banner.adWidth + 1) + ) + Assert.assertTrue( + "Wrong Ad Height", + bannerActivity.banner.getChildAt(0).height.dp >= (bannerActivity.banner.adHeight - 1) || + bannerActivity.banner.getChildAt(0).height.dp <= (bannerActivity.banner.adHeight + 1) + ) + + Thread.sleep(2000) + + checkVisibilityDetectorMap(0, bannerActivity) + + } + + + // BG Testing + + /* + * Sanity Test for the Banner Ad of size 320x50 + * */ + @Test + fun bannerLoadSize320x50TestBGOnePx() { + + Thread.sleep(2000) + + SDKSettings.setCountImpressionOn1pxRendering(true) + + bannerActivity.shouldDisplay = false; + + bannerActivity.triggerAdLoad("14757567", 320, 50, creativeId = 166843001, bgTask = true) + + Espresso.onView(ViewMatchers.withId(R.id.linearLayout)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView(ViewMatchers.withId(R.id.linearLayout)) + .check(ViewAssertions.matches(ViewMatchers.hasChildCount(1))) + Espresso.onView(ViewMatchers.withId(bannerActivity.banner_id)) + .check(ViewAssertions.matches(not(ViewMatchers.isDisplayed()))) + + checkVisibilityDetectorMap(1, bannerActivity) + + setVisibility(VISIBLE) + + Espresso.onView(ViewMatchers.withId(bannerActivity.banner_id)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + + Assert.assertTrue( + "Wrong Ad Width", + bannerActivity.banner.getChildAt(0).width.dp >= (bannerActivity.banner.adWidth - 1) || + bannerActivity.banner.getChildAt(0).width.dp <= (bannerActivity.banner.adWidth + 1) + ) + Assert.assertTrue( + "Wrong Ad Height", + bannerActivity.banner.getChildAt(0).height.dp >= (bannerActivity.banner.adHeight - 1) || + bannerActivity.banner.getChildAt(0).height.dp <= (bannerActivity.banner.adHeight + 1) + ) + + Thread.sleep(2000) + + checkVisibilityDetectorMap(0, bannerActivity) + + } + + /* + * Sanity Test for the Banner Ad of size 300x250 + * */ + @Test + fun bannerLoadSize300x250TestBGOnePx() { + + SDKSettings.setCountImpressionOn1pxRendering(true) + + bannerActivity.shouldDisplay = false; + + bannerActivity.triggerAdLoad("14847003", 300, 250, creativeId = 166843311, bgTask = true) + + Espresso.onView(ViewMatchers.withId(R.id.linearLayout)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView(ViewMatchers.withId(R.id.linearLayout)) + .check(ViewAssertions.matches(ViewMatchers.hasChildCount(1))) + + Espresso.onView(ViewMatchers.withId(bannerActivity.banner_id)) + .check(ViewAssertions.matches(not(ViewMatchers.isDisplayed()))) + + checkVisibilityDetectorMap(1, bannerActivity) + + setVisibility(VISIBLE) + + Espresso.onView(ViewMatchers.withId(bannerActivity.banner_id)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Assert.assertTrue( + "Wrong Ad Width", + bannerActivity.banner.getChildAt(0).width.dp >= (bannerActivity.banner.adWidth - 1) || + bannerActivity.banner.getChildAt(0).width.dp <= (bannerActivity.banner.adWidth + 1) + ) + Assert.assertTrue( + "Wrong Ad Height", + bannerActivity.banner.getChildAt(0).height.dp >= (bannerActivity.banner.adHeight - 1) || + bannerActivity.banner.getChildAt(0).height.dp <= (bannerActivity.banner.adHeight + 1) + ) + + Thread.sleep(2000) + + checkVisibilityDetectorMap(0, bannerActivity) + + } + + private fun setVisibility(visibility: Int) { + Handler(Looper.getMainLooper()).post({ + bannerActivity.banner.visibility = visibility + }) + } + +} diff --git a/tests/AppNexusSDKTestApp/app/src/androidTest/java/appnexus/com/appnexussdktestapp/functional/mar/MARCombinationVisibilityDetectorTest.kt b/tests/AppNexusSDKTestApp/app/src/androidTest/java/appnexus/com/appnexussdktestapp/functional/mar/MARCombinationVisibilityDetectorTest.kt new file mode 100644 index 000000000..ed272a283 --- /dev/null +++ b/tests/AppNexusSDKTestApp/app/src/androidTest/java/appnexus/com/appnexussdktestapp/functional/mar/MARCombinationVisibilityDetectorTest.kt @@ -0,0 +1,348 @@ +/* + * Copyright 2019 APPNEXUS INC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package appnexus.com.appnexussdktestapp.functional.mar + +import android.content.Intent +import android.os.Handler +import android.os.Looper +import android.view.View +import androidx.test.espresso.Espresso +import androidx.test.espresso.IdlingPolicies +import androidx.test.espresso.IdlingRegistry +import androidx.test.espresso.assertion.ViewAssertions +import androidx.test.espresso.matcher.ViewMatchers +import androidx.test.rule.ActivityTestRule +import androidx.test.runner.AndroidJUnit4 +import appnexus.com.appnexussdktestapp.MARLoadAndDisplayActivity +import appnexus.com.appnexussdktestapp.R +import appnexus.com.appnexussdktestapp.util.Utility.Companion.checkVisibilityDetectorMap +import com.appnexus.opensdk.SDKSettings +import kotlinx.android.synthetic.main.activity_mar_load.* +import org.hamcrest.Matchers.not +import org.junit.* +import org.junit.runner.RunWith +import java.util.concurrent.TimeUnit + + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class MARCombinationVisibilityDetectorTest { + + @Rule + @JvmField + var mActivityTestRule = ActivityTestRule(MARLoadAndDisplayActivity::class.java, false, false) + + internal lateinit var myActivity: MARLoadAndDisplayActivity + internal var arrayListAdType = ArrayList() + internal var arrayListBidType = ArrayList() + + + @Before + fun setup() { + IdlingPolicies.setMasterPolicyTimeout(1, TimeUnit.MINUTES) + IdlingPolicies.setIdlingResourceTimeout(1, TimeUnit.MINUTES) + arrayListAdType.clear() + arrayListBidType.clear() + } + + @After + fun destroy() { + IdlingRegistry.getInstance().unregister(myActivity.idlingResource) + } + + @Test + fun testMARCombinationTwoRTBBanner() { + SDKSettings.setCountImpressionOn1pxRendering(true) + setupTwoRTBBanner() + val intent = Intent() + intent.putExtra(MARLoadAndDisplayActivity.AD_TYPE, arrayListAdType) + intent.putExtra(MARLoadAndDisplayActivity.BID_TYPE, arrayListBidType) + mActivityTestRule.launchActivity(intent) + myActivity = mActivityTestRule.getActivity() as MARLoadAndDisplayActivity + setVisibility(View.GONE) + IdlingRegistry.getInstance().register(myActivity.idlingResource) + + checkVisibilityDetectorMap(0, myActivity) + + Espresso.onView(ViewMatchers.withId(R.id.recyclerListAdView)) + .check(ViewAssertions.matches(not(ViewMatchers.isDisplayed()))) + + Thread.sleep(2000) + + checkVisibilityDetectorMap(2, myActivity) + + Thread.sleep(2000) + + setVisibility(View.VISIBLE) + + Thread.sleep(2000) + + checkVisibilityDetectorMap(0, myActivity) + + + Assert.assertTrue( + "MultiAdRequest is still in progress, Idling Resource isn't working properly", + myActivity.multiAdRequestCompleted + ) + } + + private fun setVisibility(visibility: Int) { + Handler(Looper.getMainLooper()).post({ + myActivity.recyclerListAdView.visibility = visibility + }) + } + + @Test + fun testMARCombinationFourRTBBanner() { + SDKSettings.setCountImpressionOn1pxRendering(true) + setupTwoRTBBanner() + setupTwoRTBBanner() + val intent = Intent() + intent.putExtra(MARLoadAndDisplayActivity.AD_TYPE, arrayListAdType) + intent.putExtra(MARLoadAndDisplayActivity.BID_TYPE, arrayListBidType) + mActivityTestRule.launchActivity(intent) + myActivity = mActivityTestRule.getActivity() as MARLoadAndDisplayActivity + setVisibility(View.GONE) + IdlingRegistry.getInstance().register(myActivity.idlingResource) + + checkVisibilityDetectorMap(0, myActivity) + + Espresso.onView(ViewMatchers.withId(R.id.recyclerListAdView)) + .check(ViewAssertions.matches(not(ViewMatchers.isDisplayed()))) + + Thread.sleep(2000) + + checkVisibilityDetectorMap(4, myActivity) + + Thread.sleep(2000) + + setVisibility(View.VISIBLE) + + Thread.sleep(1000) + + myActivity.recyclerListAdView.smoothScrollToPosition(3) + + Thread.sleep(5000) + + checkVisibilityDetectorMap(0, myActivity) + + + Assert.assertTrue( + "MultiAdRequest is still in progress, Idling Resource isn't working properly", + myActivity.multiAdRequestCompleted + ) + } + + @Test + fun testMARCombinationTwelveRTBBanner() { + SDKSettings.setCountImpressionOn1pxRendering(true) + for (i in 0..5) { + setupTwoRTBBanner() + } + val intent = Intent() + intent.putExtra(MARLoadAndDisplayActivity.AD_TYPE, arrayListAdType) + intent.putExtra(MARLoadAndDisplayActivity.BID_TYPE, arrayListBidType) + mActivityTestRule.launchActivity(intent) + myActivity = mActivityTestRule.getActivity() as MARLoadAndDisplayActivity + setVisibility(View.GONE) + IdlingRegistry.getInstance().register(myActivity.idlingResource) + + checkVisibilityDetectorMap(0, myActivity) + + Espresso.onView(ViewMatchers.withId(R.id.recyclerListAdView)) + .check(ViewAssertions.matches(not(ViewMatchers.isDisplayed()))) + + Thread.sleep(2000) + + checkVisibilityDetectorMap(12, myActivity) + + Thread.sleep(2000) + + setVisibility(View.VISIBLE) + + Thread.sleep(1000) + + myActivity.recyclerListAdView.smoothScrollToPosition(5) + + Thread.sleep(5000) + + checkVisibilityDetectorMap(6, myActivity) + + myActivity.recyclerListAdView.smoothScrollToPosition(11) + + Thread.sleep(5000) + + checkVisibilityDetectorMap(0, myActivity) + + Assert.assertTrue( + "MultiAdRequest is still in progress, Idling Resource isn't working properly", + myActivity.multiAdRequestCompleted + ) + } + + @Test + fun testMARCombinationTwoRTBNative() { + SDKSettings.setCountImpressionOn1pxRendering(true) + setupTwoRTBNative() + val intent = Intent() + intent.putExtra(MARLoadAndDisplayActivity.AD_TYPE, arrayListAdType) + intent.putExtra(MARLoadAndDisplayActivity.BID_TYPE, arrayListBidType) + mActivityTestRule.launchActivity(intent) + myActivity = mActivityTestRule.getActivity() as MARLoadAndDisplayActivity + myActivity.shouldDisplayNativeAd = false + checkVisibilityDetectorMap(0, myActivity) + IdlingRegistry.getInstance().register(myActivity.idlingResource) + + Espresso.onView(ViewMatchers.withId(R.id.recyclerListAdView)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + + checkVisibilityDetectorMap(2, myActivity) + + myActivity.shouldDisplayNativeAd = true + myActivity.refreshForVisibility() + + Thread.sleep(5000) + + checkVisibilityDetectorMap(0, myActivity) + + + Assert.assertTrue( + "MultiAdRequest is still in progress, Idling Resource isn't working properly", + myActivity.multiAdRequestCompleted + ) + } + + @Test + fun testMARCombinationFourRTBNative() { + SDKSettings.setCountImpressionOn1pxRendering(true) + setupTwoRTBNative() + setupTwoRTBNative() + val intent = Intent() + intent.putExtra(MARLoadAndDisplayActivity.AD_TYPE, arrayListAdType) + intent.putExtra(MARLoadAndDisplayActivity.BID_TYPE, arrayListBidType) + mActivityTestRule.launchActivity(intent) + myActivity = mActivityTestRule.getActivity() as MARLoadAndDisplayActivity + myActivity.shouldDisplayNativeAd = false + checkVisibilityDetectorMap(0, myActivity) + IdlingRegistry.getInstance().register(myActivity.idlingResource) + + Espresso.onView(ViewMatchers.withId(R.id.recyclerListAdView)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + + Thread.sleep(2000) + + checkVisibilityDetectorMap(4, myActivity) + + Thread.sleep(2000) + + myActivity.shouldDisplayNativeAd = true + myActivity.refreshForVisibility() + + Thread.sleep(1000) + + myActivity.recyclerListAdView.smoothScrollToPosition(3) + + Thread.sleep(5000) + + checkVisibilityDetectorMap(0, myActivity) + + + Assert.assertTrue( + "MultiAdRequest is still in progress, Idling Resource isn't working properly", + myActivity.multiAdRequestCompleted + ) + } + + @Test + fun testMARCombinationTwelveRTBNative() { + SDKSettings.setCountImpressionOn1pxRendering(true) + for (i in 0..5) { + setupTwoRTBNative() + } + val intent = Intent() + intent.putExtra(MARLoadAndDisplayActivity.AD_TYPE, arrayListAdType) + intent.putExtra(MARLoadAndDisplayActivity.BID_TYPE, arrayListBidType) + mActivityTestRule.launchActivity(intent) + myActivity = mActivityTestRule.getActivity() as MARLoadAndDisplayActivity + myActivity.shouldDisplayNativeAd = false + IdlingRegistry.getInstance().register(myActivity.idlingResource) + + checkVisibilityDetectorMap(0, myActivity) + + Espresso.onView(ViewMatchers.withId(R.id.recyclerListAdView)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + + Thread.sleep(2000) + + checkVisibilityDetectorMap(myActivity.recyclerListAdView.childCount, myActivity) + + Thread.sleep(2000) + + myActivity.shouldDisplayNativeAd = true + myActivity.refreshForVisibility() + + Thread.sleep(1000) + + myActivity.recyclerListAdView.smoothScrollToPosition(myActivity.recyclerListAdView.childCount + 1) + + Thread.sleep(5000) + + checkVisibilityDetectorMap(myActivity.recyclerListAdView.childCount, myActivity) + + myActivity.recyclerListAdView.smoothScrollToPosition(11) + + Thread.sleep(5000) + + checkVisibilityDetectorMap(0, myActivity) + + Assert.assertTrue( + "MultiAdRequest is still in progress, Idling Resource isn't working properly", + myActivity.multiAdRequestCompleted + ) + } + + private fun setupTwoRTBBanner() { + addBanner() + addRTB() + addBanner() + addRTB() + } + + private fun setupTwoRTBNative() { + addNative() + addRTB() + addNative() + addRTB() + } + + private fun addRTB() { + arrayListBidType.add("RTB") + } + + private fun addBanner() { + arrayListAdType.add("Banner") + } + + private fun addNative() { + arrayListAdType.add("Native") + } + +} \ No newline at end of file diff --git a/tests/AppNexusSDKTestApp/app/src/androidTest/java/appnexus/com/appnexussdktestapp/placement/native/NativeOnePxTest.kt b/tests/AppNexusSDKTestApp/app/src/androidTest/java/appnexus/com/appnexussdktestapp/placement/native/NativeOnePxTest.kt new file mode 100644 index 000000000..1411e357d --- /dev/null +++ b/tests/AppNexusSDKTestApp/app/src/androidTest/java/appnexus/com/appnexussdktestapp/placement/native/NativeOnePxTest.kt @@ -0,0 +1,179 @@ +/* + * Copyright 2019 APPNEXUS INC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package appnexus.com.appnexussdktestapp.placement.native + +import android.content.Intent +import android.os.Handler +import android.os.Looper +import android.view.View +import androidx.test.espresso.Espresso +import androidx.test.espresso.IdlingPolicies +import androidx.test.espresso.IdlingRegistry +import androidx.test.espresso.assertion.ViewAssertions +import androidx.test.espresso.matcher.ViewMatchers +import androidx.test.rule.ActivityTestRule +import androidx.test.runner.AndroidJUnit4 +import appnexus.com.appnexussdktestapp.NativeActivity +import appnexus.com.appnexussdktestapp.R +import appnexus.com.appnexussdktestapp.util.Utility.Companion.checkVisibilityDetectorMap +import com.appnexus.opensdk.SDKSettings +import com.microsoft.appcenter.espresso.Factory +import kotlinx.android.synthetic.main.layout_native.* +import org.hamcrest.Matchers.not +import org.junit.After +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import java.util.concurrent.TimeUnit + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class NativeOnePxTest { + + @get:Rule + var reportHelper = Factory.getReportHelper() + + @Rule + @JvmField + var mActivityTestRule = ActivityTestRule(NativeActivity::class.java, false, false) + + lateinit var nativeActivity: NativeActivity + + @Before + fun setup() { + IdlingPolicies.setMasterPolicyTimeout(1, TimeUnit.MINUTES) + IdlingPolicies.setIdlingResourceTimeout(1, TimeUnit.MINUTES) + var intent = Intent() + mActivityTestRule.launchActivity(intent) + nativeActivity = mActivityTestRule.activity + IdlingRegistry.getInstance().register(nativeActivity.idlingResource) + } + + @After + fun destroy() { + SDKSettings.setCountImpressionOn1pxRendering(false) + IdlingRegistry.getInstance().unregister(nativeActivity.idlingResource) + reportHelper.label("Stopping App") + } + + /* + * Sanity Test for the Native Ad + * */ + @Test + fun nativeAdLoadTestOnePx() { + + SDKSettings.setCountImpressionOn1pxRendering(true) + + nativeActivity.shouldDisplay = false + + nativeActivity.triggerAdLoad("17982237", creativeId = 182426521) + + Espresso.onView(ViewMatchers.withId(R.id.title)) + .check(ViewAssertions.matches(not(ViewMatchers.isDisplayed()))) + + checkVisibilityDetectorMap(1, nativeActivity) + + setVisibility(View.VISIBLE) + + Espresso.onView(ViewMatchers.withId(R.id.title)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView(ViewMatchers.withId(R.id.description)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Thread.sleep(5000) + Espresso.onView(ViewMatchers.withId(R.id.icon)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView(ViewMatchers.withId(R.id.image)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + + checkVisibilityDetectorMap(0, nativeActivity) + } + + /* + * Sanity Test for the Native Ad + * */ + @Test + fun nativeAdLoadBGTest() { + + SDKSettings.setCountImpressionOn1pxRendering(true) + + nativeActivity.shouldDisplay = false + + nativeActivity.triggerAdLoad("17982237", creativeId = 182426521, bgTask = true) + + Espresso.onView(ViewMatchers.withId(R.id.title)) + .check(ViewAssertions.matches(not(ViewMatchers.isDisplayed()))) + + checkVisibilityDetectorMap(1, nativeActivity) + + setVisibility(View.VISIBLE) + + Espresso.onView(ViewMatchers.withId(R.id.title)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView(ViewMatchers.withId(R.id.description)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Thread.sleep(5000) + Espresso.onView(ViewMatchers.withId(R.id.icon)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView(ViewMatchers.withId(R.id.image)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + + checkVisibilityDetectorMap(0, nativeActivity) + } + + /* + * Sanity Test for the Native Ad + * */ + @Test + fun nativeAdLoadBGExecutorTest() { + + SDKSettings.setCountImpressionOn1pxRendering(true) + + nativeActivity.shouldDisplay = false + + nativeActivity.triggerAdLoad("17982237", creativeId = 182426521, bgTask = true, useExecutor = true) + + Espresso.onView(ViewMatchers.withId(R.id.title)) + .check(ViewAssertions.matches(not(ViewMatchers.isDisplayed()))) + + checkVisibilityDetectorMap(1, nativeActivity) + + setVisibility(View.VISIBLE) + + Espresso.onView(ViewMatchers.withId(R.id.title)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView(ViewMatchers.withId(R.id.description)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Thread.sleep(5000) + Espresso.onView(ViewMatchers.withId(R.id.icon)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView(ViewMatchers.withId(R.id.image)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + + checkVisibilityDetectorMap(0, nativeActivity) + } + + private fun setVisibility(visibility: Int) { + Handler(Looper.getMainLooper()).post({ + nativeActivity.main_native.visibility = visibility + }) + } +} diff --git a/tests/AppNexusSDKTestApp/app/src/androidTest/java/appnexus/com/appnexussdktestapp/util/Utility.kt b/tests/AppNexusSDKTestApp/app/src/androidTest/java/appnexus/com/appnexussdktestapp/util/Utility.kt new file mode 100644 index 000000000..03a907d24 --- /dev/null +++ b/tests/AppNexusSDKTestApp/app/src/androidTest/java/appnexus/com/appnexussdktestapp/util/Utility.kt @@ -0,0 +1,36 @@ +package appnexus.com.appnexussdktestapp.util + +import android.content.Context +import android.os.Handler +import android.os.Looper +import android.util.Log +import android.view.View +import java.lang.ref.WeakReference + +class Utility { + companion object { + fun checkVisibilityDetectorMap(checkZero: Int, context: Context) { + + + Handler(Looper.getMainLooper()).post({ + val vDetector = Class.forName("com.appnexus.opensdk.VisibilityDetector") + val create = vDetector.getDeclaredMethod("create", WeakReference::class.java) + create.isAccessible = true + val weakReference = WeakReference(View(context)) + val vDetInst = create.invoke(null, weakReference) + + val destroy = vDetector.getDeclaredMethod("destroy", WeakReference::class.java) + destroy.isAccessible = true + destroy.invoke(vDetInst, weakReference) + + val declaredField = vDetector.getDeclaredField("viewListenerMap") + declaredField.isAccessible = true + var map = declaredField.get(vDetInst) as HashMap + Log.e("VISIBILITY", " Size: ${checkZero} ${map.size}"); + + junit.framework.Assert.assertTrue(map.size == checkZero) + + }) + } + } +} \ No newline at end of file diff --git a/tests/AppNexusSDKTestApp/app/src/main/AndroidManifest.xml b/tests/AppNexusSDKTestApp/app/src/main/AndroidManifest.xml index 928ab4e23..397eecea0 100644 --- a/tests/AppNexusSDKTestApp/app/src/main/AndroidManifest.xml +++ b/tests/AppNexusSDKTestApp/app/src/main/AndroidManifest.xml @@ -12,32 +12,32 @@ android:supportsRtl="true" android:theme="@style/AppTheme"> - - + + - - + + - - + + - - + + - - + + - - + + diff --git a/tests/AppNexusSDKTestApp/app/src/main/java/appnexus/com/appnexussdktestapp/BannerActivity.kt b/tests/AppNexusSDKTestApp/app/src/main/java/appnexus/com/appnexussdktestapp/BannerActivity.kt index dc3868dd5..fb838ab99 100644 --- a/tests/AppNexusSDKTestApp/app/src/main/java/appnexus/com/appnexussdktestapp/BannerActivity.kt +++ b/tests/AppNexusSDKTestApp/app/src/main/java/appnexus/com/appnexussdktestapp/BannerActivity.kt @@ -19,6 +19,7 @@ import com.squareup.picasso.Picasso class BannerActivity : AppCompatActivity(), AdListener { + var shouldDisplay: Boolean = true val banner_id: Int = 1234 lateinit var banner: BannerAdView var clickUrl: String? = "" @@ -56,6 +57,9 @@ class BannerActivity : AppCompatActivity(), AdListener { Toast.makeText(this, "AdLoaded", Toast.LENGTH_LONG).show() if (layout.childCount > 0) layout.removeAllViews() + if (!shouldDisplay) { + ad!!.visibility = View.GONE + } layout.addView(ad) if (!idlingResource.isIdleNow) idlingResource.decrement() diff --git a/tests/AppNexusSDKTestApp/app/src/main/java/appnexus/com/appnexussdktestapp/MARLoadAndDisplayActivity.kt b/tests/AppNexusSDKTestApp/app/src/main/java/appnexus/com/appnexussdktestapp/MARLoadAndDisplayActivity.kt index ba68cea82..e49414dba 100644 --- a/tests/AppNexusSDKTestApp/app/src/main/java/appnexus/com/appnexussdktestapp/MARLoadAndDisplayActivity.kt +++ b/tests/AppNexusSDKTestApp/app/src/main/java/appnexus/com/appnexussdktestapp/MARLoadAndDisplayActivity.kt @@ -19,6 +19,8 @@ package appnexus.com.appnexussdktestapp import android.app.Activity import android.os.Build import android.os.Bundle +import android.os.Handler +import android.os.Looper import android.webkit.WebView import android.widget.LinearLayout import android.widget.Toast @@ -40,9 +42,11 @@ class MARLoadAndDisplayActivity : Activity() { val BID_TYPE = "BID_TYPE" } + private lateinit var layoutManager: LinearLayoutManager var onLazyLoadAdLoaded: Boolean = false var onLazyAdLoaded: Boolean = false private var displayAd = true + var shouldDisplayNativeAd = true private val arrayListAd = ArrayList() private val arrayListAdUnits = ArrayList() internal var msg = "" @@ -57,7 +61,7 @@ class MARLoadAndDisplayActivity : Activity() { internal var nativePlacementId = placementID internal var videoCreativeId = 162035356 - internal var bannerCreativeId = 166843311 + internal var bannerCreativeId = 182424585 //166843311 internal var nativeCreativeId = 162039377 internal var interstitialCreativeId = 166843825 @@ -78,7 +82,7 @@ class MARLoadAndDisplayActivity : Activity() { } SDKSettings.useHttps(true) - var layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false) + layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false) recyclerListAdView.layoutManager = layoutManager recyclerListAdView.adapter = AdViewRecyclerAdapter(arrayListAd, this) @@ -166,6 +170,9 @@ class MARLoadAndDisplayActivity : Activity() { msg += "Native Ad Loaded\n" toast() arrayListAd.add(response) + (recyclerListAdView.adapter as AdViewRecyclerAdapter).setShouldDisplay( + shouldDisplayNativeAd + ) if (displayAd) recyclerListAdView.adapter!!.notifyDataSetChanged() if (!idlingResource.isIdleNow) @@ -411,6 +418,12 @@ class MARLoadAndDisplayActivity : Activity() { return videoAd } + fun refreshForVisibility() { + Handler(Looper.getMainLooper()).post({ + (recyclerListAdView.adapter as AdViewRecyclerAdapter).setShouldDisplay(shouldDisplayNativeAd) + }) + } + private fun toast() { if (displayAd) { Toast.makeText(this, msg, Toast.LENGTH_SHORT).show() diff --git a/tests/AppNexusSDKTestApp/app/src/main/java/appnexus/com/appnexussdktestapp/NativeActivity.kt b/tests/AppNexusSDKTestApp/app/src/main/java/appnexus/com/appnexussdktestapp/NativeActivity.kt index 5f109ae9b..9fcadd682 100644 --- a/tests/AppNexusSDKTestApp/app/src/main/java/appnexus/com/appnexussdktestapp/NativeActivity.kt +++ b/tests/AppNexusSDKTestApp/app/src/main/java/appnexus/com/appnexussdktestapp/NativeActivity.kt @@ -3,6 +3,7 @@ package appnexus.com.appnexussdktestapp import android.os.Bundle import android.os.Handler import android.os.Looper +import android.view.View import android.widget.ImageView import android.widget.TextView import android.widget.Toast @@ -13,10 +14,11 @@ import com.appnexus.opensdk.* import com.appnexus.opensdk.tasksmanager.TasksManager import com.appnexus.opensdk.utils.Settings import com.squareup.picasso.Picasso -import java.util.concurrent.ExecutorService +import kotlinx.android.synthetic.main.layout_native.* class NativeActivity : AppCompatActivity(), NativeAdRequestListener, NativeAdEventListener { + var shouldDisplay: Boolean = true var didLogImpression: Boolean = false lateinit var nativeAdRequest: NativeAdRequest var idlingResource: CountingIdlingResource = CountingIdlingResource("Native Load Count", true) @@ -51,6 +53,11 @@ class NativeActivity : AppCompatActivity(), NativeAdRequestListener, NativeAdEve bgTask: Boolean = false, useExecutor: Boolean = false ) { + if (!shouldDisplay) { + Handler(Looper.getMainLooper()).post({ + main_native.visibility = View.GONE + }) + } SDKSettings.enableBackgroundThreading(bgTask) Handler(Looper.getMainLooper()).post { diff --git a/tests/AppNexusSDKTestApp/app/src/main/java/appnexus/com/appnexussdktestapp/adapter/AdViewRecyclerAdapter.kt b/tests/AppNexusSDKTestApp/app/src/main/java/appnexus/com/appnexussdktestapp/adapter/AdViewRecyclerAdapter.kt index 43d1009e9..5fca444aa 100644 --- a/tests/AppNexusSDKTestApp/app/src/main/java/appnexus/com/appnexussdktestapp/adapter/AdViewRecyclerAdapter.kt +++ b/tests/AppNexusSDKTestApp/app/src/main/java/appnexus/com/appnexussdktestapp/adapter/AdViewRecyclerAdapter.kt @@ -4,17 +4,13 @@ import android.annotation.SuppressLint import android.content.Context import android.graphics.Bitmap import android.net.Uri -import android.os.Handler import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.* import androidx.recyclerview.widget.RecyclerView import appnexus.com.appnexussdktestapp.R -import com.appnexus.opensdk.AdView -import com.appnexus.opensdk.BannerAdView -import com.appnexus.opensdk.InterstitialAdView -import com.appnexus.opensdk.NativeAdResponse +import com.appnexus.opensdk.* import com.appnexus.opensdk.instreamvideo.Quartile import com.appnexus.opensdk.instreamvideo.VideoAd import com.appnexus.opensdk.instreamvideo.VideoAdPlaybackListener @@ -22,9 +18,14 @@ import com.appnexus.opensdk.utils.Clog import com.appnexus.opensdk.utils.ViewUtil import kotlinx.android.synthetic.main.layout_ad_view.view.* import java.util.* +import kotlin.collections.ArrayList class AdViewRecyclerAdapter(val items: ArrayList, val context: Context) : RecyclerView.Adapter() { + private var shouldDisplay: Boolean = true + + private var list: ArrayList = ArrayList() + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { return ViewHolder( LayoutInflater.from(context).inflate( @@ -33,6 +34,16 @@ class AdViewRecyclerAdapter(val items: ArrayList, val context: Context) : ) } + fun setShouldDisplay(display: Boolean) { + this.shouldDisplay = display + if (display) { + for (view in list) { + view.visibility = View.VISIBLE + } + list.clear() + } + } + override fun getItemCount(): Int { return items.size } @@ -184,10 +195,39 @@ class AdViewRecyclerAdapter(val items: ArrayList, val context: Context) : (builder.container!!.parent as ViewGroup).removeAllViews() val nativeContainer = builder.container - Handler().post { - layoutNative.removeAllViews() - layoutNative.addView(nativeContainer) + if (!shouldDisplay) { + nativeContainer!!.visibility = View.GONE + list.add(nativeContainer) + } else { + nativeContainer!!.visibility = View.VISIBLE } + NativeAdSDK.registerTracking(response, nativeContainer, object : NativeAdEventListener { + override fun onAdImpression() { + + } + + override fun onAdAboutToExpire() { + + } + + override fun onAdExpired() { + + } + + override fun onAdWasClicked() { + + } + + override fun onAdWasClicked(clickUrl: String?, fallbackURL: String?) { + + } + + override fun onAdWillLeaveApplication() { + + } + }) + layoutNative.removeAllViews() + layoutNative.addView(nativeContainer) } internal inner class NativeAdBuilder @SuppressLint("NewApi") diff --git a/tests/TrackerTestApp/.gitignore b/tests/TrackerTestApp/.gitignore new file mode 100644 index 000000000..603b14077 --- /dev/null +++ b/tests/TrackerTestApp/.gitignore @@ -0,0 +1,14 @@ +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +/build +/captures +.externalNativeBuild +.cxx diff --git a/tests/TrackerTestApp/app/.gitignore b/tests/TrackerTestApp/app/.gitignore new file mode 100644 index 000000000..42afabfd2 --- /dev/null +++ b/tests/TrackerTestApp/app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/tests/TrackerTestApp/app/browserstack_config.json b/tests/TrackerTestApp/app/browserstack_config.json new file mode 100644 index 000000000..1b46f72bd --- /dev/null +++ b/tests/TrackerTestApp/app/browserstack_config.json @@ -0,0 +1,9 @@ +{ + "devices": [ + "Google Pixel 3-9.0" + ], + "deviceLogs": true, + "networkLogs": true, + "project": "TrackerTestApp" + +} diff --git a/tests/TrackerTestApp/app/build.gradle b/tests/TrackerTestApp/app/build.gradle new file mode 100644 index 000000000..27b4f1145 --- /dev/null +++ b/tests/TrackerTestApp/app/build.gradle @@ -0,0 +1,58 @@ +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' +apply plugin: 'kotlin-android-extensions' +apply plugin: "com.browserstack.gradle" + +android { + compileSdkVersion 29 + buildToolsVersion "30.0.2" + + defaultConfig { + applicationId "appnexus.com.trackertestapp" + minSdkVersion 23 + targetSdkVersion 29 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + +} + +dependencies { + implementation fileTree(dir: "libs", include: ["*.jar"]) + implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" + implementation 'androidx.core:core-ktx:1.3.2' + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation 'androidx.constraintlayout:constraintlayout:2.0.4' + implementation 'androidx.recyclerview:recyclerview:1.1.0' + // AppNexus SDK + api project(':sdk') + api project(':instreamvideo') + // Testing + testImplementation 'junit:junit:4.13.1' + androidTestImplementation 'androidx.test.ext:junit:1.1.2' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' + androidTestImplementation 'androidx.test:rules:1.3.0' + androidTestImplementation 'androidx.test.espresso:espresso-intents:3.3.0' + androidTestImplementation 'androidx.test.espresso:espresso-web:3.3.0' + implementation 'androidx.test.espresso:espresso-idling-resource:3.3.0' + implementation 'com.squareup.picasso:picasso:2.71828' + androidTestImplementation 'com.microsoft.appcenter:espresso-test-extension:1.3' + androidTestImplementation "com.squareup.okhttp3:mockwebserver:3.6.0" + +} + +//configuration for ui test +browserStackConfig { + username = "mobilesdkteam1" + accessKey = "eAqGKNyysiKQmX1wDUQ4" + configFilePath = "$projectDir/browserstack_config.json" +} \ No newline at end of file diff --git a/tests/TrackerTestApp/app/proguard-rules.pro b/tests/TrackerTestApp/app/proguard-rules.pro new file mode 100644 index 000000000..481bb4348 --- /dev/null +++ b/tests/TrackerTestApp/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/tests/TrackerTestApp/app/src/androidTest/java/appnexus/com/trackertestapp/MockDispatcher.kt b/tests/TrackerTestApp/app/src/androidTest/java/appnexus/com/trackertestapp/MockDispatcher.kt new file mode 100644 index 000000000..a4db01238 --- /dev/null +++ b/tests/TrackerTestApp/app/src/androidTest/java/appnexus/com/trackertestapp/MockDispatcher.kt @@ -0,0 +1,31 @@ +package appnexus.com.trackertestapp + +import android.net.Uri +import com.appnexus.opensdk.ut.UTConstants +import okhttp3.mockwebserver.Dispatcher +import okhttp3.mockwebserver.MockResponse +import okhttp3.mockwebserver.RecordedRequest + +class MockDispatcher : Dispatcher() { + + lateinit var adType: String + var arrRequests = ArrayList() + override fun dispatch(request: RecordedRequest?): MockResponse { + println("DISPATCH: " + request?.path) + println("DISPATCH: " + request?.requestLine) + if (!arrRequests.contains(request?.requestLine)) { + request?.requestLine?.let { arrRequests.add(Uri.decode(it).toString()) } + } + when (adType) { + "BannerNativeRenderAd" -> return MockResponse().setBody(TestResponsesUT.BANNER_NATIVE_RENDERER).setResponseCode(200) + "BannerAd" -> return MockResponse().setBody(TestResponsesUT.BANNER_AD).setResponseCode(200) + "BannerAdOMID" -> return MockResponse().setBody(TestResponsesUT.BANNER_OMID_Ad).setResponseCode(200) + "BannerVideoAd" -> return MockResponse().setBody(TestResponsesUT.BANNER_VIDEO_AD).setResponseCode(200) + "NativeAd" -> return MockResponse().setBody(TestResponsesUT.NATIVE_AD).setResponseCode(200) + else -> { + return MockResponse().setBody("").setResponseCode(200) + } + } + } + +} \ No newline at end of file diff --git a/tests/TrackerTestApp/app/src/androidTest/java/appnexus/com/trackertestapp/TestResponsesUT.java b/tests/TrackerTestApp/app/src/androidTest/java/appnexus/com/trackertestapp/TestResponsesUT.java new file mode 100644 index 000000000..1c4ca15fe --- /dev/null +++ b/tests/TrackerTestApp/app/src/androidTest/java/appnexus/com/trackertestapp/TestResponsesUT.java @@ -0,0 +1,1010 @@ +/* + * Copyright 2013 APPNEXUS INC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package appnexus.com.trackertestapp; + +import com.appnexus.opensdk.ut.UTConstants; +import com.appnexus.opensdk.utils.Clog; + +import java.util.ArrayList; + + +public class TestResponsesUT { + + public static final long DELAY = 5000; + public static final long DELAY_IP = 2000; + public static final String RESPONSE_URL_PATH = "response_url?"; + public static final String IMPRESSION_URL_PATH = "impression_url?"; + public static final String NO_AD_URL_PATH = "no_ad?"; + public static final String NOTIFY_URL_PATH = "vast_track/v2?info¬ifyURL"; + public static final String SSM_URL_PATH = "ssm?"; + public static final String REQUEST_URL = "http://mobile.devnxs.net/request_url?"; + private static final String ICON_URL = "http://vcdn.adnxs.com/p/creative-image/2f/3d/a4/8d/2f3da48d-f786-4361-ab03-e9c0c6e941e4.png"; + private static final String IMAGE_URL = "http://vcdn.adnxs.com/p/creative-image/d0/4a/de/2d/d04ade2d-52dd-4b84-9aa9-6eaef24f0eda.png"; + public static String NO_AD_URL = "http://mobile.devnxs.net/no_ad_url?"; + public static String NOTIFY_URL = "http://mobile.devnxs.net/vast_track/v2?info¬ifyURL"; + public static String IMPRESSION_URL = ""; + public static String RESPONSE_URL = ""; + public static final String NO_BID_TRUE = "true"; + public static final String NO_BID_FALSE = "false"; + public static String SSM_URL = "http://nym1-mobile.adnxs.com/ssm"; + public static final String SSM_NO_URL = ""; + + public static void setTestURL(String url) { + RESPONSE_URL = url + RESPONSE_URL_PATH; + NO_AD_URL = url + NO_AD_URL_PATH; + NOTIFY_URL = url + NOTIFY_URL_PATH; + SSM_URL = url + SSM_URL_PATH; + IMPRESSION_URL = url + IMPRESSION_URL_PATH; + } + + public static final String DUMMY_BANNER_CONTENT = ""; + // private static final String AN_NATIVE_RESPONSE = "[{\"type\":\"%s\",\"title\":\"%s\",\"description\":\"%s\", \"desc2\":\"%s\",\"full_text\":\"%s\",\"context\":\"%s\",\"icon_img_url\":\"%s\",\"main_media\":%s,\"cta\":\"%s\",\"click_trackers\":[%s],\"impression_trackers\":[%s],\"rating\":%s,\"click_url\":\"%s\",\"click_fallback_url\":\"%s\",\"sponsored_by\":\"%s\",\"custom\":%s}]"; + private static final String AN_NATIVE_RESPONSE = "{\"title\": \"%s\", \"desc\": \"%s\", \"sponsored\": \"%s\", \"ctatext\": \"%s\", \"rating\": \"%s\", \"icon\": {\"url\": \"%s\", \"width\": %d, \"height\": %d}, \"main_img\": {\"url\": \"%s\", \"width\": %d, \"height\": %d}, \"link\": {\"url\": \"%s\", \"click_trackers\": [\"%s\"]}, \"impression_trackers\": [\"%s\"], \"id\": %d, \"desc2\": \"%s\"}"; + public static final String AN_NATIVE_VIDEO_RESPONSE = "{ \"title\": \"%s\", \"desc\": \"%s\", \"sponsored\": \"%s\", \"ctatext\": \"%s\", \"rating\": \"%d\", \"icon\": { \"url\": \"%s\", \"width\": %d, \"height\": %d }, \"main_img\": { \"url\": \"%s\", \"width\": %d, \"height\": %d }, \"link\": { \"url\": \"%s\", \"fallback_url\": \"%s\", \"click_trackers\": [ \"%s\", \"%s\", \"%s\" ] }, \"impression_trackers\": [ \"%s\", \"%s\", \"%s\", \"%s\" ], \"javascript_trackers\": \"%s\", \"id\": %d, \"displayurl\": \"%s\", \"likes\": \"%d\", \"downloads\": \"%d\", \"price\": \"%d\", \"saleprice\": \"%d\", \"phone\": \"%d\", \"address\": \"%s\", \"desc2\": \"%s\", \"video\": { \"content\": \"%s\" }, \"privacy_link\": \"%s\" }"; + private static final String MRAID_CONTENT = ""; + private static final String NATIVE_MAIN_MEDIA = "[{\"url\":\"%s\",\"width\":%d,\"height\":%d,\"label\":\"default\"},{\"url\":\"%s\",\"width\":%d,\"height\":%d},{\"url\":\"%s\",\"width\":%d,\"height\":%d}]"; + private static final String NATIVE_RATING = "{\"value\":%.2f,\"scale\":%.2f}"; + private static final String RTB_NATIVE_VIEWABILITY_CONFIG = ""; + private static final String RTB_NATIVE_RENDERER_VIEWABILITY_CONFIG = ""; + private static final String CSM_NATIVE_VIEWABILITY_CONFIG = ""; + // template strings + private static final String CLASSNAME = "com.appnexus.opensdk.testviews.%s"; + + //Cookie Strings + public static final String UUID_COOKIE_1 = "uuid2=1263546692102051030; Path=/; Max-Age=7776000; Expires=Wed, 07-Dec-2025 16:23:26 GMT; Domain=.adnxs.com; HttpOnly"; + public static final String UUID_COOKIE_RESET = "uuid2=-1; Path=/; Max-Age=314496000; Expires=Thu, 27-Aug-2026 18:28:50 GMT; Domain=.adnxs.com; HttpOnly"; + + // UT Response - Template String + public static final String RESPONSE = "{\"version\":\"3.0.0\",\"tags\":[{\"tag_id\":123456,\"auction_id\":\"123456789\",\"nobid\":\"%s\",\"no_ad_url\":\"%s\",\"timeout_ms\":10000,\"ad_profile_id\":98765,%s}]}"; + public static final String RESPONSE_ = "{\"version\":\"3.0.0\",\"tags\":[{\"tag_id\":987654,\"auction_id\":\"123456789\",\"nobid\":\"%s\",\"no_ad_url\":\"%s\",\"timeout_ms\":10000,\"ad_profile_id\":98765,%s}]}"; + + public static final String ADS = "\"ads\":[%s]"; + + // Ad objects + public static final String RTB_BANNER = "{\"content_source\":\"rtb\",\"ad_type\":\"banner\",\"buyer_member_id\":123,\"creative_id\":6332753,\"media_type_id\":1,\"media_subtype_id\":1,\"client_initiated_ad_counting\":true,\"rtb\":{\"banner\":{\"content\":\"%s\",\"width\":%d,\"height\":%d},\"trackers\":[{\"impression_urls\":[\"%s\"],\"video_events\":{}}]}}"; + public static final String RTB_BANNER_ = "{\"content_source\":\"rtb\",\"ad_type\":\"banner\",\"buyer_member_id\":456,\"creative_id\":1234567,\"media_type_id\":1,\"media_subtype_id\":1,\"client_initiated_ad_counting\":true,\"rtb\":{\"banner\":{\"content\":\"%s\",\"width\":%d,\"height\":%d},\"trackers\":[{\"impression_urls\":[\"%s\"],\"video_events\":{}}]}}"; + public static final String CSM_BANNER = "{\"content_source\":\"csm\",\"ad_type\":\"banner\",\"buyer_member_id\":123,\"creative_id\":44863345,\"media_type_id\":1,\"media_subtype_id\":1,\"client_initiated_ad_counting\":false,\"csm\":{\"banner\":{\"content\":\"%s\",\"width\":10,\"height\":10},\"timeout_ms\":500,\"handler\":[{\"param\":\"%s\",\"height\":\"%d\",\"width\":\"%d\",\"id\":\"%s\",\"type\":\"%s\",\"class\":\"%s\"},{\"param\":\"#{PARAM}\",\"height\":\"50\",\"width\":\"320\",\"id\":\"163441140754789_163441480754755\",\"type\":\"ios\",\"class\":\"DummyIOSClass\"}],\"trackers\":[{\"impression_urls\":[\"%s\"],\"video_events\":{}}],\"request_url\":\"%s\",\"response_url\":\"%s\"}}"; + public static final String CSM_BANNER_TIMEOUT_ZERO = "{\"content_source\":\"csm\",\"ad_type\":\"banner\",\"buyer_member_id\":123,\"creative_id\":44863345,\"media_type_id\":1,\"media_subtype_id\":1,\"client_initiated_ad_counting\":false,\"csm\":{\"banner\":{\"content\":\"%s\",\"width\":10,\"height\":10},\"timeout_ms\":0,\"handler\":[{\"param\":\"%s\",\"height\":\"%d\",\"width\":\"%d\",\"id\":\"%s\",\"type\":\"%s\",\"class\":\"%s\"},{\"param\":\"#{PARAM}\",\"height\":\"50\",\"width\":\"320\",\"id\":\"163441140754789_163441480754755\",\"type\":\"ios\",\"class\":\"DummyIOSClass\"}],\"trackers\":[{\"impression_urls\":[\"%s\"],\"video_events\":{}}],\"request_url\":\"%s\",\"response_url\":\"%s\"}}"; + public static final String CSM_BANNER_TIMEOUT_NON_ZERO = "{\"content_source\":\"csm\",\"ad_type\":\"banner\",\"buyer_member_id\":123,\"creative_id\":44863345,\"media_type_id\":1,\"media_subtype_id\":1,\"client_initiated_ad_counting\":false,\"csm\":{\"banner\":{\"content\":\"%s\",\"width\":10,\"height\":10},\"timeout_ms\":200,\"handler\":[{\"param\":\"%s\",\"height\":\"%d\",\"width\":\"%d\",\"id\":\"%s\",\"type\":\"%s\",\"class\":\"%s\"},{\"param\":\"#{PARAM}\",\"height\":\"50\",\"width\":\"320\",\"id\":\"163441140754789_163441480754755\",\"type\":\"ios\",\"class\":\"DummyIOSClass\"}],\"trackers\":[{\"impression_urls\":[\"%s\"],\"video_events\":{}}],\"request_url\":\"%s\",\"response_url\":\"%s\"}}"; + public static final String SSM_BANNER = "{\"content_source\":\"ssm\",\"ad_type\":\"banner\",\"buyer_member_id\":123,\"creative_id\":44863345,\"media_type_id\":1,\"media_subtype_id\":1,\"client_initiated_ad_counting\":false,\"ssm\":{\"banner\":{\"content\":\"%s\",\"width\":10,\"height\":10},\"timeout_ms\":500,\"handler\":[{\"url\":\"%s\"}],\"trackers\":[{\"impression_urls\":[\"%s\"],\"video_events\":{}}],\"request_url\":\"%s\",\"response_url\":\"%s\"}}"; + public static final String SSM_BANNER_TIMEOUT_ZERO = "{\"content_source\":\"ssm\",\"ad_type\":\"banner\",\"buyer_member_id\":123,\"creative_id\":44863345,\"media_type_id\":1,\"media_subtype_id\":1,\"client_initiated_ad_counting\":false,\"ssm\":{\"banner\":{\"content\":\"%s\",\"width\":10,\"height\":10},\"timeout_ms\":0,\"handler\":[{\"url\":\"%s\"}],\"trackers\":[{\"impression_urls\":[\"%s\"],\"video_events\":{}}],\"request_url\":\"%s\",\"response_url\":\"%s\"}}"; + public static final String SSM_BANNER_TIMEOUT_NON_ZERO = "{\"content_source\":\"ssm\",\"ad_type\":\"banner\",\"buyer_member_id\":123,\"creative_id\":44863345,\"media_type_id\":1,\"media_subtype_id\":1,\"client_initiated_ad_counting\":false,\"ssm\":{\"banner\":{\"content\":\"%s\",\"width\":10,\"height\":10},\"timeout_ms\":200,\"handler\":[{\"url\":\"%s\"}],\"trackers\":[{\"impression_urls\":[\"%s\"],\"video_events\":{}}],\"request_url\":\"%s\",\"response_url\":\"%s\"}}"; + public static final String RTB_NATIVE = "{\"content_source\":\"rtb\",\"ad_type\":\"native\",\"buyer_member_id\":958,\"creative_id\":47772560,\"media_type_id\":12,\"media_subtype_id\":65,\"client_initiated_ad_counting\":true,\"viewability\":{\"config\":\"%s\"},\"rtb\":{\"native\":%s}}"; + public static final String RTB_NATIVE_RENDERER = "{\"content_source\":\"rtb\",\"ad_type\":\"native\",\"buyer_member_id\":958,\"creative_id\":47772560,\"media_type_id\":12,\"media_subtype_id\":65,\"renderer_url\": \"http://dcdn.adnxs.com/renderer-content/59929529-1dfd-49c3-a19d-f863befc96d7\", \"renderer_id\": 88, \"client_initiated_ad_counting\":true,\"viewability\":{\"config\":\"%s\"},\"rtb\":{\"native\":%s}}"; + public static final String CSM_NATIVE = "{\"content_source\":\"csm\",\"ad_type\":\"native\",\"buyer_member_id\":958,\"creative_id\":44863492,\"media_type_id\":12,\"media_subtype_id\":65,\"client_initiated_ad_counting\":true,\"viewability\":{\"config\":\"%s\"},\"csm\": {\"timeout_ms\":500,\"handler\": [{\"type\": \"android\",\"class\": \"%s\",\"param\": \"%s\",\"id\": \"%s\"},{\"type\": \"ios\",\"class\": \"DummyIOSClass\",\"param\": \"#{PARAM}\",\"id\": \"210827375150_10154672419150151\"}],\"trackers\":[{\"impression_urls\":[\"%s\"],\"video_events\":{}}],\"request_url\": \"%s\",\"response_url\": \"%s\"}}"; + public static final String CSM_NATIVE_TIMEOUT_ZERO = "{\"content_source\":\"csm\",\"ad_type\":\"native\",\"buyer_member_id\":958,\"creative_id\":44863492,\"media_type_id\":12,\"media_subtype_id\":65,\"client_initiated_ad_counting\":true,\"viewability\":{\"config\":\"%s\"},\"csm\": {\"timeout_ms\":0,\"handler\": [{\"type\": \"android\",\"class\": \"%s\",\"param\": \"%s\",\"id\": \"%s\"},{\"type\": \"ios\",\"class\": \"DummyIOSClass\",\"param\": \"#{PARAM}\",\"id\": \"210827375150_10154672419150151\"}],\"trackers\":[{\"impression_urls\":[\"%s\"],\"video_events\":{}}],\"request_url\": \"%s\",\"response_url\": \"%s\"}}"; + public static final String CSM_NATIVE_TIMEOUT_NON_ZERO = "{\"content_source\":\"csm\",\"ad_type\":\"native\",\"buyer_member_id\":958,\"creative_id\":44863492,\"media_type_id\":12,\"media_subtype_id\":65,\"client_initiated_ad_counting\":true,\"viewability\":{\"config\":\"%s\"},\"csm\": {\"timeout_ms\":200,\"handler\": [{\"type\": \"android\",\"class\": \"%s\",\"param\": \"%s\",\"id\": \"%s\"},{\"type\": \"ios\",\"class\": \"DummyIOSClass\",\"param\": \"#{PARAM}\",\"id\": \"210827375150_10154672419150151\"}],\"trackers\":[{\"impression_urls\":[\"%s\"],\"video_events\":{}}],\"request_url\": \"%s\",\"response_url\": \"%s\"}}"; + public static final String NO_BID = "{\"version\":\"3.0.0\",\"tags\":[{\"tag_id\":123456789,\"auction_id\":\"3552547938089377051000000\",\"nobid\":true,\"ad_profile_id\":2707239}]}"; + public static final String RTB_VIDEO = "{\"content_source\":\"rtb\",\"ad_type\":\"video\",\"notify_url\":\"%s\",\"buyer_member_id\":123,\"creative_id\":6332753,\"media_type_id\":4,\"media_subtype_id\":64,\"client_initiated_ad_counting\":true,\"rtb\":{\"video\":{\"content\":\"%s\",\"duration_ms\":100}}}"; + public static final String CSR_NATIVE = "{\"version\":\"3.0.0\",\"tags\":[{\"tag_id\":16268678,\"auction_id\":\"4050477843877235823\",\"nobid\":false,\"no_ad_url\":\"https://nym1-mobile.adnxs.com/it\",\"timeout_ms\":0,\"ad_profile_id\":1266762,\"rtb_video_fallback\":false,\"ads\":[{\"content_source\":\"csr\",\"ad_type\":\"native\",\"buyer_member_id\":10094,\"creative_id\":163940558,\"media_type_id\":12,\"media_subtype_id\":65,\"brand_category_id\":17,\"client_initiated_ad_counting\":true,\"viewability\":{\"config\":\"\"},\"csr\":{\"timeout_ms\":500,\"handler\":[{\"type\":\"android\",\"class\":\"%s\",\"payload\":\"{\\\"placement_id\\\":\\\"333673923704415_469697383435401\\\"}\",\"id\":\"333673923704415_469697383435401\"},{\"type\":\"ios\",\"class\":\"ANAdAdapterCSRNativeBannerFacebook\",\"payload\":\"test param\",\"id\":\"333673923704415_469697383435401\"}],\"trackers\":[{\"impression_urls\":[\"https://nym1-mobile.adnxs.com/it\"],\"video_events\":{}}],\"request_url\":\"https://nym1-mobile.adnxs.com/mediation/v2/log_req\",\"response_url\":\"https://nym1-mobile.adnxs.com/mediation/v2/log_resp\"}}]}]}"; + + + //Tracker Test App Ad Responses + public static final String BANNER_NATIVE_RENDERER = "{\n" + + " \"version\": \"3.0.0\",\n" + + " \"tags\": [\n" + + " {\n" + + " \"uuid\": \"2B8364F3-2F85-4B25-B56A-6107394844E5\",\n" + + " \"tag_id\": 20331545,\n" + + " \"auction_id\": \"8634877977927625253\",\n" + + " \"nobid\": false,\n" + + " \"no_ad_url\": \""+UTConstants.REQUEST_BASE_URL_UT+"/it?an_audit=0&referrer=itunes.apple.com%2Fus%2Fapp%2Fappnexus-sdk-app%2Fid736869833&e=wqT_3QLhCaDhBAAAAwDWAAUBCOPs6IEGEKXU4MnX-M_qdxjIxcGZj4HNjnMqNgkAAAkCABEJBwgAABkJCQjgPyEJCQgAACkRCQAxCQnwfeA_MJn42Ak47k5A7k5IAFAAWKusamAAaKODhAF4AIABAYoBAJIBA1VTRJgBAaABAagBAbABALgBAcABAMgBAtABANgBAOABAPABAIoCPHVmKCdhJywgMjk1ODQzMSwgMTYxNDQyNzc0Nyk7dWYoJ3InLCAyNDk2Njc3NDksIC4fAPC2kgLhAyFIRkpaVVFpSTI1ME1FS1hCaG5jWUFDQ3JyR293QURnQVFBUkk3azVRbWZqWUNWZ0FZUGdHYUFCd0JuaWtoZ1NBQVFhSUFhU0dCSkFCQUpnQkFLQUJBYWdCQTdBQkFMa0JkYXNOYkpxWnFUX0JBWFdyRFd5YW1ha195UUZsUHBfVmxvSDNQOWtCQUFBQUFBQUE4RF9nQVFEMUFRQUFBQUNZQWdDZ0FnQzFBZ0FBQUFDOUFnASbgRGdBZ0RvQWdENEFnQ0FBd0dZQXdHNkF3bFRTVTR6T2pVeU1qbmdBLWtxaUFRQWtBUUFtQVFCd1FRAT0JAQhNa0UJCQEBFERZQkFEeBWFKEFBQWlBWHRLS2tGAQwBARQ4RC14QlEBCgkBCHdRVQkJAQEATRkoDEFBRFIuKAAAMi4oAPBAT0FGaUNmd0JjZW1vd1A0QmRfSXRBR0NCZ05WVTBTSUJnQ1FCZ0dZQmdDaEJwcVptWm1abWFrX3FBWUJzZ1lrQ1EBbQkBAEUdjABHHQwASR0MOHVBWUuaAokBIThSTE1IdzblASxxNnhxSUFRb0FER2EFaVxabXBQem9KVTBsT016bzFNakk1UU9rcVMRUQxQQV9VEQwMQUFBVx0MAFkdDABhHQwAYx0M8NBlQUEu2AIA4ALKqE3qAjRpdHVuZXMuYXBwbGUuY29tL3VzL2FwcC9hcHBuZXh1cy1zZGstYXBwL2lkNzM2ODY5ODMzgAMAiAMBkAMAmAMXoAMBqgMAwAPgqAHIAwDYA_mjeuADAOgDAvgDAIAEAJIEBi91dC92M5gEAKIEDzEwMy4xMjEuMTE1LjIxMKgEqpkFsgQQCAAQARisAiD6ASgAMAA4ArgEAMAEAMgEANIEDzEwMDk0I1NJTjM6NTIyOdoEAggA4AQB8ASlwYZ3ggUJNxGWIIgFAZgFAKAF_xEBGAHABQDJBQAFARTwP9IFCQkFC3QAAADYBQHgBQHwBQH6BQQIABAAkAYBmAYAuAYAwQYBHzAAAPC_0AbWM9oGFgoQCREZAVwQABgA4AYM8gYCCACABwGIBwCgB0G6Bw8BSEgYACAAMAA46Q1AAMgHxbgF0gcNFXQBOAjaBwYJJyTgB_mjeuoHAggA&s=0f0d3018df2c386c9d1ac8e2086039441e1f0a19\",\n" + + " \"timeout_ms\": 0,\n" + + " \"ad_profile_id\": 1266762,\n" + + " \"rtb_video_fallback\": false,\n" + + " \"ads\": [\n" + + " {\n" + + " \"content_source\": \"rtb\",\n" + + " \"ad_type\": \"native\",\n" + + " \"buyer_member_id\": 10094,\n" + + " \"creative_id\": 249667749,\n" + + " \"media_type_id\": 12,\n" + + " \"media_subtype_id\": 65,\n" + + " \"brand_category_id\": 0,\n" + + " \"renderer_url\": \"https://dcdn.adnxs.com/renderer-content/d78931fc-805c-455d-967a-0eb7fe141470\",\n" + + " \"renderer_id\": 989,\n" + + " \"client_initiated_ad_counting\": true,\n" + + " \"viewability\": {\n" + + " \"config\": \"\"\n" + + " },\n" + + " \"rtb\": {\n" + + " \"native\": {\n" + + " \"title\": \"This is a test creative\",\n" + + " \"desc\": \"This is body of a test creative\",\n" + + " \"sponsored\": \"Xandr-Test\",\n" + + " \"icon\": {\n" + + " \"url\": \"https://dcdn.adnxs.com/shftr/https%253A%252F%252Fcrcdn01.adnxs.com%252Fcreative%252Fp%252F10094%252F2020%252F10%252F9%252F21631506%252F2c29fe67-b1ff-493c-bd63-36f7c41e68a2.png/0/300/300\",\n" + + " \"width\": 300,\n" + + " \"height\": 300,\n" + + " \"prevent_crop\": true\n" + + " },\n" + + " \"link\": {\n" + + " \"url\": \"https://xandr.com\",\n" + + " \"click_trackers\": [\n" + + " \""+UTConstants.REQUEST_BASE_URL_UT+"/click?mpmZmZmZqT-amZmZmZmpPwAAAAAAAOA_mpmZmZmZqT-amZmZmZmpPyUqOHnFP9V3yGIw8wg0HXNjNjpgAAAAABk8NgFuJwAAbicAAAIAAACloOEOK5YaAAAAAABVU0QAVVNEAAEAAQCjAQAAAAABAQQCAAAAAMIAxyLCugAAAAA./bcr=AAAAAAAA8D8=/cnd=%218RLMHwiI250MEKXBhncYq6xqIAQoADGamZmZmZmpPzoJU0lOMzo1MjI5QOkqSQAAAAAAAPA_UQAAAAAAAAAAWQAAAAAAAAAAYQAAAAAAAAAAaQAAAAAAAAAAcQAAAAAAAAAAeAA./cca=MTAwOTQjU0lOMzo1MjI5/bn=89157/\"\n" + + " ]\n" + + " },\n" + + " \"impression_trackers\": [\n" + + " \""+UTConstants.REQUEST_BASE_URL_UT+"/it?an_audit=0&referrer=itunes.apple.com%2Fus%2Fapp%2Fappnexus-sdk-app%2Fid736869833&e=wqT_3QLpCcDpBAAAAwDWAAUBCOPs6IEGEKXU4MnX-M_qdxjIxcGZj4HNjnMqNgmamZmZmZmpPxGaAQgQmak_GQAFAQjgPyERGwApEQkAMQUauADgPzCZ-NgJOO5OQO5OSAJQpcGGd1irrGpgAGijg4QBeMW4BYABAYoBA1VTRJIBAQbwVZgBAaABAagBAbABALgBAcABBMgBAtABANgBAOABAPABAIoCPHVmKCdhJywgMjk1ODQzMSwgMTYxNDQyNzc0Nyk7dWYoJ3InLCAyNDk2Njc3NDksIDE2GR_wtpIC4QMhSEZKWlVRaUkyNTBNRUtYQmhuY1lBQ0Nyckdvd0FEZ0FRQVJJN2s1UW1mallDVmdBWVBnR2FBQndCbmlraGdTQUFRYUlBYVNHQkpBQkFKZ0JBS0FCQWFnQkE3QUJBTGtCZGFzTmJKcVpxVF9CQVhXckRXeWFtYWtfeVFGbFBwX1Zsb0gzUDlrQkFBQUFBQUFBOERfZ0FRRDFBUUFBQUFDWUFnQ2dBZ0MxQWdBQUFBQzlBZwEm4ERnQWdEb0FnRDRBZ0NBQXdHWUF3RzZBd2xUU1U0ek9qVXlNam5nQS1rcWlBUUFrQVFBbUFRQndRUQE9CQEITWtFCQkBARREWUJBRHgVhShBQUFpQVh0S0trRgEMAQEUOEQteEJRAQoJAQh3UVUJCQEBAE0ZKAxBQURSLigAADIuKADwQE9BRmlDZndCY2Vtb3dQNEJkX0l0QUdDQmdOVlUwU0lCZ0NRQmdHWUJnQ2hCcHFabVptWm1ha19xQVlCc2dZa0NRAW0JAQBFHYwARx0MAEkdDDh1QVlLmgKJASE4UkxNSHc25QEscTZ4cUlBUW9BREdhBWlcWm1wUHpvSlUwbE9Nem8xTWpJNVFPa3FTEVEMUEFfVREMDEFBQVcdDABZHQwAYR0MAGMdDPDQZUFBLtgCAOACyqhN6gI0aXR1bmVzLmFwcGxlLmNvbS91cy9hcHAvYXBwbmV4dXMtc2RrLWFwcC9pZDczNjg2OTgzM4ADAIgDAZADAJgDF6ADAaoDAMAD4KgByAMA2AP5o3rgAwDoAwL4AwCABACSBAYvdXQvdjOYBACiBA8xMDMuMTIxLjExNS4yMTCoBKqZBbIEEAgAEAEYrAIg-gEoADAAOAK4BADABADIBADSBA8xMDA5NCNTSU4zOjUyMjnaBAIIAeAEAfAEpcGGd4IFCTcRliCIBQGYBQCgBf8RARgBwAUAyQUABQEU8D_SBQkJBQt0AAAA2AUB4AUB8AUB-gUECAAQAJAGAZgGALgGAMEGAR8wAADwP9AG1jPaBhYKEAkRGQFcEAAYAOAGDPIGAggAgAcBiAcAoAdBugcPAUhIGAAgADAAOOkNQADIB8W4BdIHDRV0ATgI2gcGCSck4Af5o3rqBwIIAA..&s=40340ac69fb948357bdaf3b945e841658ce3a6d6\"\n" + + " ],\n" + + " \"id\": 249667749\n" + + " }\n" + + " }\n" + + " }\n" + + " ]\n" + + " }\n" + + " ]\n" + + "}"; + + public static final String BANNER_VIDEO_AD = "{\n" + + " \"version\": \"3.0.0\",\n" + + " \"tags\": [\n" + + " {\n" + + " \"tag_id\": 16417701,\n" + + " \"auction_id\": \"5531859120537927358\",\n" + + " \"nobid\": false,\n" + + " \"no_ad_url\": \""+UTConstants.REQUEST_BASE_URL_UT+"/it?an_audit=0&referrer=itunes.apple.com%2Fus%2Fapp%2Fappnexus-sdk-app%2Fid736869833&e=wqT_3QKSCXySBAAAAwDWAAUBCMP36fMFEL79q5n6kcfiTBgAKjYJAA0BABENCAQAGQkJCOA_IQkJCAAAKREJADEJCfCa4D8wpYfqBzi-B0C-B0gAUABYuaxgYABokUB4AIABAYoBAJIBA1VTRJgBAaABAagBAbABALgBA8ABAMgBAtABANgBAOABAPABAIoCWXVmKCdhJywgMjc2NzIwNywgMTU4NTA4NTM3OSk7dWYoJ2knLCAxNTUyMjgxLCAxNTg1MDg1Mzc5KTt1ZigncicsIDE3MjA1OTEyOCwgMTUZPPCfkgKhAyFYRWljcGdpcGw0TVBFUGpUaFZJWUFDQzVyR0F3QURnQVFBUkl2Z2RRcFlmcUIxZ0FZUF9fX184UGFBQndBWGdCZ0FFQmlBRUJrQUVCbUFFQm9BRUJxQUVEc0FFQXVRRXBpNGlEQUFEd1A4RUJLWXVJZ3dBQThEX0pBVGFSbXRSRm1PMF8yUUVBQUFBQUFBRHdQLUFCbWQ5ZTlRRQUUKG1BSUFvQUlBdFFJBRAAdg0ImHdBSUJ5QUlCMEFJQjJBSUI0QUlBNkFJQS1BSUFnQU1CbUFNQnFBTwXYoHVnTUpUbGxOTWpvME16WXg0QU9ISFlBRUFJZ0VBSkFFQUpnRUFjRUVBBWIBAQhESkIBBw0BCDBRUQkKJElBYVFOZ0VBUEUdLCBDSUJZa2lxUVUJJBhBRHdQN0VGDQ0UQUFBREJCDT8BAQB5FSgMQUFBTjIoAABaLigASDRBV2dqUVkumgKJASFHaENFRXc2pQEkdWF4Z0lBUW9BRBGMEER3UHpvMukAEFFJY2RTEX0MUEFfVREMDEFBQVcdDABZHQwAYR0MAGMNDKhnQnBBZUFBLtgC6AfgAsfTAeoCNGl0dW5lcy5hcHBsZS5jb20vdXMvYXBwAQTwvG5leHVzLXNkay1hcHAvaWQ3MzY4Njk4MzOAAwGIAwGQAwCYAxegAwGqAwDAA-CoAcgDANgD-aN64AMA6AMC-AMAgAQAkgQGL3V0L3YzmAQAogQLMTAuNzUuMTEuNDSoBACyBBcIABAEGMACIDIoASgCKAMoBCgFMAA4ArgEAMAEAMgEANIEDTk1OCNOWU0yOjQzNjHaBAIIAOAEAPAE-NOFUvoEEgkAAABAluRCQBEAAADAAppewIIFCTczNgmqIIgFAZgFAKAF_xEBGAHABQDJBQAFARDwP9IFCQFABQFo2AUB4AUB8AUB-gUECAAQAJAGAZgGALgGAMEGBSAsAPC_0AbWAtoGFgoQCREZAVwQABgA4AYE8gYCCACABwGIBwCgB0DIBwA.&s=571a47ebf4c02182eddc8edc156a3b1212484cbf\",\n" + + " \"timeout_ms\": 10000,\n" + + " \"ad_profile_id\": 27079,\n" + + " \"rtb_video_fallback\": false,\n" + + " \"ads\": [\n" + + " {\n" + + " \"content_source\": \"rtb\",\n" + + " \"ad_type\": \"video\",\n" + + " \"notify_url\": \""+UTConstants.REQUEST_BASE_URL_UT+"/vast_track/v2?info=ZQAAAAMArgAFAQnDe3peAAAAABG-_iqjjxzFTBnDe3peAAAAACD404VSKAAwvgc4vgdAx9FMSPP7jAJQpYfqB1gBYgItLWgBcAF4AIABAogBAJABwAKYATKgAQCoAfjThVKwAQE.&s=e16d7e8c3217476e84c0bda1789fee8d39f0313d&event_type=1\",\n" + + " \"usersync_url\": \"https%3A%2F%2Facdn.adnxs.com%2Fdmp%2Fasync_usersync.html\",\n" + + " \"buyer_member_id\": 958,\n" + + " \"creative_id\": 172059128,\n" + + " \"media_type_id\": 4,\n" + + " \"media_subtype_id\": 64,\n" + + " \"brand_category_id\": 0,\n" + + " \"client_initiated_ad_counting\": true,\n" + + " \"rtb\": {\n" + + " \"video\": {\n" + + " \"player_width\": 300,\n" + + " \"player_height\": 250,\n" + + " \"duration_ms\": 32000,\n" + + " \"playback_methods\": [\n" + + " \"auto_play_sound_off\"\n" + + " ],\n" + + " \"frameworks\": [\n" + + " \"vpaid_1_0\",\n" + + " \"vpaid_2_0\",\n" + + " \"mraid_1\",\n" + + " \"mraid_2\",\n" + + " \"ormma\"\n" + + " ],\n" + + " \"content\": \"adnxs00:00:32\"\n" + + " }\n" + + " }\n" + + " }\n" + + " ]\n" + + " }\n" + + " ]\n" + + "}\n"; + public static final String BANNER_OMID_Ad = "{\n" + + " \"version\": \"0.0.1\",\n" + + " \"tags\": [\n" + + " {\n" + + " \"tag_id\": 13457285,\n" + + " \"auction_id\": \"2291993975392492012\",\n" + + " \"nobid\": false,\n" + + " \"no_ad_url\": \""+UTConstants.REQUEST_BASE_URL_UT+"/it?e=wqT_3QLLBmxLAwAAAwDWAAUBCPqkptkFEOyz69SIjLPnHxj_EQEQASo2CQANAQARDQgEABkRCQAhEQkAKREJADERCfB7MIWvtQY4vgdAvgdIAFAAWPfiP2AAaJFAeACAAQGSAQNVU0SYAawCoAH6AagBAbABALgBAcABAMgBAtABANgBAOABAPABAIoCPHVmKCdhJywgMTEwNzYxNywgMTUyOTQ1MTEzMCk7dWYoJ3InLCAxMDIwNzA4MjksIDE1MhUf8JySAvkBIU5qdWd5Z2lmMnVrS0VLMzAxVEFZQUNEMzRqOHdBRGdBUUFSSXZnZFFoYS0xQmxnQVlQX19fXzhQYUFCd0FYZ0JnQUVCaUFFQmtBRUJtQUVCb0FFQnFBRURzQUVBdVFHUjd3cnc0WHFFUDhFQmtlOEs4T0Y2aERfSkFaZmk4WFhnd2UwXzJRRUFBQUFBQUFEd1AtQUJBUFVCBQ8oSmdDQUtBQ0FMVUMFEARMMAkI8ExNQUNBY2dDQWRBQ0FkZ0NBZUFDQU9nQ0FQZ0NBSUFEQVpBREFKZ0RBYWdEbjlycENyb0RDVTVaVFRJNk16WXpOUS4umgItIUd3cFB0Zzb8APDkOS1JX0lBUW9BRG9KVGxsTk1qb3pOak0x2ALoB-ACx9MB6gI9cGxheS5nb29nbGUuY29tL3N0b3JlL2FwcHMvZGV0YWlscz9pZD1jb20uYXBwbmV4dXMub3BlbnNka2FwcIADAYgDAZADAJgDF6ADAaoDAMADrALIAwDYA_zgWeADAOgDAvgDAIAEAJIEBi91dC92MpgEAKIECjEwLjEuMTMuNTaoBACyBBAIABABGKwCIPoBKAAwADgCuAQAwAQAyAQA0gQNOTU4I05ZTTI6MzYzNdoEAggA4AQB8ASt9NUw-gQSCUWHCEBKQEG0KMDMzCpAggUXY29tTq8AHIgFAZgFAKAFUfMY_wHABQDJBQVCFADwP9IFCXUCYNgFAeAFAfAFAfoFBAgAEACQBgCYBgC4BgE.&s=5fd01dbe8aba75c8a527ef1fa733fcb84ec8dd0c&referrer=play.google.com%2Fstore%2Fapps%2Fdetails%3Fid%3Dcom.appnexus.opensdkapp\",\n" + + " \"timeout_ms\": 10000,\n" + + " \"ad_profile_id\": 27079,\n" + + " \"ads\": [\n" + + " {\n" + + " \"content_source\": \"rtb\",\n" + + " \"ad_type\": \"banner\",\n" + + " \"buyer_member_id\": 958,\n" + + " \"creative_id\": 102070829,\n" + + " \"media_type_id\": 1,\n" + + " \"media_subtype_id\": 1,\n" + + " \"client_initiated_ad_counting\": true,\n" + + " \"rtb\": {\n" + + " \"banner\": {\n" + + " \"content\": \"
\",\n" + + " \"width\": 300,\n" + + " \"height\": 250\n" + + " },\n" + + " \"trackers\": [\n" + + " {\n" + + " \"impression_urls\": [\n" + + " \""+UTConstants.REQUEST_BASE_URL_UT+"/it?e=wqT_3QLWBmxWAwAAAwDWAAUBCPqkptkFEOyz69SIjLPnHxj_EQEwASo2CXsUrkfheoQ_EREJBBkADQEAIRESACkRCQAxDRqoADCFr7UGOL4HQL4HSAJQrfTVMFj34j9gAGiRQHjH0wSAAQGKAQNVU0SSAQEG9GgBmAGsAqAB-gGoAQGwAQC4AQHAAQTIAQLQAQDYAQDgAQDwAQCKAjx1ZignYScsIDExMDc2MTcsIDE1Mjk0NTExMzApO3VmKCdyJywgMTAyMDcwODI5LCAxNTI5NDUxMTMwKTuSAvkBIU5qdWd5Z2lmMnVrS0VLMzAxVEFZQUNEMzRqOHdBRGdBUUFSSXZnZFFoYS0xQmxnQVlQX19fXzhQYUFCd0FYZ0JnQUVCaUFFQmtBRUJtQUVCb0FFQnFBRURzQUVBdVFHUjd3cnc0WHFFUDhFQmtlOEs4T0Y2aERfSkFaZmk4WFhnd2UwXzJRRUFBQUFBQUFEd1AtQUJBUFVCQUFBQUFKZ0NBS0FDQUxVQ0FBQUFBTDBDQUFBQUFNQUNBY2dDQWRBQ0FkZ0NBZUFDQU9nQ0FQZ0NBSUFEQVpBREFKZ0RBYWdEbjlycENyb0RDVTVaVFRJNk16WXpOUS4umgItIUd3cFB0Zzb8APDcOS1JX0lBUW9BRG9KVGxsTk1qb3pOak0x2ALoB-ACx9MB6gI9cGxheS5nb29nbGUuY29tL3N0b3JlL2FwcHMvZGV0YWlscz9pZD1jb20uYXBwbmV4dXMub3BlbnNka2FwcIADAYgDAZADAJgDF6ADAaoDAMADrALIAwDYA_zgWeADAOgDAvgDAIAEAJIEBi91dC92MpgEAKIECjEwLjEuMTMuNTaoBACyBBAIABABGKwCIPoBKAAwADgCuAQAwAQAyAQA0gQNOTU4I05ZTTI6MzYzNdoEAggB4AQB8ARBdAz6BBIJRZJEQEpAEQAAAMDMzCpAggUXY29tTq8AHIgFAZgFAKAFUf4Y_wHABQDJBQVCFADwP9IFCQlObAAAANgFAeAFAfAFAfoFBAgAEACQBgCYBgC4BgE.&s=c60bf8485daaa47c88843086734eff52180ae5a5&referrer=play.google.com%2Fstore%2Fapps%2Fdetails%3Fid%3Dcom.appnexus.opensdkapp\"\n" + + " ],\n" + + " \"video_events\": {}\n" + + " }\n" + + " ]\n" + + " }\n" + + " }\n" + + " ]\n" + + " }\n" + + " ]\n" + + "}\n"; + public static final String BANNER_AD = "{\n" + + " \"version\": \"3.0.0\",\n" + + " \"tags\": [\n" + + " {\n" + + " \"uuid\": \"45022FA4-FEE0-4F7B-8E4A-21C37C5CDD3F\",\n" + + " \"tag_id\": 20331545,\n" + + " \"auction_id\": \"3845066681594113162\",\n" + + " \"nobid\": false,\n" + + " \"no_ad_url\": \""+UTConstants.REQUEST_BASE_URL_UT+"/it?an_audit=0&test=1&e=wqT_3QLAA6DAAQAAAwDWAAUBCO63g4IGEIrZ7fDl-JquNRjIxcGZj4HNjnMqNgkAAAkCABEJBwgAABkRCQAhEQkAKREJADERCfD9MJn42Ak47k5A7k5IAFAAWABgAGgAeACAAQCKAQCYAQCgAQCoAQGwAQC4AQHAAQDIAQDQAQDYAQDgAQHwAQDYAgDgAgCAAwCIAwGQAwCYAxegAwCqAwDAA6wCyAMA2AMA4AMA6AMC-AMAgAQAkgQGL3V0L3YzmAQAqAQAsgQQCAAQARj3AiCsBigAMAA4ArgEAMAEAMgEANoEAggA4AQB8AQAggUbY29tLmFwcG5leHVzLkFwcE5leHVzU0RLQXBwiAUBmAUAoAUAwAUAyQUAAAAAAADwP9IFCQkAAAAAAAAAANgFAOAFAPAFAPoFBAgAEACQBgCYBgC4BgDBBgAlBhjwv9oGFgoQBQwdAVwQABgA4AYA8gYCCACABwGIBwCgBwC6Bw4BREAYACAAMAA4AEAAyAcA0gcNCRE4ATVA2gcGCAAQABgA4AcA6gcCCAA.&s=448fe5fc4acace6a6076ae0d814492bcdfd249b5\",\n" + + " \"timeout_ms\": 0,\n" + + " \"ad_profile_id\": 0,\n" + + " \"rtb_video_fallback\": false,\n" + + " \"ads\": [\n" + + " {\n" + + " \"content_source\": \"rtb\",\n" + + " \"ad_type\": \"banner\",\n" + + " \"buyer_member_id\": 10094,\n" + + " \"creative_id\": 223272198,\n" + + " \"media_type_id\": 1,\n" + + " \"media_subtype_id\": 1,\n" + + " \"brand_category_id\": 0,\n" + + " \"client_initiated_ad_counting\": true,\n" + + " \"rtb\": {\n" + + " \"banner\": {\n" + + " \"content\": \"
\",\n" + + " \"width\": 300,\n" + + " \"height\": 250\n" + + " },\n" + + " \"trackers\": [\n" + + " {\n" + + " \"impression_urls\": [\n" + + " \""+UTConstants.REQUEST_BASE_URL_UT+"/it?an_audit=0&test=1&e=wqT_3QLFA6DFAQAAAwDWAAUBCO63g4IGEIrZ7fDl-JquNRjIxcGZj4HNjnMqNgkAAAkCABEJBwgAABkRCQAhEQkAKREJADERCfDeMJn42Ak47k5A7k5IAFCGurtqWABgAGgAeACAAQCKAQCYAawCoAH6AagBAbABALgBAcABA8gBANABANgBAOABAfABANgCAOACAIADAIgDAZADAJgDF6ADAKoDAMADrALIAwDYAwDgAwDoAwL4AwCABACSBAYvdXQvdjOYBACoBACyBBAIABABGPcCIKwGKAAwADgCuAQAwAQAyAQA2gQCCAHgBAHwBACCBRtjb20uYXBwbmV4dXMuQXBwTmV4dXNTREtBcHCIBQGYBQCgBQDABQDJBQAAAAAAAPA_0gUJCRHnaNgFAOAFAPAFAPoFBAgAEACQBgCYBgC4BgDBBhEjENoGFgoQEQ0RAVwQABgA4AYA8gYCCACABwGIBwCgBwC6Bw4BREAYACAAMAA4AEAAyAcA0gcNCS41AEDaBwYIABAAGADgBwDqBwIIAA..&s=43fe777de574d97b27f3546f595b79ed44c9285b\"\n" + + " ],\n" + + " \"video_events\": {}\n" + + " }\n" + + " ]\n" + + " }\n" + + " }\n" + + " ]\n" + + " }\n" + + " ]\n" + + "}"; + public static final String NATIVE_AD = "{\n" + + " \"version\": \"3.0.0\",\n" + + " \"tags\": [\n" + + " {\n" + + " \"tag_id\": 15740033,\n" + + " \"auction_id\": \"3579836792830527402\",\n" + + " \"nobid\": false,\n" + + " \"no_ad_url\": \""+UTConstants.REQUEST_BASE_URL_UT+"/it?an_audit=0&referrer=itunes.apple.com%2Fus%2Fapp%2Fappnexus-sdk-app%2Fid736869833&e=wqT_3QKtCKAtBAAAAwDWAAUBCIG7rucFEKqPprSK04jXMRi1zICs5JfXxSYqNgkAAAkCABEJBywAABkAAABA4XqEPyEREgApEQkAMREb8JowgdnABzjuTkDuTkgAUABYzrNsYABopJOGAXgAgAEBigEAkgEDVVNEmAEBoAEBqAEBsAEAuAEBwAEAyAEC0AEA2AEA4AEA8AEAigJZdWYoJ2EnLCAzMDM4MzE0LCAxNTU4OTQ1MTUzKTt1ZignaScsIDEwMDg3NzgsIDE1NTg5NDUxNTMpO3VmKCdyJywgMTU0NDg1ODA3LCAxNRk88JqSApECIXJ6N1J3UWlIaS1FTkVLLUkxVWtZQUNET3Myd3dBRGdBUUFSSTdrNVFnZG5BQjFnQVlLVUZhQUJ3QUhnQWdBRUFpQUVBa0FFQm1BRUJvQUVCcUFFRHNBRUF1UUdSN3dydzRYcUVQOEVCa2U4SzhPRjZoRF9KQVNSRzBvREZWUUJBMlFFQUFBQUFBQUR3UC1BQmlzazk5UQkULG1BSUFvQUlBdFFJQQEBAHYNCJh3QUlBeUFJQTBBSUEyQUlBNEFJQTZBSUEtQUlBZ0FNQm1BTUJxQU8F1Oh1Z01KVTBsT01Ub3pOVGMxNEFQTURJQUU4OERrQVlnRTljRGtBWkFFQUpnRUFRLi6aAmEhT0JDSDVnaQVAMRRAenJOc0lBUW9BREY3Rks1SDQB2AR6bzJcABRRTXdNU1EBqhhBQUFQQV9VEQwMQUFBVx0M9DQB2AIA4ALKqE3qAjRpdHVuZXMuYXBwbGUuY29tL3VzL2FwcC9hcHBuZXh1cy1zZGstYXBwL2lkNzM2ODY5ODMzgAMAiAMBkAMAmAMXoAMBqgMAwAOsAsgDANIDKAgAEiQ1MzYzOTYzYi02MWVhLTRiZmUtYjczMS05ZGE1MGFhNTJhYmPSAygIChIkZWY1NDA0ZTQtM2NmOS00YzNiLTkzNTAtYjRhOWE5YzA0YjU12AP5o3rgAwDoAwL4AwCABACSBAYvdXQvdjOYBACiBAsxMC4xNC4xMi40NagEhOYisgQQCAAQARisAiD6ASgAMAA4ArgEAMAEAMgEANIEDzEwMDk0I1NJTjE6MzU3NdoEAggA4AQB8ASviNVJ-gQSCQAAAECW5EJAEQAAAMACml7AggUJNzM2CfwgiAUBmAUAoAX_EQEYAcAFAMkFAAUBFPA_0gUJCQULdAAAANgFAeAFAfAFAfoFBAgAEACQBgGYBgC4BgDBBgEfLAAA8L_IBgDaBhYKEAkQGQFEEAAYAOAGDPIGAggAgAcBiAcA&s=6f2fa33f3b83d94aacc2346e5345dead47934579\",\n" + + " \"timeout_ms\": 0,\n" + + " \"ad_profile_id\": 1266762,\n" + + " \"ads\": [\n" + + " {\n" + + " \"content_source\": \"rtb\",\n" + + " \"ad_type\": \"native\",\n" + + " \"buyer_member_id\": 10094,\n" + + " \"creative_id\": 154485807,\n" + + " \"media_type_id\": 12,\n" + + " \"media_subtype_id\": 65,\n" + + " \"brand_category_id\": 0,\n" + + " \"client_initiated_ad_counting\": true,\n" + + " \"viewability\": {\n" + + " \"config\": \"\"\n" + + " },\n" + + " \"rtb\": {\n" + + " \"native\": {\n" + + " \"title\": \"Native Renderer Title\",\n" + + " \"desc\": \"Native Renderer Desc\",\n" + + " \"sponsored\": \"Abhishek Sharma\",\n" + + " \"ctatext\": \"NativeRendererCampaign\",\n" + + " \"icon\": {\n" + + " \"url\": \"https://www.keralagiftdelivery.com/images/gifts/G20101.jpg\",\n" + + " \"width\": 868,\n" + + " \"height\": 996,\n" + + " \"prevent_crop\": false\n" + + " },\n" + + " \"main_img\": {\n" + + " \"url\": \"https://www.keralagiftdelivery.com/images/gifts/G20101.jpg\",\n" + + " \"width\": 868,\n" + + " \"height\": 996,\n" + + " \"prevent_crop\": false\n" + + " },\n" + + " \"link\": {\n" + + " \"url\": \"https://appnexus.com\",\n" + + " \"click_trackers\": [\n" + + " \""+UTConstants.REQUEST_BASE_URL_UT+"/click?exSuR-F6hD97FK5H4XqEPwAAAEDheoQ_exSuR-F6hD97FK5H4XqEP6qHiaaYIq4xNSaARb5ciyaBnetcAAAAAIEs8ABuJwAAbicAAAIAAAAvRDUJzhkbAAAAAABVU0QAVVNEAAEAAQCkiQAAAAABAQQCAAAAAMIAfSJ4VQAAAAA./cpcpm=AAAAAAAAAAA=/bcr=AAAAAAAA8D8=/cnd=%21OBCH5giHi-ENEK-I1UkYzrNsIAQoADF7FK5H4XqEPzoJU0lOMTozNTc1QMwMSQAAAAAAAPA_UQAAAAAAAAAAWQAAAAAAAAAA/cca=MTAwOTQjU0lOMTozNTc1/bn=86603/\"\n" + + " ]\n" + + " },\n" + + " \"impression_trackers\": [\n" + + " \""+UTConstants.REQUEST_BASE_URL_UT+"/it?an_audit=0&referrer=itunes.apple.com%2Fus%2Fapp%2Fappnexus-sdk-app%2Fid736869833&e=wqT_3QK2CPA8NgQAAAMA1gAFAQiBu67nBRCqj6a0itOI1zEYtcyArOSX18UmKjYJexSuR-F6hD8RexSuR-F6hD8ZAAAAQAESACERGwApEQkAMREbqDCB2cAHOO5OQO5OSAJQr4jVSVjOs2xgAGikk4YBeMukBYABAYoBA1VTRJIFBvBPmAEBoAEBqAEBsAEAuAEBwAEEyAEC0AEA2AEA4AEA8AEAigJZdWYoJ2EnLCAzMDM4MzE0LCAxNTU4OTQ1MTUzKTt1ZignaScsIDEwMDg3NzhGHQAEcicBFBg0NDg1ODA3AQsZPPCakgKRAiFyejdSd1FpSGktRU5FSy1JMVVrWUFDRE9zMnd3QURnQVFBUkk3azVRZ2RuQUIxZ0FZS1VGYUFCd0FIZ0FnQUVBaUFFQWtBRUJtQUVCb0FFQnFBRURzQUVBdVFHUjd3cnc0WHFFUDhFQmtlOEs4T0Y2aERfSkFTUkcwb0RGVlFCQTJRRUFBQUFBQUFEd1AtQUJpc2s5OVEJFCxtQUlBb0FJQXRRSUEBAQB2DQiYd0FJQXlBSUEwQUlBMkFJQTRBSUE2QUlBLUFJQWdBTUJtQU1CcUFPBdTodWdNSlUwbE9NVG96TlRjMTRBUE1ESUFFODhEa0FZZ0U5Y0RrQVpBRUFKZ0VBUS4umgJhIU9CQ0g1Z2kFQDEUQHpyTnNJQVFvQURGN0ZLNUg0AdgEem8yXAAUUU13TVNRAaoYQUFBUEFfVREMDEFBQVcdDPReAdgCAOACyqhN6gI0aXR1bmVzLmFwcGxlLmNvbS91cy9hcHAvYXBwbmV4dXMtc2RrLWFwcC9pZDczNjg2OTgzM4ADAIgDAZADAJgDF6ADAaoDAMAD4KgByAMA0gMoCAASJDUzNjM5NjNiLTYxZWEtNGJmZS1iNzMxLTlkYTUwYWE1MmFiY9IDKAgKEiRlZjU0MDRlNC0zY2Y5LTRjM2ItOTM1MC1iNGE5YTljMDRiNTXYA_mjeuADAOgDAvgDAIAEAJIEBi91dC92M5gEAKIECzEwLjE0LjEyLjQ1qASE5iKyBBAIABABGKwCIPoBKAAwADgCuAQAwAQAyAQA0gQPMTAwOTQjU0lOMTozNTc12gQCCAHgBAHwBK-I1Un6BBIJAAAAQJbkQkARAAAAwAKaXsCCBQk3MzY4Njk4MzOIBQGYBQCgBf___________wHABQDJBQAAAAAAAPA_0gUJCQULdAAAANgFAeAFAfAFAfoFBAgAEACQBgGYBgC4BgDBBgEfLAAA8D_IBgDaBhYKEAkQGQFEEAAYAOAGDPIGAggAgAcBiAcA&s=91d3af1f75b86ccc77cdf065374aa86b1385b11f\"\n" + + " ],\n" + + " \"id\": 154485807\n" + + " }\n" + + " }\n" + + " }\n" + + " ]\n" + + " }\n" + + " ]\n" + + "}\n"; + + + public static String mediationNoFillThenCSRSuccessfull() { + return "{\"version\":\"3.0.0\",\"tags\":[{\"tag_id\":16268678,\"auction_id\":\"4050477843877235823\",\"nobid\":false,\"no_ad_url\":\"https://nym1-mobile.adnxs.com/it\",\"timeout_ms\":0,\"ad_profile_id\":1266762,\"rtb_video_fallback\":false,\"ads\":[{\"content_source\":\"csm\",\"ad_type\":\"native\",\"buyer_member_id\":10094,\"creative_id\":163940558,\"media_type_id\":12,\"media_subtype_id\":65,\"brand_category_id\":17,\"client_initiated_ad_counting\":true,\"viewability\":{\"config\":\"\"},\"csm\":{\"timeout_ms\":500,\"handler\":[{\"type\":\"android\",\"class\":\"com.appnexus.opensdk.testviews.MediatedNativeNoFill\",\"param\":\"test param\",\"id\":\"2038077109846299_2317914228529251\"},{\"type\":\"ios\",\"class\":\"ANAdAdapterNativeFacebook\",\"param\":\"test param\",\"id\":\"2038077109846299_2317914228529251\"}],\"trackers\":[{\"impression_urls\":[\"https://nym1-mobile.adnxs.com/it\"],\"video_events\":{}}],\"request_url\":\"https://nym1-mobile.adnxs.com/mediation/v2/log_req\",\"response_url\":\"https://nym1-mobile.adnxs.com/mediation/v2/log_resp\"}},{\"content_source\":\"csr\",\"ad_type\":\"native\",\"buyer_member_id\":10094,\"creative_id\":163940558,\"media_type_id\":12,\"media_subtype_id\":65,\"brand_category_id\":17,\"client_initiated_ad_counting\":true,\"viewability\":{\"config\":\"\"},\"csr\":{\"timeout_ms\":500,\"handler\":[{\"type\":\"android\",\"class\":\"com.appnexus.opensdk.testviews.CSRNativeSuccessful\",\"payload\":\"{\\\"placement_id\\\":\\\"333673923704415_469697383435401\\\"}\",\"id\":\"333673923704415_469697383435401\"},{\"type\":\"ios\",\"class\":\"ANAdAdapterCSRNativeBannerFacebook\",\"payload\":\"test param\",\"id\":\"333673923704415_469697383435401\"}],\"trackers\":[{\"impression_urls\":[\"https://nym1-mobile.adnxs.com/it\"],\"video_events\":{}}],\"request_url\":\"https://nym1-mobile.adnxs.com/mediation/v2/log_req\",\"response_url\":\"https://nym1-mobile.adnxs.com/mediation/v2/log_resp\"}}]}]}"; + } + + public static String csrNoFillThenMediationSuccessfull() { + return "{\"version\":\"3.0.0\",\"tags\":[{\"tag_id\":16268678,\"auction_id\":\"4050477843877235823\",\"nobid\":false,\"no_ad_url\":\"https://nym1-mobile.adnxs.com/it\",\"timeout_ms\":0,\"ad_profile_id\":1266762,\"rtb_video_fallback\":false,\"ads\":[{\"content_source\":\"csr\",\"ad_type\":\"native\",\"buyer_member_id\":10094,\"creative_id\":163940558,\"media_type_id\":12,\"media_subtype_id\":65,\"brand_category_id\":17,\"client_initiated_ad_counting\":true,\"viewability\":{\"config\":\"\"},\"csr\":{\"timeout_ms\":500,\"handler\":[{\"type\":\"android\",\"class\":\"com.appnexus.opensdk.testviews.CSRNativeNoFill\",\"payload\":\"{\\\"placement_id\\\":\\\"333673923704415_469697383435401\\\"}\",\"id\":\"333673923704415_469697383435401\"},{\"type\":\"ios\",\"class\":\"ANAdAdapterCSRNativeBannerFacebook\",\"payload\":\"test param\",\"id\":\"333673923704415_469697383435401\"}],\"trackers\":[{\"impression_urls\":[\"https://nym1-mobile.adnxs.com/it\"],\"video_events\":{}}],\"request_url\":\"https://nym1-mobile.adnxs.com/mediation/v2/log_req\",\"response_url\":\"https://nym1-mobile.adnxs.com/mediation/v2/log_resp\"}},{\"content_source\":\"csm\",\"ad_type\":\"native\",\"buyer_member_id\":10094,\"creative_id\":163940558,\"media_type_id\":12,\"media_subtype_id\":65,\"brand_category_id\":17,\"client_initiated_ad_counting\":true,\"viewability\":{\"config\":\"\"},\"csm\":{\"timeout_ms\":500,\"handler\":[{\"type\":\"android\",\"class\":\"com.appnexus.opensdk.testviews.MediatedNativeSuccessful\",\"param\":\"test param\",\"id\":\"2038077109846299_2317914228529251\"},{\"type\":\"ios\",\"class\":\"ANAdAdapterNativeFacebook\",\"param\":\"test param\",\"id\":\"2038077109846299_2317914228529251\"}],\"trackers\":[{\"impression_urls\":[\"https://nym1-mobile.adnxs.com/it\"],\"video_events\":{}}],\"request_url\":\"https://nym1-mobile.adnxs.com/mediation/v2/log_req\",\"response_url\":\"https://nym1-mobile.adnxs.com/mediation/v2/log_resp\"}}]}]}"; + } + + public static String csrNativeNofill() { + String className = createClassName("CSRNativeNoFill"); + return String.format(CSR_NATIVE, className); + } + + public static String csrNativeSuccessful() { + String className = createClassName("CSRNativeSuccessful"); + return String.format(CSR_NATIVE, className); + } + + public static String csrNativeSuccesfulWithMockTrackers(String impression, String click, String requestUrl, String responseUrl) { + String template = "{\"version\":\"3.0.0\",\"tags\":[{\"tag_id\":16268678,\"auction_id\":\"4050477843877235823\",\"nobid\":false,\"no_ad_url\":\"https://nym1-mobile.adnxs.com/it\",\"timeout_ms\":0,\"ad_profile_id\":1266762,\"rtb_video_fallback\":false,\"ads\":[{\"content_source\":\"csr\",\"ad_type\":\"native\",\"buyer_member_id\":10094,\"creative_id\":163940558,\"media_type_id\":12,\"media_subtype_id\":65,\"brand_category_id\":17,\"client_initiated_ad_counting\":true,\"viewability\":{\"config\":\"\"},\"csr\":{\"timeout_ms\":500,\"handler\":[{\"type\":\"android\",\"class\":\"%s\",\"payload\":\"{\\\"placement_id\\\":\\\"333673923704415_469697383435401\\\"}\",\"id\":\"333673923704415_469697383435401\"},{\"type\":\"ios\",\"class\":\"ANAdAdapterCSRNativeBannerFacebook\",\"payload\":\"test param\",\"id\":\"333673923704415_469697383435401\"}],\"trackers\":[{\"impression_urls\":[\"%s\"],\"video_events\":{},\"click_urls\":[\"%s\"]}],\"request_url\":\"%s\",\"response_url\":\"%s\"}}]}]}"; + return String.format(template, createClassName("CSRNativeSuccessful"), impression, click, requestUrl, responseUrl); + } + + public static String blank() { + return ""; + } + + public static String noResponse() { + return "{\"version\":\"3.0.0\",\"tags\":[{\"tag_id\":123456,\"auction_id\":\"1234567890\",\"nobid\":true,\"ad_profile_id\":98765}]}"; + } + + /** + * Returns a RTB HTML Banner UT Response + */ + public static String banner() { + String bannerContent = String.format(DUMMY_BANNER_CONTENT, "Test Banner Content"); + return templateBannerRTBAdsResponse(bannerContent, 320, 50, IMPRESSION_URL); + } + + /** + * Returns a RTB HTML Banner UT Response + */ + public static String banner_() { + String bannerContent = String.format(DUMMY_BANNER_CONTENT, "Test Banner Content"); + return templateBannerRTBAdsResponse_(bannerContent, 320, 50, IMPRESSION_URL); + } + + public static String blankBanner() { + return templateBannerRTBAdsResponse("", 320, 50, IMPRESSION_URL); + } + + public static String invalidBanner() { + return templateBannerRTBAdsResponse("Error", 320, 50, IMPRESSION_URL); + } + + public static String mraidBanner(String name) { + return mraidBanner(name, 320, 50); + } + + public static String mraidBanner(String name, int width, int height) { + String mraidContent = String.format(MRAID_CONTENT, name); + return templateBannerRTBAdsResponse(mraidContent, width, height, IMPRESSION_URL); + } + + + public static String mediatedSuccessfulBanner() { + return templateSingleCSMAdResponseBannerInterstitial(createClassName("MediatedBannerSuccessful"), RESPONSE_URL); + } + + public static String mediatedSuccessfulBannerTimeout() { + return templateSingleCSMAdResponseBannerInterstitialTimeout(createClassName("MediatedBannerSuccessful"), RESPONSE_URL); + } + + public static String mediatedSuccessfulBannerTimeoutNonZero() { + return templateSingleCSMAdResponseNativeTimeoutNonZero(createClassName("MediatedBannerSuccessful"), RESPONSE_URL); + } + + + public static String mediatedNoRequestBanner() { + return templateSingleCSMAdResponseBannerInterstitial(createClassName("MediatedBannerNoRequest"), RESPONSE_URL); + } + + public static String mediatedOutOfMemoryBanner() { + return templateSingleCSMAdResponseBannerInterstitial(createClassName("MediatedBannerOOM"), RESPONSE_URL); + } + + public static String mediatedNoFillBanner() { + return templateSingleCSMAdResponseBannerInterstitial(createClassName("MediatedBannerNoFillView"), RESPONSE_URL); + } + + public static String mediatedSuccessfulInterstitial() { + return templateSingleCSMAdResponseBannerInterstitial(createClassName("MediatedInterstitialSuccessful"), RESPONSE_URL); + } + public static String mediatedSuccessfulInterstitialTimeout() { + return templateSingleCSMAdResponseBannerInterstitialTimeout(createClassName("MediatedInterstitialSuccessful"), RESPONSE_URL); + } + + public static String mediatedSuccessfulInterstitialTimeoutNonZero() { + return templateSingleCSMAdResponseBannerInterstitialTimeoutNonZero(createClassName("MediatedInterstitialSuccessful"), RESPONSE_URL); + } + + public static String mediatedNoRequestInterstitial() { + return templateSingleCSMAdResponseBannerInterstitial(createClassName("MediatedInterstitialNoRequest"), RESPONSE_URL); + } + + public static String mediatedOutOfMemoryInterstitial() { + return templateSingleCSMAdResponseBannerInterstitial(createClassName("MediatedInterstitialOOM"), RESPONSE_URL); + } + + public static String mediatedNoFillInterstitial() { + return templateSingleCSMAdResponseBannerInterstitial(createClassName("MediatedInterstitialNoFillView"), RESPONSE_URL); + } + + public static String mediatedFakeClassBannerInterstitial() { + return templateSingleCSMAdResponseBannerInterstitial(createClassName("FakeClass"), RESPONSE_URL); + } + + public static String mediatedDummyClassBannerInterstitial() { + return templateSingleCSMAdResponseBannerInterstitial(createClassName("DummyClass"), RESPONSE_URL); + } + + + public static String mediatedSuccessfulNative() { + return templateSingleCSMAdResponseNative(createClassName("MediatedNativeSuccessful"), RESPONSE_URL); + } + + + public static String mediatedSuccessfulNativeTimeout() { + return templateSingleCSMAdResponseNativeTimeout(createClassName("MediatedNativeSuccessful"), RESPONSE_URL); + } + + + public static String mediatedSuccessfulNativeTimeoutNonZero() { + return templateSingleCSMAdResponseNativeTimeoutNonZero(createClassName("MediatedNativeSuccessful"), RESPONSE_URL); + } + + + public static String mediatedNoRequestNative() { + return templateSingleCSMAdResponseNative(createClassName("MediatedNativeNoRequest"), RESPONSE_URL); + } + + public static String mediatedOutOfMemoryNative() { + return templateSingleCSMAdResponseNative(createClassName("MediatedNativeOOM"), RESPONSE_URL); + } + + public static String mediatedNoFillNative() { + return templateSingleCSMAdResponseNative(createClassName("MediatedNativeNoFill"), RESPONSE_URL); + } + + + public static String mediatedFakeClass_Native() { + return templateSingleCSMAdResponseNative(createClassName("FakeClass"), RESPONSE_URL); + } + + public static String mediatedDummyClass_Native() { + return templateSingleCSMAdResponseNative(createClassName("DummyClass"), RESPONSE_URL); + } + + public static String mediatedSSMBanner() { + return templateSingleSSMAdResponse(); + } + + + public static String mediatedSSMBannerWithTimeoutZero() { + return templateSingleSSMAdResponseWithTimeoutZero(); + } + + public static String mediatedNoSSMBanner() { + return templateNoURLSSMResponse(); + } + + public static String noFillCSM_RTBBanner() { + //Create a CSM - Ad + String csmAd = templateSingleCSMAdResponseBannerInterstitial(createClassName("MediatedBannerNoFillView"), 320, 50, IMPRESSION_URL, REQUEST_URL, RESPONSE_URL, "", "", "android"); + + // Create a RTB Banner Ad + String bannerContent = String.format(DUMMY_BANNER_CONTENT, "Test Banner Content"); + String bannerAd = singleRTBBanner(bannerContent, 320, 50, IMPRESSION_URL); + + ArrayList adsArray = new ArrayList(2); + adsArray.add(csmAd); + adsArray.add(bannerAd); + + //Return a WaterFall response + return templateMediatedWaterFallResponses(adsArray.toArray(new String[adsArray.size()])); + } + + public static String noFillCSMBanner() { + //Create a CSM - Ad + String csmAd = templateSingleCSMAdResponseBannerInterstitial(createClassName("MediatedBannerNoFillView"), 320, 50, IMPRESSION_URL, REQUEST_URL, RESPONSE_URL, "", "", "android"); + + ArrayList adsArray = new ArrayList(1); + adsArray.add(csmAd); + + //Return a WaterFall response + return templateMediatedWaterFallResponses(adsArray.toArray(new String[adsArray.size()])); + } + + public static String noFillCSM_RTBInterstitial() { + //Create a CSM - Ad + String csmAd = templateSingleCSMAdResponseBannerInterstitial(createClassName("MediatedInterstitialNoFillView"), 320, 480, IMPRESSION_URL, REQUEST_URL, RESPONSE_URL, "", "", "android"); + + // Create a RTB Banner Ad + String bannerConetent = String.format(DUMMY_BANNER_CONTENT, "Test Banner Content"); + String bannerAd = singleRTBBanner(bannerConetent, 320, 480, IMPRESSION_URL); + + ArrayList adsArray = new ArrayList(2); + adsArray.add(csmAd); + adsArray.add(bannerAd); + + //Return a WaterFall response + return templateMediatedWaterFallResponses(adsArray.toArray(new String[adsArray.size()])); + } + + + public static String noFillCSM_RTBNative() { + //Create a CSM - Ad + String csmAd = templateSingleCSMAdResponseNative(createClassName("MediatedNativeNoFill"), "", "", IMPRESSION_URL, REQUEST_URL, RESPONSE_URL); + + // Create a RTB Banner Ad + String nativeResponse = templateNativeResponse("test title", "description", "desc2", "sponsored", "cta", + "5", "http://path_to_icon.com", 100, 100, "http://path_to_main.com", + 300, 200, "http://www.appnexus.com", "http://ib.adnxs.com/click...", + "http://ib.adnxs.com/it...", 111796070); + System.out.println(nativeResponse + "\n"); + String nativeRTB = templateRTBNativeAdResponse(nativeResponse); + + ArrayList adsArray = new ArrayList(2); + adsArray.add(csmAd); + adsArray.add(nativeRTB); + + //Return a WaterFall response + return templateMediatedWaterFallResponses(adsArray.toArray(new String[adsArray.size()])); + } + + private static String createClassName(String className) { + return String.format(CLASSNAME, className); + } + + private static String resultCB(int code) { + return String.format(RESPONSE_URL + "&reason=%d", code); + } + + + public static String waterfall_CSM_Banner_Interstitial(String[] classNames, String[] responseURLS) { + if (classNames.length != responseURLS.length) { + System.err.println("different numbers of class names and resultCBs"); + return ""; + } + + ArrayList adsArray = new ArrayList(classNames.length); + + for (int i = 0; i < classNames.length; i++) { + String singleCSMAd = templateSingleCSMAdResponseBannerInterstitial(createClassName(classNames[i]), 320, 50, IMPRESSION_URL, REQUEST_URL, responseURLS[i], "", "", "android"); + adsArray.add(singleCSMAd); + } + + return templateMediatedWaterFallResponses(adsArray.toArray(new String[adsArray.size()])); + } + + + // Just take in count here since SSM waterfall can be controlled by altering the response for handler URL response. + public static String waterfall_SSM_Banner_Interstitial(int count) { + + ArrayList adsArray = new ArrayList(count); + + for (int i = 0; i < count; i++) { + String ssmAdTag = String.format(SSM_BANNER, DUMMY_BANNER_CONTENT, SSM_URL, IMPRESSION_URL, REQUEST_URL, RESPONSE_URL); + adsArray.add(ssmAdTag); + } + + return templateMediatedWaterFallResponses(adsArray.toArray(new String[adsArray.size()])); + } + + + public static String waterfall_CSM_Native(String[] classNames, String[] responseURLS) { + if (classNames.length != responseURLS.length) { + System.err.println("different numbers of class names and resultCBs"); + return ""; + } + + ArrayList adsArray = new ArrayList(classNames.length); + + for (int i = 0; i < classNames.length; i++) { + String singleCSMAd = templateSingleCSMAdResponseNative(createClassName(classNames[i]), "", "", IMPRESSION_URL, REQUEST_URL, responseURLS[i]); + adsArray.add(singleCSMAd); + } + + return templateMediatedWaterFallResponses(adsArray.toArray(new String[adsArray.size()])); + } + + + private static String templateMediatedWaterFallResponses(String[] adsArray) { + StringBuilder sb = new StringBuilder(); + for (String handler : adsArray) { + sb.append(handler).append(","); + } + sb.deleteCharAt(sb.length() - 1); + return templateMediatedAdResponse(sb.toString()); + } + + + public static String callbacks(int testNumber) { + return templateSingleCSMAdResponseBannerInterstitial(createClassName("MediatedBannerCallbacksTestView"), RESPONSE_URL, String.valueOf(testNumber)); + } + + public static String anNativeTripleLift() { + return anNative().replace("\"buyer_member_id\":958", "\"buyer_member_id\":11217"); + } + + public static String anNative() { + String nativeResponse = templateNativeResponse("test title", "test description", "additional test description", "sponsored", "cta", + "5", "http://path_to_icon.com", 100, 150, "http://path_to_main.com", + 300, 200, "http://www.appnexus.com", "http://ib.adnxs.com/click...", + "http://ib.adnxs.com/it...", 111796070); + System.out.println(nativeResponse + "\n"); + String nativeRTB = templateRTBNativeAdResponse(nativeResponse); + System.out.println(nativeRTB + "\n"); + String ads = String.format(ADS, nativeRTB); + System.out.println(ads + "\n"); + return templateResponse(NO_BID_FALSE, NO_AD_URL, ads); + } + + public static String anOMIDNativeRTB() { + String nativeResponse = templateNativeResponse("test title", "test description", "additional test description", "sponsored", "cta", + "5", "http://path_to_icon.com", 100, 150, "http://path_to_main.com", + 300, 200, "http://www.appnexus.com", "http://ib.adnxs.com/click...", + "http://ib.adnxs.com/it...", 111796070); + System.out.println(nativeResponse + "\n"); + String nativeRTB = templateRTBNativeAdResponse(nativeResponse); + System.out.println(nativeRTB + "\n"); + String ads = String.format(ADS, nativeRTB); + System.out.println(ads + "\n"); + return templateResponse(NO_BID_FALSE, NO_AD_URL, ads); + } + + public static String anNativeVideo() { + String nativeResponse = templateNativeVideoResponse("test title", "test description", "additional test description", + "sponsored", "cta", 5, "http://path_to_icon.com", 100, 150, "http://path_to_main.com", + 300, 200, "http://www.appnexus.com", "http://ib.adnxs.com/fallback", + "http://ib.adnxs.com/click...1", "http://ib.adnxs.com/click...2", "http://ib.adnxs.com/click...3", + "http://ib.adnxs.com/it...1", "http://ib.adnxs.com/it...2", "http://ib.adnxs.com/it...3", "http://ib.adnxs.com/it...4", + "http://ib.adnxs.com/jt...", 123456789, "http://ib.adnxs.com/display...", 10, 1000, 5, 4, 987654321, + "AppNexus Address", "content", "http://ib.adnxs.com/privacy..."); + System.out.println(nativeResponse + "\n"); + String nativeRTB = templateRTBNativeAdResponse(nativeResponse); + System.out.println(nativeRTB + "\n"); + String ads = String.format(ADS, nativeRTB); + System.out.println(ads + "\n"); + return templateResponse(NO_BID_FALSE, NO_AD_URL, ads); + } + + public static String anNativeRenderer() { + String nativeResponse = templateNativeResponse("test title", "test description", "additional test description", "sponsored", "cta", + "5", "http://path_to_icon.com", 100, 150, "http://path_to_main.com", + 300, 200, "http://www.appnexus.com", "http://ib.adnxs.com/click...", + "http://ib.adnxs.com/it...", 111796070); + System.out.println(nativeResponse + "\n"); + String nativeRTB = String.format(RTB_NATIVE_RENDERER, RTB_NATIVE_RENDERER_VIEWABILITY_CONFIG, nativeResponse); + System.out.println(nativeRTB + "\n"); + String ads = String.format(ADS, nativeRTB); + System.out.println(ads + "\n"); + return templateResponse(NO_BID_FALSE, NO_AD_URL, ads); + } + + public static String anNativeWithoutImages() { + String nativeResponse = templateNativeResponse("test title", "test description", "additional test description", "sponsored", "cta", + "5", "", 0, 0, "http://path_to_main.com", + 300, 200, "http://www.appnexus.com", "http://ib.adnxs.com/click...", + "http://ib.adnxs.com/it...", 111796070); + System.out.println(nativeResponse + "\n"); + String nativeRTB = templateRTBNativeAdResponse(nativeResponse); + System.out.println(nativeRTB + "\n"); + String ads = String.format(ADS, nativeRTB); + System.out.println(ads + "\n"); + return templateResponse(NO_BID_FALSE, NO_AD_URL, ads); + } + + public static String nativeResponseWithImageAndIconUrl() { + String nativeResponse = templateNativeResponse("native", "test title", "test description", "additional test description", "full text", "newsfeed", + ICON_URL, templateNativeMainMedia(IMAGE_URL, 300, 200, "http://path_to_main2.com", 50, 50, "http://path_to_main3.com", 250, 250), + "install", "\"http://ib.adnxs.com/click...\"", "\"http://ib.adnxs.com/it...\"", templateNativeRating(4f, 5f), "http://www.appnexus.com", "http://www.google.com", "test sponsored by", "{\"key\":\"value\"}" + ); + System.out.println(nativeResponse + "\n"); + String nativeRTB = templateRTBNativeAdResponse(nativeResponse); + System.out.println(nativeRTB + "\n"); + String ads = String.format(ADS, nativeRTB); + System.out.println(ads + "\n"); + return templateResponse(NO_BID_FALSE, NO_AD_URL, ads); + } + + + // templates + + private static String templateBannerRTBAdsResponse(String content, int width, int height, String impressionURL) { + String rtbBanner = singleRTBBanner(content, width, height, impressionURL); + String ads = String.format(ADS, rtbBanner); + return templateResponse(NO_BID_FALSE, NO_AD_URL, ads); + } + + private static String templateBannerRTBAdsResponse_(String content, int width, int height, String impressionURL) { + String rtbBanner = singleRTBBanner_(content, width, height, impressionURL); + String ads = String.format(ADS, rtbBanner); + return templateResponse_(NO_BID_FALSE, NO_AD_URL, ads); + } + + + private static String templateRTBNativeAdResponse(String nativeResponse) { + return String.format(RTB_NATIVE, RTB_NATIVE_VIEWABILITY_CONFIG, nativeResponse); + } + + + private static String singleRTBBanner(String content, int width, int height, String impressionURL) { + return (String.format(RTB_BANNER, content, width, height, impressionURL)); + } + + private static String singleRTBBanner_(String content, int width, int height, String impressionURL) { + return (String.format(RTB_BANNER_, content, width, height, impressionURL)); + } + + private static String singleRTBVideo(String content) { + return (String.format(RTB_VIDEO, NOTIFY_URL, content)); + } + + + private static String templateResponse(String noBid, String noAdURL, String ads) { + System.out.println(String.format(RESPONSE, noBid, noAdURL, ads)); + return String.format(RESPONSE, noBid, noAdURL, ads); + } + + private static String templateResponse_(String noBid, String noAdURL, String ads) { + System.out.println(String.format(RESPONSE_, noBid, noAdURL, ads)); + return String.format(RESPONSE_, noBid, noAdURL, ads); + } + + private static String templateSingleCSMAdResponseBannerInterstitial(String className, String response_url) { + String csmBanner = templateSingleCSMAdResponseBannerInterstitial(className, 320, 50, IMPRESSION_URL, REQUEST_URL, response_url, "", "", "android"); + return templateMediatedAdResponse(csmBanner); + } + + private static String templateSingleCSMAdResponseBannerInterstitialTimeout(String className, String response_url) { + String csmBanner = templateSingleCSMAdResponseBannerInterstitialTimeout(className, 320, 50, IMPRESSION_URL, REQUEST_URL, response_url, "", "", "android"); + return templateMediatedAdResponse(csmBanner); + } + + + private static String templateSingleCSMAdResponseBannerInterstitialTimeoutNonZero(String className, String response_url) { + String csmBanner = templateSingleCSMAdResponseBannerInterstitialTimeoutNonZero(className, 320, 50, IMPRESSION_URL, REQUEST_URL, response_url, "", "", "android"); + return templateMediatedAdResponse(csmBanner); + } + + + public static String templateSingleCSMAdResponseBannerInterstitial(String className, String response_url, String id) { + String csmBanner = templateSingleCSMAdResponseBannerInterstitial(className, 320, 50, IMPRESSION_URL, REQUEST_URL, response_url, "", id, "android"); + return templateMediatedAdResponse(csmBanner); + } + + private static String templateSingleCSMAdResponseNative(String className, String response_url) { + String csmNative = templateSingleCSMAdResponseNative(className, "abc", "1234", IMPRESSION_URL, REQUEST_URL, response_url); + return templateMediatedAdResponse(csmNative); + } + + + private static String templateSingleCSMAdResponseNativeTimeout(String className, String response_url) { + String csmNative = templateSingleCSMAdResponseNativeTimeout(className, "abc", "1234", IMPRESSION_URL, REQUEST_URL, response_url); + return templateMediatedAdResponse(csmNative); + } + + private static String templateSingleCSMAdResponseNativeTimeoutNonZero(String className, String response_url) { + String csmNative = templateSingleCSMAdResponseNativeTimeoutNonZero(className, "abc", "1234", IMPRESSION_URL, REQUEST_URL, response_url); + return templateMediatedAdResponse(csmNative); + } + + + private static String templateSingleSSMAdResponseWithTimeoutZero() { + String ssmAdTag = String.format(SSM_BANNER_TIMEOUT_ZERO, DUMMY_BANNER_CONTENT, SSM_URL, IMPRESSION_URL, REQUEST_URL, RESPONSE_URL); + String ads = String.format(ADS, ssmAdTag); + return String.format(RESPONSE, NO_BID_FALSE, NO_AD_URL, ads); + } + + private static String templateSingleSSMAdResponse() { + String ssmAdTag = String.format(SSM_BANNER, DUMMY_BANNER_CONTENT, SSM_URL, IMPRESSION_URL, REQUEST_URL, RESPONSE_URL); + String ads = String.format(ADS, ssmAdTag); + return String.format(RESPONSE, NO_BID_FALSE, NO_AD_URL, ads); + } + + private static String templateNoURLSSMResponse() { + String ssmAdTag = String.format(SSM_BANNER, DUMMY_BANNER_CONTENT, SSM_NO_URL, IMPRESSION_URL, REQUEST_URL, RESPONSE_URL); + String ads = String.format(ADS, ssmAdTag); + return String.format(RESPONSE, NO_BID_FALSE, NO_AD_URL, ads); + } + + + private static String templateSingleCSMAdResponseBannerInterstitial(String className, int width, int height, String impressionURL, String request_url, String response_url, String params, String id, String type) { + return String.format(CSM_BANNER, DUMMY_BANNER_CONTENT, params, height, width, id, type, className, impressionURL, request_url, response_url); + } + + private static String templateSingleCSMAdResponseBannerInterstitialTimeout(String className, int width, int height, String impressionURL, String request_url, String response_url, String params, String id, String type) { + return String.format(CSM_BANNER_TIMEOUT_ZERO, DUMMY_BANNER_CONTENT, params, height, width, id, type, className, impressionURL, request_url, response_url); + } + + private static String templateSingleCSMAdResponseBannerInterstitialTimeoutNonZero(String className, int width, int height, String impressionURL, String request_url, String response_url, String params, String id, String type) { + return String.format(CSM_BANNER_TIMEOUT_NON_ZERO, DUMMY_BANNER_CONTENT, params, height, width, id, type, className, impressionURL, request_url, response_url); + } + private static String templateSingleCSMAdResponseNative(String className, String params, String id, String impression_url, String request_url, String response_url) { + Clog.d("Native Ad", String.format(CSM_NATIVE, CSM_NATIVE_VIEWABILITY_CONFIG, className, params, id, impression_url, request_url, response_url)); + return String.format(CSM_NATIVE, CSM_NATIVE_VIEWABILITY_CONFIG, className, params, id, impression_url, request_url, response_url); + } + + + private static String templateSingleCSMAdResponseNativeTimeout(String className, String params, String id, String impression_url, String request_url, String response_url) { + return String.format(CSM_NATIVE_TIMEOUT_ZERO, CSM_NATIVE_VIEWABILITY_CONFIG, className, params, id, impression_url, request_url, response_url); + } + + + + private static String templateSingleCSMAdResponseNativeTimeoutNonZero(String className, String params, String id, String impression_url, String request_url, String response_url) { + return String.format(CSM_NATIVE_TIMEOUT_NON_ZERO, CSM_NATIVE_VIEWABILITY_CONFIG, className, params, id, impression_url, request_url, response_url); + } + + + private static String templateMediatedAdResponse(String adsArray) { + String ads = String.format(ADS, adsArray); + return templateResponse(NO_BID_FALSE, NO_AD_URL, ads); + } + + + private static String templateNativeResponse(String type, String title, String description, String additionalDescription, String full_text, String context, + String icon, String main_media, String cta, String click_trackers, + String imp_trackers, String rating, String click_url, + String click_fallback_url, String sponsored_by, String custom) { + return String.format(AN_NATIVE_RESPONSE, type, title, description, additionalDescription, full_text, context, icon, main_media, cta, click_trackers, imp_trackers, rating, click_url, click_fallback_url, sponsored_by, custom); + + + } + + + private static String templateNativeResponse(String title, String description, String additionalDescription, String sponsored, String cta, + String rating, String icon_url, int icon_width, int icon_height, String main_img_url, + int main_img_width, int main_img_height, String link_url, String click_trackers, + String imp_trackers, int id) { + return String.format(AN_NATIVE_RESPONSE, title, description, sponsored, cta, rating, icon_url, icon_width, icon_height, main_img_url, main_img_width, main_img_height, link_url, click_trackers, imp_trackers, id, additionalDescription); + + + } + + private static String templateNativeVideoResponse(String title, String description, String additionalDescription, String sponsored, String cta, + int rating, String icon_url, int icon_width, int icon_height, String main_img_url, + int main_img_width, int main_img_height, String link_url, String fallback_url, String click_trackers1, + String click_trackers2, String click_trackers3, String imp_tracker1, String impression_tracker2, + String impression_tracker3, String impression_tracker4, String javascript_trackers, int id, + String displayurl, int likes, int downloads, int price, int saleprice, int phone, String address, + String video_content, String privacy_link) { + return String.format(AN_NATIVE_VIDEO_RESPONSE, title, description, sponsored, cta, rating, icon_url, icon_width, icon_height, main_img_url, + main_img_width, main_img_height, link_url, fallback_url, click_trackers1, click_trackers2, click_trackers3, imp_tracker1, + impression_tracker2, impression_tracker3, impression_tracker4, javascript_trackers, id, displayurl, likes, downloads, price, + saleprice, phone, address, additionalDescription, video_content, privacy_link); + + + } + + private static String templateNativeMainMedia(String url, int width, int height, String url2, int width2, int height2, String url3, int width3, int height3) { + return String.format(NATIVE_MAIN_MEDIA, url, width, height, url2, width2, height2, url3, width3, height3); + } + + private static String templateNativeRating(float value, float scale) { + return String.format(NATIVE_RATING, value, scale); + } + + + private static final String DUMMY_VIDEO_CONTENT = "\n" + + "\n" + + " \n" + + " \n" + + " adnxs\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " 00:02:25\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + ""; + + + public static String rtbVASTVideo() { + return templateVideoRTBAdsResponse(DUMMY_VIDEO_CONTENT); + } + + + private static String templateVideoRTBAdsResponse(String content) { + String rtbVideo = singleRTBVideo(content); + String ads = String.format(ADS, rtbVideo); + return templateResponse(NO_BID_FALSE, NO_AD_URL, ads); + } + + +} diff --git a/tests/TrackerTestApp/app/src/androidTest/java/appnexus/com/trackertestapp/tracker/banner/BannerAdTrackerTest.kt b/tests/TrackerTestApp/app/src/androidTest/java/appnexus/com/trackertestapp/tracker/banner/BannerAdTrackerTest.kt new file mode 100644 index 000000000..1a539b91d --- /dev/null +++ b/tests/TrackerTestApp/app/src/androidTest/java/appnexus/com/trackertestapp/tracker/banner/BannerAdTrackerTest.kt @@ -0,0 +1,578 @@ +/* + * Copyright 2021 APPNEXUS INC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package appnexus.com.trackertestapp.tracker.banner + +import android.content.Intent +import android.content.res.Resources +import android.net.Uri +import android.net.http.SslError +import android.os.Handler +import android.os.Looper +import android.view.View +import android.webkit.SslErrorHandler +import android.webkit.WebResourceResponse +import android.webkit.WebView +import android.webkit.WebViewClient +import androidx.test.espresso.Espresso +import androidx.test.espresso.IdlingPolicies +import androidx.test.espresso.IdlingRegistry +import androidx.test.espresso.action.ViewActions +import androidx.test.espresso.assertion.ViewAssertions +import androidx.test.espresso.matcher.ViewMatchers +import androidx.test.rule.ActivityTestRule +import androidx.test.runner.AndroidJUnit4 +import appnexus.com.trackertestapp.BannerActivity +import appnexus.com.trackertestapp.MockDispatcher +import appnexus.com.trackertestapp.R +import appnexus.com.trackertestapp.TestResponsesUT +import appnexus.com.trackertestapp.util.Util +import com.appnexus.opensdk.SDKSettings +import com.appnexus.opensdk.ut.UTConstants +import com.microsoft.appcenter.espresso.Factory +import okhttp3.HttpUrl +import okhttp3.mockwebserver.MockWebServer +import org.hamcrest.Matchers.not +import org.junit.* +import org.junit.runner.RunWith +import java.util.concurrent.TimeUnit + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class BannerAdTrackerTest { + val Int.dp: Int + get() = (this / Resources.getSystem().displayMetrics.density).toInt() + val Int.px: Int + get() = (this * Resources.getSystem().displayMetrics.density).toInt() + + @get:Rule + var reportHelper = Factory.getReportHelper() + + @Rule + @JvmField + var mActivityTestRule = ActivityTestRule(BannerActivity::class.java, true, false) + + lateinit var bannerActivity: BannerActivity + + lateinit var mockWebServer: MockWebServer + + lateinit var mockDispatcher: MockDispatcher + + @Before + fun setup() { + IdlingPolicies.setMasterPolicyTimeout(1, TimeUnit.MINUTES) + IdlingPolicies.setIdlingResourceTimeout(1, TimeUnit.MINUTES) + var intent = Intent() + mActivityTestRule.launchActivity(intent) + bannerActivity = mActivityTestRule.activity + Util.getWifiIp(bannerActivity) + Thread.sleep(TestResponsesUT.DELAY_IP) + val ip = Util.getMobileIPAddress() + mockWebServer = MockWebServer() + mockDispatcher = MockDispatcher() + mockDispatcher.adType = "BannerAd" + mockWebServer.start(ip, 8080) + mockWebServer.setDispatcher(mockDispatcher) + + val url: HttpUrl = mockWebServer.url("/") + UTConstants.REQUEST_BASE_URL_UT = url.toString() + println("URL: " + UTConstants.REQUEST_BASE_URL_UT) + TestResponsesUT.setTestURL(url.toString()) + IdlingRegistry.getInstance().register(bannerActivity.idlingResource) + } + + @After + fun destroy() { + SDKSettings.setCountImpressionOn1pxRendering(false) + try { + Handler(Looper.getMainLooper()).post { + bannerActivity.removeBannerAd() + } + Thread.sleep(TestResponsesUT.DELAY_IP) + mockWebServer.shutdown() + } catch (e: Exception) { + e.printStackTrace() + } + IdlingRegistry.getInstance().unregister(bannerActivity.idlingResource) + reportHelper.label("Stopping App") + } + + /* + testBannerImpressionTrackerTestAdDefault: To test the impression tracker is fired by the Banner Ad. + */ + @Test + fun testBannerImpressionTrackerTestAdDefault() { + + bannerActivity.triggerAdLoad("14847003", 300, 250, creativeId = 156075267) + + Espresso.onView(ViewMatchers.withId(R.id.linearLayout)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView(ViewMatchers.withId(R.id.linearLayout)) + .check(ViewAssertions.matches(ViewMatchers.hasChildCount(1))) + Espresso.onView(ViewMatchers.withId(bannerActivity.banner_id)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + + Thread.sleep(TestResponsesUT.DELAY) + println("DISPATCH: LIST: " + mockDispatcher.arrRequests) + + var c = 0 + for (request in mockDispatcher.arrRequests) { + if (request.startsWith("GET //it?")) { + c++ + break + } + } + + Assert.assertTrue(c > 0) + } + + /* + testBannerImpressionTrackerTestAdCountOnAdLoad: To test the impression tracker is fired by the Banner Ad. + countImpressionOnAdLoad = true + */ + @Test + fun testBannerImpressionTrackerTestAdCountOnAdLoad() { + + bannerActivity.shouldDisplay = false + bannerActivity.triggerAdLoad( + "14847003", + 300, + 250, + creativeId = 156075267, + countImpressionOnAdLoad = true + ) + + Espresso.onView(ViewMatchers.withId(R.id.linearLayout)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView(ViewMatchers.withId(R.id.linearLayout)) + .check(ViewAssertions.matches(ViewMatchers.hasChildCount(1))) + Espresso.onView(ViewMatchers.withId(bannerActivity.banner_id)) + .check(ViewAssertions.matches(not(ViewMatchers.isDisplayed()))) + + Thread.sleep(TestResponsesUT.DELAY) + println("DISPATCH: LIST: " + mockDispatcher.arrRequests) + + var c = 0 + for (request in mockDispatcher.arrRequests) { + if (request.startsWith("GET //it?")) { + c++ + break + } + } + + Assert.assertTrue(c > 0) + } + + /* + testBannerImpressionTrackerTestAdLazyLoad: To test the impression tracker is fired by the Banner Ad. + enableLazyLoad = true + */ + @Test + fun testBannerImpressionTrackerTestAdLazyLoad() { + + bannerActivity.shouldDisplay = false + bannerActivity.triggerAdLoad("14847003", 300, 250, creativeId = 156075267, lazyLoad = true) + + Espresso.onView(ViewMatchers.withId(R.id.linearLayout)) + .check(ViewAssertions.matches(ViewMatchers.hasChildCount(0))) + + var c = 0 + for (request in mockDispatcher.arrRequests) { + if (request.startsWith("GET //it?")) { + c++ + break + } + } + + Assert.assertTrue(c == 0) + + triggerLazyLoad() + + Espresso.onView(ViewMatchers.withId(R.id.linearLayout)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView(ViewMatchers.withId(R.id.linearLayout)) + .check(ViewAssertions.matches(ViewMatchers.hasChildCount(1))) + Espresso.onView(ViewMatchers.withId(bannerActivity.banner_id)) + .check(ViewAssertions.matches(not(ViewMatchers.isDisplayed()))) + + Thread.sleep(TestResponsesUT.DELAY) + println("DISPATCH: LIST: " + mockDispatcher.arrRequests) + + for (request in mockDispatcher.arrRequests) { + if (request.startsWith("GET //it?")) { + c++ + break + } + } + + Assert.assertTrue(c > 0) + } + + /* + testBannerImpressionTrackerTestOnePx: To test the impression tracker is fired by the Banner Ad. + SDKSettings.setCountImpressionOn1pxRendering(true) + */ + @Test + fun testBannerImpressionTrackerTestOnePx() { + + bannerActivity.shouldDisplay = false + SDKSettings.setCountImpressionOn1pxRendering(true) + bannerActivity.triggerAdLoad("14847003", 300, 250, creativeId = 156075267) + + Espresso.onView(ViewMatchers.withId(R.id.linearLayout)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView(ViewMatchers.withId(R.id.linearLayout)) + .check(ViewAssertions.matches(ViewMatchers.hasChildCount(1))) + Espresso.onView(ViewMatchers.withId(bannerActivity.banner_id)) + .check(ViewAssertions.matches(not(ViewMatchers.isDisplayed()))) + + Thread.sleep(TestResponsesUT.DELAY) + println("DISPATCH: LIST: " + mockDispatcher.arrRequests) + + var c = 0 + for (request in mockDispatcher.arrRequests) { + if (request.startsWith("GET //it?")) { + c++ + break + } + } + + Assert.assertTrue(c == 0) + + setVisibility(View.VISIBLE) + + Thread.sleep(TestResponsesUT.DELAY) + println("DISPATCH: LIST: " + mockDispatcher.arrRequests) + + for (request in mockDispatcher.arrRequests) { + if (request.startsWith("GET //it?")) { + c++ + break + } + } + + Assert.assertTrue(c > 0) + } + + /* + testBannerImpressionTrackerTestAdCountOnAdLoadPreferredOverOnePx: To test the impression tracker is fired by the Banner Ad. + countImpressionOnAdLoad = true + SDKSettings.setCountImpressionOn1pxRendering(true) + */ + @Test + fun testBannerImpressionTrackerTestAdCountOnAdLoadPreferredOverOnePx() { + + SDKSettings.setCountImpressionOn1pxRendering(true) + bannerActivity.shouldDisplay = false + bannerActivity.triggerAdLoad( + "14847003", + 300, + 250, + creativeId = 156075267, + countImpressionOnAdLoad = true + ) + + Espresso.onView(ViewMatchers.withId(R.id.linearLayout)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView(ViewMatchers.withId(R.id.linearLayout)) + .check(ViewAssertions.matches(ViewMatchers.hasChildCount(1))) + Espresso.onView(ViewMatchers.withId(bannerActivity.banner_id)) + .check(ViewAssertions.matches(not(ViewMatchers.isDisplayed()))) + + Thread.sleep(TestResponsesUT.DELAY) + println("DISPATCH: LIST: " + mockDispatcher.arrRequests) + + var c = 0 + for (request in mockDispatcher.arrRequests) { + if (request.startsWith("GET //it?")) { + c++ + break + } + } + + Assert.assertTrue(c > 0) + } + + /* + testBannerImpressionTrackerTestAdCountOnAdLoadPreferredOverLazyLoad: To test the impression tracker is fired by the Banner Ad. + countImpressionOnAdLoad = true + enableLazyLoad = true + */ + @Test + fun testBannerImpressionTrackerTestAdCountOnAdLoadPreferredOverLazyLoad() { + + bannerActivity.shouldDisplay = false + bannerActivity.triggerAdLoad( + "14847003", + 300, + 250, + creativeId = 156075267, + countImpressionOnAdLoad = true, + lazyLoad = true + ) + + Espresso.onView(ViewMatchers.withId(R.id.linearLayout)) + .check(ViewAssertions.matches(ViewMatchers.hasChildCount(0))) + + var c = 0 + for (request in mockDispatcher.arrRequests) { + if (request.startsWith("GET //it?")) { + c++ + break + } + } + + Assert.assertTrue(c == 0) + + triggerLazyLoad() + + Espresso.onView(ViewMatchers.withId(R.id.linearLayout)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView(ViewMatchers.withId(R.id.linearLayout)) + .check(ViewAssertions.matches(ViewMatchers.hasChildCount(1))) + Espresso.onView(ViewMatchers.withId(bannerActivity.banner_id)) + .check(ViewAssertions.matches(not(ViewMatchers.isDisplayed()))) + + Thread.sleep(TestResponsesUT.DELAY) + println("DISPATCH: LIST: " + mockDispatcher.arrRequests) + + for (request in mockDispatcher.arrRequests) { + if (request.startsWith("GET //it?")) { + c++ + break + } + } + + Assert.assertTrue(c > 0) + } + + /* + testBannerImpressionTrackerTestOnePxPreferredOverLazyLoad: To test the impression tracker is fired by the Banner Ad. + enableLazyLoad = true + SDKSettings.setCountImpressionOn1pxRendering(true) + */ + @Test + fun testBannerImpressionTrackerTestOnePxPreferredOverLazyLoad() { + + bannerActivity.shouldDisplay = false + SDKSettings.setCountImpressionOn1pxRendering(true) + bannerActivity.triggerAdLoad("14847003", 300, 250, creativeId = 156075267, lazyLoad = true) + + Espresso.onView(ViewMatchers.withId(R.id.linearLayout)) + .check(ViewAssertions.matches(ViewMatchers.hasChildCount(0))) + + var c = 0 + for (request in mockDispatcher.arrRequests) { + if (request.startsWith("GET //it?")) { + c++ + break + } + } + + Assert.assertTrue(c == 0) + + triggerLazyLoad() + + Espresso.onView(ViewMatchers.withId(R.id.linearLayout)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView(ViewMatchers.withId(R.id.linearLayout)) + .check(ViewAssertions.matches(ViewMatchers.hasChildCount(1))) + Espresso.onView(ViewMatchers.withId(bannerActivity.banner_id)) + .check(ViewAssertions.matches(not(ViewMatchers.isDisplayed()))) + + Thread.sleep(TestResponsesUT.DELAY) + println("DISPATCH: LIST: " + mockDispatcher.arrRequests) + + for (request in mockDispatcher.arrRequests) { + if (request.startsWith("GET //it?")) { + c++ + break + } + } + + Assert.assertTrue(c == 0) + + setVisibility(View.VISIBLE) + + Thread.sleep(TestResponsesUT.DELAY) + println("DISPATCH: LIST: " + mockDispatcher.arrRequests) + + for (request in mockDispatcher.arrRequests) { + if (request.startsWith("GET //it?")) { + c++ + break + } + } + + Assert.assertTrue(c > 0) + } + + /* + testBannerImpressionTrackerTestAdCountOnAdLoadPreferredOverAllOtherFlags: To test the impression tracker is fired by the Banner Ad. + SDKSettings.setCountImpressionOn1pxRendering(true) + enableLazyLoad = true + countImpressionOnAdLoad = true + */ + @Test + fun testBannerImpressionTrackerTestAdCountOnAdLoadPreferredOverAllOtherFlags() { + + bannerActivity.shouldDisplay = false + SDKSettings.setCountImpressionOn1pxRendering(true) + bannerActivity.triggerAdLoad( + "14847003", + 300, + 250, + creativeId = 156075267, + countImpressionOnAdLoad = true, + lazyLoad = true + ) + + Espresso.onView(ViewMatchers.withId(R.id.linearLayout)) + .check(ViewAssertions.matches(ViewMatchers.hasChildCount(0))) + + var c = 0 + for (request in mockDispatcher.arrRequests) { + if (request.startsWith("GET //it?")) { + c++ + break + } + } + + Assert.assertTrue(c == 0) + + triggerLazyLoad() + + Espresso.onView(ViewMatchers.withId(R.id.linearLayout)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView(ViewMatchers.withId(R.id.linearLayout)) + .check(ViewAssertions.matches(ViewMatchers.hasChildCount(1))) + Espresso.onView(ViewMatchers.withId(bannerActivity.banner_id)) + .check(ViewAssertions.matches(not(ViewMatchers.isDisplayed()))) + + Thread.sleep(TestResponsesUT.DELAY) + println("DISPATCH: LIST: " + mockDispatcher.arrRequests) + + for (request in mockDispatcher.arrRequests) { + if (request.startsWith("GET //it?")) { + c++ + break + } + } + + Assert.assertTrue(c > 0) + } + + /* + testBannerClickTrackerTestAd: To test the click tracker is fired by the Banner Ad. + */ + @Test + fun testBannerClickTrackerTestAd() { + + bannerActivity.triggerAdLoad("14847003", 300, 250, creativeId = 156075267) + + Espresso.onView(ViewMatchers.withId(R.id.linearLayout)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView(ViewMatchers.withId(R.id.linearLayout)) + .check(ViewAssertions.matches(ViewMatchers.hasChildCount(1))) + Espresso.onView(ViewMatchers.withId(bannerActivity.banner_id)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + for (i in 0..bannerActivity.banner.childCount - 1) { + var child = bannerActivity.banner.getChildAt(i) + if (child is WebView) { + Handler(Looper.getMainLooper()).post { + var webviewClient = child.webViewClient + child.webViewClient = object : WebViewClient() { + + override fun shouldOverrideUrlLoading( + view: WebView?, + url: String? + ): Boolean { + mockDispatcher.arrRequests.add(Uri.decode(url).toString()) + println("DISPATCH REQUEST: " + url) + return webviewClient.shouldOverrideUrlLoading(view, url) + } + + override fun shouldInterceptRequest( + view: WebView?, + url: String? + ): WebResourceResponse? { + mockDispatcher.arrRequests.add(Uri.decode(url).toString()) + println("DISPATCH REQUEST: " + url) + return webviewClient.shouldInterceptRequest(view, url) + } + + override fun onPageFinished(view: WebView?, url: String?) { + webviewClient.onPageFinished(view, url) + } + + override fun onReceivedError( + view: WebView?, + errorCode: Int, + description: String?, + failingUrl: String? + ) { + webviewClient.onReceivedError(view, errorCode, description, failingUrl) + } + + override fun onReceivedSslError( + view: WebView?, + handler: SslErrorHandler?, + error: SslError? + ) { + webviewClient.onReceivedSslError(view, handler, error) + } + + override fun onLoadResource(view: WebView?, url: String?) { + webviewClient.onLoadResource(view, url) + } + } + } + } + } + + Thread.sleep(TestResponsesUT.DELAY) + Espresso.onView(ViewMatchers.withId(bannerActivity.banner_id)).perform(ViewActions.click()) + + + println("DISPATCH: LIST: " + mockDispatcher.arrRequests) + + var c = 0 + for (request in mockDispatcher.arrRequests) { + if (request.contains(UTConstants.REQUEST_BASE_URL_UT + "/click")) { + c++ + break + } + } + Assert.assertTrue(c > 0) + } + + private fun setVisibility(visibility: Int) { + Handler(Looper.getMainLooper()).post({ + bannerActivity.banner.visibility = visibility + }) + } + + private fun triggerLazyLoad() { + Handler(Looper.getMainLooper()).post({ + bannerActivity.loadLazyAd() + }) + } + +} diff --git a/tests/TrackerTestApp/app/src/androidTest/java/appnexus/com/trackertestapp/tracker/banner_native/BannerNativeAdTrackerTest.kt b/tests/TrackerTestApp/app/src/androidTest/java/appnexus/com/trackertestapp/tracker/banner_native/BannerNativeAdTrackerTest.kt new file mode 100644 index 000000000..90c46325d --- /dev/null +++ b/tests/TrackerTestApp/app/src/androidTest/java/appnexus/com/trackertestapp/tracker/banner_native/BannerNativeAdTrackerTest.kt @@ -0,0 +1,167 @@ +/* + * Copyright 2021 APPNEXUS INC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package appnexus.com.trackertestapp.tracker.banner_native + +import android.content.Intent +import android.content.res.Resources +import android.os.Handler +import android.os.Looper +import androidx.test.espresso.Espresso +import androidx.test.espresso.IdlingPolicies +import androidx.test.espresso.IdlingRegistry +import androidx.test.espresso.action.ViewActions +import androidx.test.espresso.assertion.ViewAssertions +import androidx.test.espresso.matcher.ViewMatchers +import androidx.test.internal.runner.junit4.statement.UiThreadStatement +import androidx.test.rule.ActivityTestRule +import androidx.test.runner.AndroidJUnit4 +import appnexus.com.trackertestapp.BannerActivity +import appnexus.com.trackertestapp.MockDispatcher +import appnexus.com.trackertestapp.R +import appnexus.com.trackertestapp.TestResponsesUT +import appnexus.com.trackertestapp.util.Util +import com.appnexus.opensdk.ut.UTConstants +import com.microsoft.appcenter.espresso.Factory +import okhttp3.HttpUrl +import okhttp3.mockwebserver.MockWebServer +import org.junit.* +import org.junit.runner.RunWith +import java.util.concurrent.TimeUnit + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class BannerNativeAdTrackerTest { + val Int.dp: Int + get() = (this / Resources.getSystem().displayMetrics.density).toInt() + val Int.px: Int + get() = (this * Resources.getSystem().displayMetrics.density).toInt() + + @get:Rule + var reportHelper = Factory.getReportHelper() + + @Rule + @JvmField + var mActivityTestRule = ActivityTestRule(BannerActivity::class.java, true, false) + + lateinit var bannerActivity: BannerActivity + + lateinit var mockWebServer: MockWebServer + + lateinit var mockDispatcher: MockDispatcher + + @Before + fun setup() { + IdlingPolicies.setMasterPolicyTimeout(1, TimeUnit.MINUTES) + IdlingPolicies.setIdlingResourceTimeout(1, TimeUnit.MINUTES) + var intent = Intent() + mActivityTestRule.launchActivity(intent) + bannerActivity = mActivityTestRule.activity + Util.getWifiIp(bannerActivity) + Thread.sleep(TestResponsesUT.DELAY_IP) + val ip = Util.getMobileIPAddress() + mockWebServer = MockWebServer() + mockDispatcher = MockDispatcher() + mockDispatcher.adType = "NativeAd" + mockWebServer.start(ip, 8080) + mockWebServer.setDispatcher(mockDispatcher) + + val url: HttpUrl = mockWebServer.url("/") + UTConstants.REQUEST_BASE_URL_UT = url.toString() + println("URL: " + UTConstants.REQUEST_BASE_URL_UT) + TestResponsesUT.setTestURL(url.toString()) + IdlingRegistry.getInstance().register(bannerActivity.idlingResource) + + } + + @After + fun destroy() { + try { + Handler(Looper.getMainLooper()).post { + bannerActivity.removeBannerAd() + } + Thread.sleep(TestResponsesUT.DELAY_IP) + mockWebServer.shutdown() + } catch (e: Exception) { + e.printStackTrace() + } + IdlingRegistry.getInstance().unregister(bannerActivity.idlingResource) + reportHelper.label("Stopping App") + } + + /* + testBannerNativeImpressionTrackerTestAd: To test the impression tracker is fired by the Banner Native Ad. + */ + @Test + fun testBannerNativeImpressionTrackerTestAd() { + bannerActivity.triggerAdLoad("20331545", allowNativeDemand = true) + Espresso.onView(ViewMatchers.withId(R.id.title)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView(ViewMatchers.withId(R.id.description)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView(ViewMatchers.withId(R.id.icon)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView(ViewMatchers.withId(R.id.image)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + + Thread.sleep(TestResponsesUT.DELAY) + println("DISPATCH: LIST: " + mockDispatcher.arrRequests) + + var c = 0 + for (request in mockDispatcher.arrRequests) { + if (request.startsWith("GET //it?")) { + c++ + break + } + } + + Assert.assertTrue(c > 0) + } + + /* + testBannerNativeClickTrackerTestAd: To test the click tracker is fired by the Banner Native Ad. + */ + @Test + fun testBannerNativeClickTrackerTestAd() { + bannerActivity.triggerAdLoad("20331545", allowNativeDemand = true) + Espresso.onView(ViewMatchers.withId(R.id.title)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView(ViewMatchers.withId(R.id.description)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView(ViewMatchers.withId(R.id.icon)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView(ViewMatchers.withId(R.id.image)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + + Espresso.onView(ViewMatchers.withId(R.id.main_native)).perform(ViewActions.click()) + Thread.sleep(TestResponsesUT.DELAY) + println("DISPATCH: LIST: " + mockDispatcher.arrRequests) + + var c = 0 + for (request in mockDispatcher.arrRequests) { + if (request.startsWith("GET //click?")) { + c++ + break + } + } + Assert.assertTrue(c > 0) + } + +} diff --git a/tests/TrackerTestApp/app/src/androidTest/java/appnexus/com/trackertestapp/tracker/banner_native_render/BannerNativeRendererAdTrackerTest.kt b/tests/TrackerTestApp/app/src/androidTest/java/appnexus/com/trackertestapp/tracker/banner_native_render/BannerNativeRendererAdTrackerTest.kt new file mode 100644 index 000000000..95cc71b16 --- /dev/null +++ b/tests/TrackerTestApp/app/src/androidTest/java/appnexus/com/trackertestapp/tracker/banner_native_render/BannerNativeRendererAdTrackerTest.kt @@ -0,0 +1,225 @@ +/* + * Copyright 2021 APPNEXUS INC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package appnexus.com.trackertestapp.tracker.banner_native_render + +import android.content.Intent +import android.content.res.Resources +import android.net.Uri +import android.net.http.SslError +import android.os.Handler +import android.os.Looper +import android.webkit.SslErrorHandler +import android.webkit.WebResourceResponse +import android.webkit.WebView +import android.webkit.WebViewClient +import androidx.test.espresso.Espresso +import androidx.test.espresso.IdlingPolicies +import androidx.test.espresso.IdlingRegistry +import androidx.test.espresso.action.ViewActions +import androidx.test.espresso.assertion.ViewAssertions +import androidx.test.espresso.matcher.ViewMatchers +import androidx.test.internal.runner.junit4.statement.UiThreadStatement +import androidx.test.rule.ActivityTestRule +import androidx.test.runner.AndroidJUnit4 +import appnexus.com.trackertestapp.BannerActivity +import appnexus.com.trackertestapp.MockDispatcher +import appnexus.com.trackertestapp.R +import appnexus.com.trackertestapp.TestResponsesUT +import appnexus.com.trackertestapp.util.Util +import com.appnexus.opensdk.ut.UTConstants +import com.microsoft.appcenter.espresso.Factory +import okhttp3.HttpUrl +import okhttp3.mockwebserver.MockWebServer +import org.junit.* +import org.junit.runner.RunWith +import java.util.concurrent.TimeUnit + + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class BannerNativeRendererAdTrackerTest { + val Int.dp: Int + get() = (this / Resources.getSystem().displayMetrics.density).toInt() + val Int.px: Int + get() = (this * Resources.getSystem().displayMetrics.density).toInt() + + @get:Rule + var reportHelper = Factory.getReportHelper() + + @Rule + @JvmField + var mActivityTestRule = ActivityTestRule(BannerActivity::class.java, true, false) + + lateinit var bannerActivity: BannerActivity + + lateinit var mockWebServer: MockWebServer + + lateinit var mockDispatcher: MockDispatcher + + @Before + fun setup() { + IdlingPolicies.setMasterPolicyTimeout(1, TimeUnit.MINUTES) + IdlingPolicies.setIdlingResourceTimeout(1, TimeUnit.MINUTES) + var intent = Intent() + mActivityTestRule.launchActivity(intent) + bannerActivity = mActivityTestRule.activity + Util.getWifiIp(bannerActivity) + Thread.sleep(TestResponsesUT.DELAY_IP) + val ip = Util.getMobileIPAddress() + mockWebServer = MockWebServer() + mockDispatcher = MockDispatcher() + mockDispatcher.adType = "BannerNativeRenderAd" + mockWebServer.start(ip, 8080) + mockWebServer.setDispatcher(mockDispatcher) + + val url: HttpUrl = mockWebServer.url("/") + UTConstants.REQUEST_BASE_URL_UT = url.toString() + println("URL: " + UTConstants.REQUEST_BASE_URL_UT) + TestResponsesUT.setTestURL(url.toString()) + IdlingRegistry.getInstance().register(bannerActivity.idlingResource) + + } + + @After + fun destroy() { + try { + Handler(Looper.getMainLooper()).post { + bannerActivity.removeBannerAd() + } + mockWebServer.shutdown() + } catch (e: Exception) { + e.printStackTrace() + } + IdlingRegistry.getInstance().unregister(bannerActivity.idlingResource) + reportHelper.label("Stopping App") + } + + /* + testBannerNativeRendererImpressionTrackerTestAd: To test the impression tracker is fired by the Banner Native Renderer Ad. + */ + @Test + fun testBannerNativeRendererImpressionTrackerTestAd() { + bannerActivity.triggerAdLoad("20331545", allowNativeDemand = true, useNativeRenderer = true, rendererId = 0) + Espresso.onView(ViewMatchers.withId(R.id.linearLayout)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView(ViewMatchers.withId(R.id.linearLayout)) + .check(ViewAssertions.matches(ViewMatchers.hasChildCount(1))) + Espresso.onView(ViewMatchers.withId(bannerActivity.banner_id)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + + Thread.sleep(TestResponsesUT.DELAY) + Espresso.onView(ViewMatchers.withId(R.id.linearLayout)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + println("DISPATCH: LIST: " + mockDispatcher.arrRequests) + + var c = 0 + for (request in mockDispatcher.arrRequests) { + if (request.startsWith("GET //it?")) { + c++ + break + } + } + + Assert.assertTrue(c > 0) + } + + /* + testBannerNativeRendererClickTrackerTestAd: To test the click tracker is fired by the Banner Native Renderer Ad. + */ + @Test + fun testBannerNativeRendererClickTrackerTestAd() { + bannerActivity.triggerAdLoad("20331545", allowNativeDemand = true, useNativeRenderer = true, rendererId = 0) + Espresso.onView(ViewMatchers.withId(R.id.linearLayout)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView(ViewMatchers.withId(R.id.linearLayout)) + .check(ViewAssertions.matches(ViewMatchers.hasChildCount(1))) + Espresso.onView(ViewMatchers.withId(bannerActivity.banner_id)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + + for (i in 0..bannerActivity.banner.childCount - 1) { + var child = bannerActivity.banner.getChildAt(i) + if (child is WebView) { + Handler(Looper.getMainLooper()).post { + var webviewClient = child.webViewClient + child.webViewClient = object : WebViewClient() { + + override fun shouldOverrideUrlLoading( + view: WebView?, + url: String? + ): Boolean { + mockDispatcher.arrRequests.add(Uri.decode(url).toString()) + println("DISPATCH REQUEST: " + url) + return webviewClient.shouldOverrideUrlLoading(view, url) + } + + override fun shouldInterceptRequest( + view: WebView?, + url: String? + ): WebResourceResponse? { + mockDispatcher.arrRequests.add(Uri.decode(url).toString()) + println("DISPATCH REQUEST: " + url) + return webviewClient.shouldInterceptRequest(view, url) + } + + override fun onPageFinished(view: WebView?, url: String?) { + webviewClient.onPageFinished(view, url) + } + + override fun onReceivedError( + view: WebView?, + errorCode: Int, + description: String?, + failingUrl: String? + ) { + webviewClient.onReceivedError(view, errorCode, description, failingUrl) + } + + override fun onReceivedSslError( + view: WebView?, + handler: SslErrorHandler?, + error: SslError? + ) { + webviewClient.onReceivedSslError(view, handler, error) + } + + override fun onLoadResource(view: WebView?, url: String?) { + webviewClient.onLoadResource(view, url) + } + } + } + } + } + + Espresso.onView(ViewMatchers.withId(bannerActivity.banner_id)).perform(ViewActions.click()) + Thread.sleep(TestResponsesUT.DELAY) + println("DISPATCH: LIST: " + mockDispatcher.arrRequests) + + var c = 0 + for (request in mockDispatcher.arrRequests) { + if (request.contains(UTConstants.REQUEST_BASE_URL_UT + "/click")) { + c++ + break + } + } + Assert.assertTrue(c > 0) + } + +} diff --git a/tests/TrackerTestApp/app/src/androidTest/java/appnexus/com/trackertestapp/tracker/banner_video/BannerVideoAdTrackerTest.kt b/tests/TrackerTestApp/app/src/androidTest/java/appnexus/com/trackertestapp/tracker/banner_video/BannerVideoAdTrackerTest.kt new file mode 100644 index 000000000..6f6ef2d0b --- /dev/null +++ b/tests/TrackerTestApp/app/src/androidTest/java/appnexus/com/trackertestapp/tracker/banner_video/BannerVideoAdTrackerTest.kt @@ -0,0 +1,229 @@ +/* + * Copyright 2021 APPNEXUS INC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package appnexus.com.trackertestapp.tracker.banner_video + +import android.content.Intent +import android.content.res.Resources +import android.net.Uri +import android.net.http.SslError +import android.os.Handler +import android.os.Looper +import android.webkit.SslErrorHandler +import android.webkit.WebResourceResponse +import android.webkit.WebView +import android.webkit.WebViewClient +import androidx.test.espresso.Espresso +import androidx.test.espresso.IdlingPolicies +import androidx.test.espresso.IdlingRegistry +import androidx.test.espresso.assertion.ViewAssertions +import androidx.test.espresso.matcher.ViewMatchers +import androidx.test.espresso.web.sugar.Web.onWebView +import androidx.test.espresso.web.webdriver.DriverAtoms.* +import androidx.test.espresso.web.webdriver.Locator +import androidx.test.internal.runner.junit4.statement.UiThreadStatement +import androidx.test.rule.ActivityTestRule +import androidx.test.runner.AndroidJUnit4 +import appnexus.com.trackertestapp.BannerActivity +import appnexus.com.trackertestapp.MockDispatcher +import appnexus.com.trackertestapp.R +import appnexus.com.trackertestapp.TestResponsesUT +import appnexus.com.trackertestapp.util.Util +import com.appnexus.opensdk.ut.UTConstants +import com.microsoft.appcenter.espresso.Factory +import okhttp3.HttpUrl +import okhttp3.mockwebserver.MockWebServer +import org.junit.* +import org.junit.runner.RunWith +import java.util.concurrent.TimeUnit + + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class BannerVideoAdTrackerTest { + val Int.dp: Int + get() = (this / Resources.getSystem().displayMetrics.density).toInt() + val Int.px: Int + get() = (this * Resources.getSystem().displayMetrics.density).toInt() + + @get:Rule + var reportHelper = Factory.getReportHelper() + + @Rule + @JvmField + var mActivityTestRule = ActivityTestRule(BannerActivity::class.java, true, false) + + lateinit var bannerActivity: BannerActivity + + lateinit var mockWebServer: MockWebServer + + lateinit var mockDispatcher: MockDispatcher + + @Before + fun setup() { + IdlingPolicies.setMasterPolicyTimeout(1, TimeUnit.MINUTES) + IdlingPolicies.setIdlingResourceTimeout(1, TimeUnit.MINUTES) + var intent = Intent() + mActivityTestRule.launchActivity(intent) + bannerActivity = mActivityTestRule.activity + Util.getWifiIp(bannerActivity) + Thread.sleep(TestResponsesUT.DELAY_IP) + val ip = Util.getMobileIPAddress() + mockWebServer = MockWebServer() + mockDispatcher = MockDispatcher() + mockDispatcher.adType = "BannerVideoAd" + mockWebServer.start(ip,8080) + mockWebServer.setDispatcher(mockDispatcher) + + val url: HttpUrl = mockWebServer.url("/") + UTConstants.REQUEST_BASE_URL_UT = url.toString() + println("URL: " + UTConstants.REQUEST_BASE_URL_UT) + TestResponsesUT.setTestURL(url.toString()) + IdlingRegistry.getInstance().register(bannerActivity.idlingResource) + } + + @After + fun destroy() { + try { + Handler(Looper.getMainLooper()).post { + bannerActivity.removeBannerAd() + } + mockWebServer.shutdown() + } catch (e: Exception) { + e.printStackTrace() + } + IdlingRegistry.getInstance().unregister(bannerActivity.idlingResource) + reportHelper.label("Stopping App") + } + + /* + testBannerVideoImpressionTrackerTestAd: To test the impression tracker is fired by the Banner Ad. + */ + @Test + fun testBannerVideoImpressionTrackerTestAd() { + + bannerActivity.triggerAdLoad("14757590", allowVideoDemand = true, creativeId = 162035356) + + Espresso.onView(ViewMatchers.withId(R.id.linearLayout)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView(ViewMatchers.withId(R.id.linearLayout)) + .check(ViewAssertions.matches(ViewMatchers.hasChildCount(1))) + Espresso.onView(ViewMatchers.withId(bannerActivity.banner_id)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + + Thread.sleep(TestResponsesUT.DELAY) + println("DISPATCH: LIST: " + mockDispatcher.arrRequests) + + var c = 0 + for (request in mockDispatcher.arrRequests) { + if (request.startsWith("GET //it?")) { + c++ + break + } + } + + Assert.assertTrue(c > 0) + } + + /* + testBannerVideoClickTrackerTestAd: To test the click tracker is fired by the Banner Ad. + */ + @Test + fun testBannerVideoClickTrackerTestAd() { + + bannerActivity.triggerAdLoad("14757590", allowVideoDemand = true, creativeId = 162035356) + + Espresso.onView(ViewMatchers.withId(R.id.linearLayout)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView(ViewMatchers.withId(R.id.linearLayout)) + .check(ViewAssertions.matches(ViewMatchers.hasChildCount(1))) + Espresso.onView(ViewMatchers.withId(bannerActivity.banner_id)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + for (i in 0..bannerActivity.banner.childCount - 1) { + var child = bannerActivity.banner.getChildAt(i) + if (child is WebView) { + Handler(Looper.getMainLooper()).post { + var webviewClient = child.webViewClient + child.webViewClient = object : WebViewClient() { + + override fun shouldOverrideUrlLoading( + view: WebView?, + url: String? + ): Boolean { + mockDispatcher.arrRequests.add(Uri.decode(url).toString()) + println("DISPATCH REQUEST: " + url) + return webviewClient.shouldOverrideUrlLoading(view, url) + } + + override fun shouldInterceptRequest( + view: WebView?, + url: String? + ): WebResourceResponse? { + mockDispatcher.arrRequests.add(Uri.decode(url).toString()) + println("DISPATCH REQUEST: " + url) + return webviewClient.shouldInterceptRequest(view, url) + } + + override fun onPageFinished(view: WebView?, url: String?) { + webviewClient.onPageFinished(view, url) + } + + override fun onReceivedError( + view: WebView?, + errorCode: Int, + description: String?, + failingUrl: String? + ) { + webviewClient.onReceivedError(view, errorCode, description, failingUrl) + } + + override fun onReceivedSslError( + view: WebView?, + handler: SslErrorHandler?, + error: SslError? + ) { + webviewClient.onReceivedSslError(view, handler, error) + } + + override fun onLoadResource(view: WebView?, url: String?) { + webviewClient.onLoadResource(view, url) + } + } + } + } + } + onWebView().forceJavascriptEnabled() + onWebView().inWindow(selectFrameByIndex(2)).withElement(findElement(Locator.ID, "ad_indicator_text")) + .perform(webClick()) + Thread.sleep(TestResponsesUT.DELAY) + println("DISPATCH: LIST: " + mockDispatcher.arrRequests) + + var c = 0 + for (request in mockDispatcher.arrRequests) { + if (request.contains(UTConstants.REQUEST_BASE_URL_UT + "/click")) { + c++ + break + } + } + Assert.assertTrue(c > 0) + } + + +} diff --git a/tests/TrackerTestApp/app/src/androidTest/java/appnexus/com/trackertestapp/tracker/interstitial/InterstitialAdTrackerTest.kt b/tests/TrackerTestApp/app/src/androidTest/java/appnexus/com/trackertestapp/tracker/interstitial/InterstitialAdTrackerTest.kt new file mode 100644 index 000000000..b9ee14513 --- /dev/null +++ b/tests/TrackerTestApp/app/src/androidTest/java/appnexus/com/trackertestapp/tracker/interstitial/InterstitialAdTrackerTest.kt @@ -0,0 +1,207 @@ +/* + * Copyright 2021 APPNEXUS INC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package appnexus.com.trackertestapp.tracker.interstitial + +import android.content.Intent +import android.os.Handler +import android.os.Looper +import androidx.test.espresso.Espresso +import androidx.test.espresso.IdlingPolicies +import androidx.test.espresso.IdlingRegistry +import androidx.test.espresso.action.ViewActions +import androidx.test.espresso.assertion.ViewAssertions +import androidx.test.espresso.intent.Intents.intended +import androidx.test.espresso.intent.matcher.IntentMatchers +import androidx.test.espresso.intent.rule.IntentsTestRule +import androidx.test.espresso.matcher.ViewMatchers +import androidx.test.filters.LargeTest +import androidx.test.internal.runner.junit4.statement.UiThreadStatement +import androidx.test.runner.AndroidJUnit4 +import appnexus.com.trackertestapp.InterstitialActivity +import appnexus.com.trackertestapp.MockDispatcher +import appnexus.com.trackertestapp.TestResponsesUT +import appnexus.com.trackertestapp.util.Util +import com.appnexus.opensdk.AdActivity +import com.appnexus.opensdk.ut.UTConstants +import com.microsoft.appcenter.espresso.Factory +import okhttp3.HttpUrl +import okhttp3.mockwebserver.MockWebServer +import org.junit.* +import org.junit.Assert.assertTrue +import org.junit.runner.RunWith +import java.util.concurrent.TimeUnit + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@LargeTest +@RunWith(AndroidJUnit4::class) +class InterstitialAdTrackerTest { + + @get:Rule + var reportHelper = Factory.getReportHelper() + + @Rule + @JvmField + var mActivityTestRule = IntentsTestRule(InterstitialActivity::class.java, true, false) + + lateinit var interstitialActivity: InterstitialActivity + + lateinit var mockWebServer: MockWebServer + + lateinit var mockDispatcher: MockDispatcher + + @Before + fun setup() { + IdlingPolicies.setMasterPolicyTimeout(1, TimeUnit.MINUTES) + IdlingPolicies.setIdlingResourceTimeout(1, TimeUnit.MINUTES) + var intent = Intent() + mActivityTestRule.launchActivity(intent) + interstitialActivity = mActivityTestRule.activity + Util.getWifiIp(interstitialActivity) + Thread.sleep(TestResponsesUT.DELAY_IP) + val ip = Util.getMobileIPAddress() + mockWebServer = MockWebServer() + mockDispatcher = MockDispatcher() + mockDispatcher.adType = "BannerAd" + mockWebServer.start(ip,8080) + mockWebServer.setDispatcher(mockDispatcher) + + val url: HttpUrl = mockWebServer.url("/") + UTConstants.REQUEST_BASE_URL_UT = url.toString() + println("URL: " + UTConstants.REQUEST_BASE_URL_UT) + TestResponsesUT.setTestURL(url.toString()) + IdlingRegistry.getInstance().register(interstitialActivity.idlingResource) + } + + @After + fun destroy() { + try { + mockWebServer.shutdown() + Handler(Looper.getMainLooper()).post { + interstitialActivity.removeInterstitialAd() + } + } catch (e: Exception) { + e.printStackTrace() + } + IdlingRegistry.getInstance().unregister(interstitialActivity.idlingResource) + reportHelper.label("Stopping App") + } + + /* + testInterstitialImpressionTrackerTestAd: To test the impression tracker is fired by the Interstitial Ad. + */ + @Test + fun testInterstitialImpressionTrackerTestAd() { + interstitialActivity.triggerAdLoad("17058950", creativeId = 166843001) + intended(IntentMatchers.hasComponent(AdActivity::class.java.name)) + val closeButtonId = android.R.id.closeButton + Espresso.onView(ViewMatchers.withId(closeButtonId)).check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Thread.sleep(TestResponsesUT.DELAY) + println("DISPATCH: LIST: " + mockDispatcher.arrRequests) + + var c = 0 + for (request in mockDispatcher.arrRequests) { + if (request.startsWith("GET //it?")) { + c++ + break + } + } + assertTrue(c > 0) + } + +// /* +// testInterstitialClickTrackerTestAd: To test the click tracker is fired by the Interstitial Ad. +// */ +// @Test +// fun testInterstitialClickTrackerTestAd() { +// +// interstitialActivity.triggerAdLoad("17058950", creativeId = 166843001) +// intended(IntentMatchers.hasComponent(AdActivity::class.java.name)) +// val closeButtonId = android.R.id.closeButton +// Espresso.onView(ViewMatchers.withId(closeButtonId)).check(ViewAssertions.matches(ViewMatchers.isDisplayed())) +// +// for (i in 0..interstitialActivity.interstitial.childCount - 1) { +// var child = interstitialActivity.interstitial.getChildAt(i) +// if (child is WebView) { +// Handler(Looper.getMainLooper()).post { +// var webviewClient = child.webViewClient +// child.webViewClient = object : WebViewClient() { +// +// override fun shouldOverrideUrlLoading( +// view: WebView?, +// url: String? +// ): Boolean { +// mockDispatcher.arrRequests.add(Uri.decode(url).toString()) +// println("DISPATCH REQUEST: " + url) +// return webviewClient.shouldOverrideUrlLoading(view, url) +// } +// +// override fun shouldInterceptRequest( +// view: WebView?, +// url: String? +// ): WebResourceResponse? { +// mockDispatcher.arrRequests.add(Uri.decode(url).toString()) +// println("DISPATCH REQUEST: " + url) +// return webviewClient.shouldInterceptRequest(view, url) +// } +// +// override fun onPageFinished(view: WebView?, url: String?) { +// webviewClient.onPageFinished(view, url) +// } +// +// override fun onReceivedError( +// view: WebView?, +// errorCode: Int, +// description: String?, +// failingUrl: String? +// ) { +// webviewClient.onReceivedError(view, errorCode, description, failingUrl) +// } +// +// override fun onReceivedSslError( +// view: WebView?, +// handler: SslErrorHandler?, +// error: SslError? +// ) { +// webviewClient.onReceivedSslError(view, handler, error) +// } +// +// override fun onLoadResource(view: WebView?, url: String?) { +// webviewClient.onLoadResource(view, url) +// } +// } +// } +// } +// } +// +// Espresso.onView(ViewMatchers.withId(interstitialActivity.interstitial_id)).perform(ViewActions.click()) +// Thread.sleep(TestResponsesUT.DELAY) +// println("DISPATCH: LIST: " + mockDispatcher.arrRequests) +// +// var c = 0 +// for (request in mockDispatcher.arrRequests) { +// if (request.contains(UTConstants.REQUEST_BASE_URL_UT + "/click")) { +// c++ +// break +// } +// } +// assertTrue(c > 0) +// } +} diff --git a/tests/TrackerTestApp/app/src/androidTest/java/appnexus/com/trackertestapp/tracker/native/NativeAdTrackerTest.kt b/tests/TrackerTestApp/app/src/androidTest/java/appnexus/com/trackertestapp/tracker/native/NativeAdTrackerTest.kt new file mode 100644 index 000000000..139b5967d --- /dev/null +++ b/tests/TrackerTestApp/app/src/androidTest/java/appnexus/com/trackertestapp/tracker/native/NativeAdTrackerTest.kt @@ -0,0 +1,163 @@ +/* + * Copyright 2021 APPNEXUS INC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package appnexus.com.trackertestapp.tracker + +import android.content.Intent +import android.content.res.Resources +import android.net.Uri +import android.net.http.SslError +import android.os.Handler +import android.os.Looper +import android.webkit.SslErrorHandler +import android.webkit.WebResourceResponse +import android.webkit.WebView +import android.webkit.WebViewClient +import androidx.test.espresso.Espresso +import androidx.test.espresso.IdlingPolicies +import androidx.test.espresso.IdlingRegistry +import androidx.test.espresso.action.ViewActions +import androidx.test.espresso.assertion.ViewAssertions +import androidx.test.espresso.matcher.ViewMatchers +import androidx.test.internal.runner.junit4.statement.UiThreadStatement +import androidx.test.rule.ActivityTestRule +import androidx.test.runner.AndroidJUnit4 +import appnexus.com.trackertestapp.* +import appnexus.com.trackertestapp.util.Util +import com.appnexus.opensdk.ut.UTConstants +import com.microsoft.appcenter.espresso.Factory +import okhttp3.HttpUrl +import okhttp3.mockwebserver.MockWebServer +import org.junit.* +import org.junit.runner.RunWith +import java.util.concurrent.TimeUnit + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class NativeAdTrackerTest { + + @get:Rule + var reportHelper = Factory.getReportHelper() + + @Rule + @JvmField + var mActivityTestRule = ActivityTestRule(NativeActivity::class.java, true, false) + + lateinit var nativeActivity: NativeActivity + + lateinit var mockWebServer: MockWebServer + + lateinit var mockDispatcher: MockDispatcher + + @Before + fun setup() { + IdlingPolicies.setMasterPolicyTimeout(1, TimeUnit.MINUTES) + IdlingPolicies.setIdlingResourceTimeout(1, TimeUnit.MINUTES) + var intent = Intent() + mActivityTestRule.launchActivity(intent) + nativeActivity = mActivityTestRule.activity + Util.getWifiIp(nativeActivity) + Thread.sleep(TestResponsesUT.DELAY_IP) + val ip = Util.getMobileIPAddress() + mockWebServer = MockWebServer() + mockDispatcher = MockDispatcher() + mockDispatcher.adType = "NativeAd" + mockWebServer.start(ip, 8080) + mockWebServer.setDispatcher(mockDispatcher) + + val url: HttpUrl = mockWebServer.url("/") + UTConstants.REQUEST_BASE_URL_UT = url.toString() + println("URL: " + UTConstants.REQUEST_BASE_URL_UT) + TestResponsesUT.setTestURL(url.toString()) + IdlingRegistry.getInstance().register(nativeActivity.idlingResource) + } + + @After + fun destroy() { + try { + Handler(Looper.getMainLooper()).post { + nativeActivity.removeNativeAd() + } + mockWebServer.shutdown() + } catch (e: Exception) { + e.printStackTrace() + } + IdlingRegistry.getInstance().unregister(nativeActivity.idlingResource) + reportHelper.label("Stopping App") + } + + /* + testNativeImpressionTrackerTestAd: To test the impression tracker is fired by the Native Ad. + */ + @Test + fun testNativeImpressionTrackerTestAd() { + nativeActivity.triggerAdLoad("19213468", creativeId = 154679174) + Espresso.onView(ViewMatchers.withId(R.id.title)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView(ViewMatchers.withId(R.id.description)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView(ViewMatchers.withId(R.id.icon)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView(ViewMatchers.withId(R.id.image)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + + Thread.sleep(TestResponsesUT.DELAY) + println("DISPATCH: LIST: " + mockDispatcher.arrRequests) + + var c = 0 + for (request in mockDispatcher.arrRequests) { + if (request.startsWith("GET //it?")) { + c++ + break + } + } + + Assert.assertTrue(c > 0) + } + + /* + testNativeClickTrackerTestAd: To test the click tracker is fired by the Native Ad. + */ + @Test + fun testNativeClickTrackerTestAd() { + nativeActivity.triggerAdLoad("19213468", creativeId = 154679174) + Espresso.onView(ViewMatchers.withId(R.id.title)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView(ViewMatchers.withId(R.id.description)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView(ViewMatchers.withId(R.id.icon)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView(ViewMatchers.withId(R.id.image)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + + Espresso.onView(ViewMatchers.withId(R.id.main_native)).perform(ViewActions.click()) + Thread.sleep(TestResponsesUT.DELAY) + println("DISPATCH: LIST: " + mockDispatcher.arrRequests) + + var c = 0 + for (request in mockDispatcher.arrRequests) { + if (request.startsWith("GET //click?")) { + c++ + break + } + } + Assert.assertTrue(c > 0) + } +} diff --git a/tests/TrackerTestApp/app/src/androidTest/java/appnexus/com/trackertestapp/tracker/video/VideoAdTrackerTest.kt b/tests/TrackerTestApp/app/src/androidTest/java/appnexus/com/trackertestapp/tracker/video/VideoAdTrackerTest.kt new file mode 100644 index 000000000..6deec6eb1 --- /dev/null +++ b/tests/TrackerTestApp/app/src/androidTest/java/appnexus/com/trackertestapp/tracker/video/VideoAdTrackerTest.kt @@ -0,0 +1,148 @@ +/* + * Copyright 2021 APPNEXUS INC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package appnexus.com.trackertestapp.tracker.video + + +import android.content.Intent +import android.os.Handler +import android.os.Looper +import androidx.test.espresso.Espresso +import androidx.test.espresso.IdlingPolicies +import androidx.test.espresso.IdlingRegistry +import androidx.test.espresso.action.ViewActions +import androidx.test.espresso.assertion.ViewAssertions +import androidx.test.espresso.matcher.ViewMatchers +import androidx.test.espresso.web.sugar.Web.onWebView +import androidx.test.espresso.web.webdriver.DriverAtoms.* +import androidx.test.espresso.web.webdriver.Locator +import androidx.test.filters.LargeTest +import androidx.test.internal.runner.junit4.statement.UiThreadStatement +import androidx.test.rule.ActivityTestRule +import androidx.test.runner.AndroidJUnit4 +import appnexus.com.trackertestapp.* +import appnexus.com.trackertestapp.util.Util +import com.appnexus.opensdk.ut.UTConstants +import com.microsoft.appcenter.espresso.Factory +import okhttp3.HttpUrl +import okhttp3.mockwebserver.MockWebServer +import org.junit.* +import org.junit.runner.RunWith +import java.util.concurrent.TimeUnit + +@LargeTest +@RunWith(AndroidJUnit4::class) +class VideoAdTrackerTest { + + @get:Rule + var reportHelper = Factory.getReportHelper() + + @Rule + @JvmField + var mActivityTestRule = ActivityTestRule(VideoActivity::class.java, true, false) + + lateinit var videoActivity: VideoActivity + + lateinit var mockWebServer: MockWebServer + + lateinit var mockDispatcher: MockDispatcher + + @Before + fun setup() { + IdlingPolicies.setMasterPolicyTimeout(1, TimeUnit.MINUTES) + IdlingPolicies.setIdlingResourceTimeout(1, TimeUnit.MINUTES) + var intent = Intent() + mActivityTestRule.launchActivity(intent) + videoActivity = mActivityTestRule.activity + Util.getWifiIp(videoActivity) + Thread.sleep(TestResponsesUT.DELAY_IP) + val ip = Util.getMobileIPAddress() + mockWebServer = MockWebServer() + mockDispatcher = MockDispatcher() + mockDispatcher.adType = "BannerVideoAd" + mockWebServer.start(ip,8080) + mockWebServer.setDispatcher(mockDispatcher) + + val url: HttpUrl = mockWebServer.url("/") + UTConstants.REQUEST_BASE_URL_UT = url.toString() + println("URL: " + UTConstants.REQUEST_BASE_URL_UT) + TestResponsesUT.setTestURL(url.toString()) + IdlingRegistry.getInstance().register(videoActivity.idlingResource) + } + + @After + fun destroy() { + try { + Handler(Looper.getMainLooper()).post { + videoActivity.removeVideoAd() + } + mockWebServer.shutdown() + } catch (e: Exception) { + e.printStackTrace() + } + IdlingRegistry.getInstance().unregister(videoActivity.idlingResource) + reportHelper.label("Stopping App") + } + + /* + testVideoImpressionTrackerTestAd: To test the impression tracker is fired by the Video Ad. + */ + @Test + fun testVideoImpressionTrackerTestAd() { + videoActivity.triggerAdLoad("14757590", creativeId = 162035356) + Espresso.onView(ViewMatchers.withId(R.id.play_button)).check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView(ViewMatchers.withId(R.id.play_button)).perform(ViewActions.click()) + + Thread.sleep(TestResponsesUT.DELAY) + println("DISPATCH: LIST: " + mockDispatcher.arrRequests) + + var c = 0 + for (request in mockDispatcher.arrRequests) { + if (request.startsWith("GET //it?")) { + c++ + break + } + } + + Assert.assertTrue(c > 0) + } + + /* + testVideoClickTrackerTestAd: To test the click tracker is fired by the Video Ad. + */ + @Test + fun testVideoClickTrackerTestAd() { + videoActivity.triggerAdLoad("14757590", creativeId = 162035356) + Espresso.onView(ViewMatchers.withId(R.id.play_button)).check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView(ViewMatchers.withId(R.id.play_button)).perform(ViewActions.click()) + + onWebView().forceJavascriptEnabled() + onWebView().inWindow(selectFrameByIndex(2)).withElement(findElement(Locator.ID, "ad_indicator_text")) + .perform(webClick()) + Thread.sleep(TestResponsesUT.DELAY) + + println("DISPATCH: LIST: " + mockDispatcher.arrRequests) + + var c = 0 + for (request in mockDispatcher.arrRequests) { + if (request.startsWith("GET //click?")) { + c++ + break + } + } + Assert.assertTrue(c > 0) + } +} diff --git a/tests/TrackerTestApp/app/src/androidTest/java/appnexus/com/trackertestapp/util/SizeMatcher.kt b/tests/TrackerTestApp/app/src/androidTest/java/appnexus/com/trackertestapp/util/SizeMatcher.kt new file mode 100644 index 000000000..83510340a --- /dev/null +++ b/tests/TrackerTestApp/app/src/androidTest/java/appnexus/com/trackertestapp/util/SizeMatcher.kt @@ -0,0 +1,21 @@ +package appnexus.com.trackertestapp.util + +import android.view.View +import org.hamcrest.TypeSafeMatcher + + +class SizeMatcher(private val expectedWith: Int, private val expectedHeight: Int) : + TypeSafeMatcher(View::class.java) { + + override fun matchesSafely(target: View?): Boolean { + val targetWidth: Int = target!!.getWidth() + val targetHeight: Int = target!!.getHeight() + return targetWidth >= expectedWith - 2 && targetWidth <= expectedWith + 2 && targetHeight >= expectedHeight - 2 && targetHeight <= expectedHeight + 2 + } + + override fun describeTo(description: org.hamcrest.Description?) { + description?.appendText("with SizeMatcher: ") + description?.appendValue(expectedWith.toString() + "x" + expectedHeight) + } + +} \ No newline at end of file diff --git a/tests/TrackerTestApp/app/src/androidTest/java/appnexus/com/trackertestapp/util/Util.kt b/tests/TrackerTestApp/app/src/androidTest/java/appnexus/com/trackertestapp/util/Util.kt new file mode 100644 index 000000000..f1c9abb44 --- /dev/null +++ b/tests/TrackerTestApp/app/src/androidTest/java/appnexus/com/trackertestapp/util/Util.kt @@ -0,0 +1,51 @@ +package appnexus.com.trackertestapp.util + +import android.content.Context +import android.net.wifi.WifiManager +import java.net.Inet4Address +import java.net.InetAddress +import java.net.NetworkInterface +import java.util.* + +class Util { + + companion object { + + fun getWifiIp(context: Context): String? { + return context.getSystemService().let { + when { +// it == null -> "No wifi available" + !it.isWifiEnabled -> "Wifi is disabled" + it.connectionInfo == null -> "Wifi not connected" + else -> { + it.isWifiEnabled = false + val ip = it.connectionInfo.ipAddress + ((ip and 0xFF).toString() + "." + (ip shr 8 and 0xFF) + "." + (ip shr 16 and 0xFF) + "." + (ip shr 24 and 0xFF)) + } + } + } + } + + + fun getMobileIPAddress(): InetAddress? { + try { + val interfaces: List = + Collections.list(NetworkInterface.getNetworkInterfaces()) + for (intf in interfaces) { + val addrs: List = Collections.list(intf.getInetAddresses()) + for (addr in addrs) { + if (!addr.isLoopbackAddress() && addr is Inet4Address) { + return addr + } + } + } + } catch (ex: Exception) { + } // for now eat exceptions + return null + } + } +} + +private fun Context.getSystemService(): WifiManager { + return getSystemService(Context.WIFI_SERVICE) as WifiManager +} diff --git a/tests/TrackerTestApp/app/src/androidTest/java/appnexus/com/trackertestapp/viewability/banner/BannerAdViewabilityTrackerTest.kt b/tests/TrackerTestApp/app/src/androidTest/java/appnexus/com/trackertestapp/viewability/banner/BannerAdViewabilityTrackerTest.kt new file mode 100644 index 000000000..1dae8daba --- /dev/null +++ b/tests/TrackerTestApp/app/src/androidTest/java/appnexus/com/trackertestapp/viewability/banner/BannerAdViewabilityTrackerTest.kt @@ -0,0 +1,425 @@ +/* + * Copyright 2021 APPNEXUS INC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package appnexus.com.trackertestapp.viewability.banner + +import android.content.Intent +import android.content.res.Resources +import android.net.Uri +import android.net.http.SslError +import android.os.Handler +import android.os.Looper +import android.webkit.SslErrorHandler +import android.webkit.WebResourceResponse +import android.webkit.WebView +import android.webkit.WebViewClient +import androidx.test.espresso.Espresso +import androidx.test.espresso.IdlingPolicies +import androidx.test.espresso.IdlingRegistry +import androidx.test.espresso.assertion.ViewAssertions +import androidx.test.espresso.matcher.ViewMatchers +import androidx.test.internal.runner.junit4.statement.UiThreadStatement.runOnUiThread +import androidx.test.rule.ActivityTestRule +import androidx.test.runner.AndroidJUnit4 +import appnexus.com.trackertestapp.BannerActivity +import appnexus.com.trackertestapp.MockDispatcher +import appnexus.com.trackertestapp.R +import appnexus.com.trackertestapp.TestResponsesUT +import appnexus.com.trackertestapp.util.Util +import com.appnexus.opensdk.ut.UTConstants +import com.microsoft.appcenter.espresso.Factory +import okhttp3.HttpUrl +import okhttp3.mockwebserver.MockWebServer +import org.junit.* +import org.junit.runner.RunWith +import java.util.concurrent.TimeUnit + + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class BannerAdViewabilityTrackerTest { + val Int.dp: Int + get() = (this / Resources.getSystem().displayMetrics.density).toInt() + val Int.px: Int + get() = (this * Resources.getSystem().displayMetrics.density).toInt() + + @get:Rule + var reportHelper = Factory.getReportHelper() + + @Rule + @JvmField + var mActivityTestRule = ActivityTestRule(BannerActivity::class.java, false, false) + + lateinit var bannerActivity: BannerActivity + + lateinit var mockWebServer: MockWebServer + + lateinit var mockDispatcher: MockDispatcher + + var omidSupported = false + var sessionStart = false + var environmentApp = false + var adSessionTypeHTML = false + var supportsClid = false + var mediaTypeDisplay = false + var partnerNameAppnexus = false + var osAndroid = false + var impressionTypeViewable = false + var creativeTypeHtmlDisplay = false + var omidJsInfo = false + var appLibraryVersion = false + var accessMode = false + var percentageInView0 = false + var percentageInView100 = false + var geometryChange = false + var versionEvent = false + var sessionFinish = false + + + @Before + fun setup() { + IdlingPolicies.setMasterPolicyTimeout(1, TimeUnit.MINUTES) + IdlingPolicies.setIdlingResourceTimeout(1, TimeUnit.MINUTES) + var intent = Intent() + mActivityTestRule.launchActivity(intent) + bannerActivity = mActivityTestRule.activity + Util.getWifiIp(bannerActivity) + Thread.sleep(TestResponsesUT.DELAY) + val ip = Util.getMobileIPAddress() + mockWebServer = MockWebServer() + mockDispatcher = MockDispatcher() + mockDispatcher.adType = "BannerAdOMID" + mockWebServer.start(ip, 8080) + mockWebServer.setDispatcher(mockDispatcher) + + val url: HttpUrl = mockWebServer.url("/") + UTConstants.REQUEST_BASE_URL_UT = url.toString() + println("URL: " + UTConstants.REQUEST_BASE_URL_UT) + TestResponsesUT.setTestURL(url.toString()) + IdlingRegistry.getInstance().register(bannerActivity.idlingResource) + } + + @After + fun destroy() { + try { + Handler(Looper.getMainLooper()).post { + bannerActivity.removeBannerAd() + } + Thread.sleep(TestResponsesUT.DELAY_IP) + mockWebServer.shutdown() + } catch (e: Exception) { + e.printStackTrace() + } + IdlingRegistry.getInstance().unregister(bannerActivity.idlingResource) + reportHelper.label("Stopping App") + + omidSupported = false + sessionStart = false + environmentApp = false + adSessionTypeHTML = false + supportsClid = false + mediaTypeDisplay = false + partnerNameAppnexus = false + osAndroid = false + impressionTypeViewable = false + creativeTypeHtmlDisplay = false + omidJsInfo = false + appLibraryVersion = false + accessMode = false + percentageInView0 = false + percentageInView100 = false + geometryChange = false + versionEvent = false + sessionFinish = false + } + + fun setupAndLoadBanner(){ + bannerActivity.triggerAdLoad("14847003", 300, 250, creativeId = 156075267) + + Espresso.onView(ViewMatchers.withId(R.id.linearLayout)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView(ViewMatchers.withId(R.id.linearLayout)) + .check(ViewAssertions.matches(ViewMatchers.hasChildCount(1))) + Espresso.onView(ViewMatchers.withId(bannerActivity.banner_id)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + + for (i in 0..bannerActivity.banner.childCount - 1) { + var child = bannerActivity.banner.getChildAt(i) + if (child is WebView) { + Handler(Looper.getMainLooper()).post { + var webviewClient = child.webViewClient + child.webViewClient = object : WebViewClient() { + + override fun shouldOverrideUrlLoading( + view: WebView?, + url: String? + ): Boolean { + mockDispatcher.arrRequests.add(Uri.decode(url).toString()) + println("DISPATCH REQUEST: " + url) + return webviewClient.shouldOverrideUrlLoading(view, url) + } + + override fun shouldInterceptRequest( + view: WebView?, + url: String? + ): WebResourceResponse? { + mockDispatcher.arrRequests.add(Uri.decode(url).toString()) + println("DISPATCH REQUEST: " + url) + return webviewClient.shouldInterceptRequest(view, url) + } + + override fun onPageFinished(view: WebView?, url: String?) { + webviewClient.onPageFinished(view, url) + } + + override fun onReceivedError( + view: WebView?, + errorCode: Int, + description: String?, + failingUrl: String? + ) { + webviewClient.onReceivedError(view, errorCode, description, failingUrl) + } + + override fun onReceivedSslError( + view: WebView?, + handler: SslErrorHandler?, + error: SslError? + ) { + webviewClient.onReceivedSslError(view, handler, error) + } + + override fun onLoadResource(view: WebView?, url: String?) { + webviewClient.onLoadResource(view, url) + } + } + } + break + } + } + } + + /* + testBannerOMIDEventSupportedIsYes: To test the OMID is supported tracker is fired by the Banner Ad. + */ + @Test + fun testBannerOMIDEventSupportedIsYes() { + setupAndLoadBanner() + Thread.sleep(TestResponsesUT.DELAY) + println("DISPATCH: LIST: " + mockDispatcher.arrRequests) + for (request in mockDispatcher.arrRequests) { + if (request.contains("OmidSupported[true]")) { + omidSupported = true + break + } + } + Assert.assertTrue(omidSupported) + Espresso.onView(ViewMatchers.withId(R.id.linearLayout)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + } + + /* + testBannerOMIDEventSessionStart: To test the OMID is Session Start & few related events are fired by the Banner Ad. + */ + @Test + fun testBannerOMIDEventSessionStart() { + setupAndLoadBanner() + Thread.sleep(TestResponsesUT.DELAY) + println("DISPATCH: LIST: " + mockDispatcher.arrRequests) + for (request in mockDispatcher.arrRequests) { + if (request.contains("\"type\":\"sessionStart\"")) { + sessionStart = true + } + if (request.contains("\"environment\":\"app\"")) { + environmentApp = true + } + if (request.contains("\"adSessionType\":\"html\"")) { + adSessionTypeHTML = true + } + if (request.contains("\"supports\":[\"clid\",\"vlid\"]")) { + supportsClid = true + } + if (request.contains("\"mediaType\":\"display\"")) { + mediaTypeDisplay = true + } + if (request.contains("\"partnerName\":\"Appnexus\"")) { + partnerNameAppnexus = true + } + if (request.contains("\"os\":\"Android\"")) { + osAndroid = true + } + if (request.contains("\"impressionType\":\"viewable\"")) { + impressionTypeViewable = true + } + if (request.contains("\"creativeType\":\"htmlDisplay\"")) { + creativeTypeHtmlDisplay = true + } + if (request.contains("\"omidJsInfo\":{\"omidImplementer\":\"omsdk\",\"serviceVersion\":\"1.3.7-iab2228\"}")) { + omidJsInfo = true + } + if (request.contains("\"app\":{\"libraryVersion\":\"1.3.7-Appnexus\",\"appId\":\"appnexus.com.trackertestapp\"}")) { + appLibraryVersion = true + } + if (request.contains("\"accessMode\":\"limited\"")) { + accessMode = true + } + } + Assert.assertTrue(sessionStart) + Assert.assertTrue(environmentApp) + Assert.assertTrue(adSessionTypeHTML) + Assert.assertTrue(supportsClid) + Assert.assertTrue(mediaTypeDisplay) + Assert.assertTrue(partnerNameAppnexus) + Assert.assertTrue(osAndroid) + Assert.assertTrue(impressionTypeViewable) + Assert.assertTrue(creativeTypeHtmlDisplay) + Assert.assertTrue(omidJsInfo) + Assert.assertTrue(appLibraryVersion) + Assert.assertTrue(accessMode) + + Espresso.onView(ViewMatchers.withId(R.id.linearLayout)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + } + + /* + testBannerOMIDEventPercentageViewableZero: To test the OMID is 0% Viewable + */ + @Test + fun testBannerOMIDEventPercentageViewableZero() { + setupAndLoadBanner() + Thread.sleep(TestResponsesUT.DELAY) + println("DISPATCH: LIST: " + mockDispatcher.arrRequests) + + for (request in mockDispatcher.arrRequests) { + if (request.contains("\"percentageInView\":0")) { + percentageInView0 = true + } + if (request.contains("\"type\":\"geometryChange\"")) { + geometryChange = true + } + } + + Assert.assertTrue(percentageInView0) + Assert.assertTrue(geometryChange) + + Espresso.onView(ViewMatchers.withId(R.id.linearLayout)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + } + + /* + testBannerOMIDEventViewable100Percentage: To test the OMID is 100% Viewable + */ + @Test + fun testBannerOMIDEventPercentageViewable100() { + setupAndLoadBanner() + Thread.sleep(TestResponsesUT.DELAY) + println("DISPATCH: LIST: " + mockDispatcher.arrRequests) + + for (request in mockDispatcher.arrRequests) { + if (request.contains("\"percentageInView\":100")) { + percentageInView100 = true + } + if (request.contains("\"type\":\"geometryChange\"")) { + geometryChange = true + } + } + + Assert.assertTrue(percentageInView100) + Assert.assertTrue(geometryChange) + + Espresso.onView(ViewMatchers.withId(R.id.linearLayout)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + } + + /* + testBannerOMIDVersionEvent: To verify the OMID version + */ + @Test + fun testBannerOMIDVersionEvent() { + setupAndLoadBanner() + Thread.sleep(TestResponsesUT.DELAY) + println("DISPATCH: LIST: " + mockDispatcher.arrRequests) + + for (request in mockDispatcher.arrRequests) { + if (request.contains("\"libraryVersion\":\"1.3.7-Appnexus\"")) { + versionEvent = true + break + } + } + + Assert.assertTrue(versionEvent) + Espresso.onView(ViewMatchers.withId(R.id.linearLayout)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + } + + /* + testBannerOMIDTypeImpression: To test the OMID Media Type and impressionType + */ + @Test + fun testBannerOMIDTypeImpression() { + setupAndLoadBanner() + Thread.sleep(TestResponsesUT.DELAY) + println("DISPATCH: LIST: " + mockDispatcher.arrRequests) + + for (request in mockDispatcher.arrRequests) { + if (request.contains("\"impressionType\":\"viewable\"")) { + impressionTypeViewable = true + } + if (request.contains("\"mediaType\":\"display\"")) { + mediaTypeDisplay = true + } + if (request.contains("\"creativeType\":\"htmlDisplay\"")) { + creativeTypeHtmlDisplay = true + } + } + + Assert.assertTrue(impressionTypeViewable) + Assert.assertTrue(mediaTypeDisplay) + Assert.assertTrue(creativeTypeHtmlDisplay) + Espresso.onView(ViewMatchers.withId(R.id.linearLayout)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + } + + /* + testBannerOMIDSessionFinish: To verify Session finish getting fired + */ + @Test + fun testBannerOMIDSessionFinish() { + setupAndLoadBanner() + Thread.sleep(TestResponsesUT.DELAY) + println("DISPATCH: LIST: " + mockDispatcher.arrRequests) + runOnUiThread { + bannerActivity.removeBannerAd() + } + Thread.sleep(TestResponsesUT.DELAY) + + for (request in mockDispatcher.arrRequests) { + if (request.contains("\"type\":\"sessionFinish\"")) { + sessionFinish = true + break + } + } + + Assert.assertTrue(sessionFinish) + Espresso.onView(ViewMatchers.withId(R.id.linearLayout)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + } + +} diff --git a/tests/TrackerTestApp/app/src/androidTest/java/appnexus/com/trackertestapp/viewability/banner_video/BannerVideoViewabilityTrackerTests.kt b/tests/TrackerTestApp/app/src/androidTest/java/appnexus/com/trackertestapp/viewability/banner_video/BannerVideoViewabilityTrackerTests.kt new file mode 100644 index 000000000..7c9885b39 --- /dev/null +++ b/tests/TrackerTestApp/app/src/androidTest/java/appnexus/com/trackertestapp/viewability/banner_video/BannerVideoViewabilityTrackerTests.kt @@ -0,0 +1,423 @@ +/* + * Copyright 2021 APPNEXUS INC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package appnexus.com.trackertestapp.viewability.banner_video + +import android.content.Intent +import android.content.res.Resources +import android.net.Uri +import android.net.http.SslError +import android.os.Handler +import android.os.Looper +import android.webkit.SslErrorHandler +import android.webkit.WebResourceResponse +import android.webkit.WebView +import android.webkit.WebViewClient +import androidx.test.espresso.Espresso +import androidx.test.espresso.IdlingPolicies +import androidx.test.espresso.IdlingRegistry +import androidx.test.espresso.assertion.ViewAssertions +import androidx.test.espresso.matcher.ViewMatchers +import androidx.test.internal.runner.junit4.statement.UiThreadStatement.runOnUiThread +import androidx.test.rule.ActivityTestRule +import androidx.test.runner.AndroidJUnit4 +import appnexus.com.trackertestapp.BannerActivity +import appnexus.com.trackertestapp.MockDispatcher +import appnexus.com.trackertestapp.R +import appnexus.com.trackertestapp.TestResponsesUT +import appnexus.com.trackertestapp.util.Util +import com.appnexus.opensdk.ut.UTConstants +import com.microsoft.appcenter.espresso.Factory +import okhttp3.HttpUrl +import okhttp3.mockwebserver.MockWebServer +import org.junit.* +import org.junit.runner.RunWith +import java.util.concurrent.TimeUnit + + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class BannerVideoViewabilityTrackerTests { + val Int.dp: Int + get() = (this / Resources.getSystem().displayMetrics.density).toInt() + val Int.px: Int + get() = (this * Resources.getSystem().displayMetrics.density).toInt() + + @get:Rule + var reportHelper = Factory.getReportHelper() + + @Rule + @JvmField + var mActivityTestRule = ActivityTestRule(BannerActivity::class.java, false, false) + + lateinit var bannerActivity: BannerActivity + + lateinit var mockWebServer: MockWebServer + + lateinit var mockDispatcher: MockDispatcher + + var omidSupported = false + var sessionStart = false + var environmentApp = false + var adSessionTypeHTML = false + var supportsClid = false + var mediaTypeDisplay = false + var partnerNameAppnexus = false + var osAndroid = false + var impressionTypeViewable = false + var creativeTypeHtmlDisplay = false + var omidJsInfo = false + var appLibraryVersion = false + var accessMode = false + var percentageInView0 = false + var percentageInView100 = false + var geometryChange = false + var versionEvent = false + var sessionFinish = false + + + @Before + fun setup() { + IdlingPolicies.setMasterPolicyTimeout(1, TimeUnit.MINUTES) + IdlingPolicies.setIdlingResourceTimeout(1, TimeUnit.MINUTES) + var intent = Intent() + mActivityTestRule.launchActivity(intent) + bannerActivity = mActivityTestRule.activity + Util.getWifiIp(bannerActivity) + Thread.sleep(TestResponsesUT.DELAY) + val ip = Util.getMobileIPAddress() + mockWebServer = MockWebServer() + mockDispatcher = MockDispatcher() + mockDispatcher.adType = "BannerVideoAd" + mockWebServer.start(ip, 8080) + mockWebServer.setDispatcher(mockDispatcher) + + val url: HttpUrl = mockWebServer.url("/") + UTConstants.REQUEST_BASE_URL_UT = url.toString() + println("URL: " + UTConstants.REQUEST_BASE_URL_UT) + TestResponsesUT.setTestURL(url.toString()) + IdlingRegistry.getInstance().register(bannerActivity.idlingResource) + } + + @After + fun destroy() { + try { + Handler(Looper.getMainLooper()).post { + bannerActivity.removeBannerAd() + } + mockWebServer.shutdown() + } catch (e: Exception) { + e.printStackTrace() + } + IdlingRegistry.getInstance().unregister(bannerActivity.idlingResource) + reportHelper.label("Stopping App") + + omidSupported = false + sessionStart = false + environmentApp = false + adSessionTypeHTML = false + supportsClid = false + mediaTypeDisplay = false + partnerNameAppnexus = false + osAndroid = false + impressionTypeViewable = false + creativeTypeHtmlDisplay = false + omidJsInfo = false + appLibraryVersion = false + accessMode = false + percentageInView0 = false + percentageInView100 = false + geometryChange = false + versionEvent = false + sessionFinish = false + } + + fun setupAndLoadBannerVideo(){ + + bannerActivity.triggerAdLoad("14757590", allowVideoDemand = true, creativeId = 162035356) + + Espresso.onView(ViewMatchers.withId(R.id.linearLayout)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + Espresso.onView(ViewMatchers.withId(R.id.linearLayout)) + .check(ViewAssertions.matches(ViewMatchers.hasChildCount(1))) + Espresso.onView(ViewMatchers.withId(bannerActivity.banner_id)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + for (i in 0..bannerActivity.banner.childCount - 1) { + var child = bannerActivity.banner.getChildAt(i) + if (child is WebView) { + Handler(Looper.getMainLooper()).post { + var webviewClient = child.webViewClient + child.webViewClient = object : WebViewClient() { + + override fun shouldOverrideUrlLoading( + view: WebView?, + url: String? + ): Boolean { + mockDispatcher.arrRequests.add(Uri.decode(url).toString()) + println("DISPATCH REQUEST: " + url) + return webviewClient.shouldOverrideUrlLoading(view, url) + } + + override fun shouldInterceptRequest( + view: WebView?, + url: String? + ): WebResourceResponse? { + mockDispatcher.arrRequests.add(Uri.decode(url).toString()) + println("DISPATCH REQUEST: " + url) + return webviewClient.shouldInterceptRequest(view, url) + } + + override fun onPageFinished(view: WebView?, url: String?) { + webviewClient.onPageFinished(view, url) + } + + override fun onReceivedError( + view: WebView?, + errorCode: Int, + description: String?, + failingUrl: String? + ) { + webviewClient.onReceivedError(view, errorCode, description, failingUrl) + } + + override fun onReceivedSslError( + view: WebView?, + handler: SslErrorHandler?, + error: SslError? + ) { + webviewClient.onReceivedSslError(view, handler, error) + } + + override fun onLoadResource(view: WebView?, url: String?) { + webviewClient.onLoadResource(view, url) + } + } + } + break + } + } + } + +// /* +// testBannerVideoOMIDEventSupportedIsYes: To test the OMID is supported tracker is fired by the Banner Ad. +// */ +// @Test +// fun testBannerVideoOMIDEventSupportedIsYes() { +//// setupAndLoadBannerVideo() +//// Thread.sleep(TestResponsesUT.DELAY) +//// println("DISPATCH: LIST: " + mockDispatcher.arrRequests) +//// for (request in mockDispatcher.arrRequests) { +//// if (request.contains("OmidSupported[true]")) { +//// omidSupported = true +//// break +//// } +//// } +//// Assert.assertTrue(omidSupported) +//// Espresso.onView(ViewMatchers.withId(R.id.linearLayout)) +//// .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) +// } +// +// /* +// testBannerVideoOMIDEventSessionStart: To test the OMID is Session Start & few related events are fired by the Banner Ad. +// */ +// @Test +// fun testBannerVideoOMIDEventSessionStart() { +//// setupAndLoadBannerVideo() +//// Thread.sleep(20000) +//// println("DISPATCH: LIST: " + mockDispatcher.arrRequests) +//// for (request in mockDispatcher.arrRequests) { +//// if (request.contains("\"type\":\"sessionStart\"")) { +//// sessionStart = true +//// } +//// if (request.contains("\"environment\":\"app\"")) { +//// environmentApp = true +//// } +//// if (request.contains("\"adSessionType\":\"html\"")) { +//// adSessionTypeHTML = true +//// } +//// if (request.contains("\"supports\":[\"clid\",\"vlid\"]")) { +//// supportsClid = true +//// } +//// if (request.contains("\"mediaType\":\"display\"")) { +//// mediaTypeDisplay = true +//// } +//// if (request.contains("\"partnerName\":\"Appnexus\"")) { +//// partnerNameAppnexus = true +//// } +//// if (request.contains("\"os\":\"Android\"")) { +//// osAndroid = true +//// } +//// if (request.contains("\"impressionType\":\"viewable\"")) { +//// impressionTypeViewable = true +//// } +//// if (request.contains("\"creativeType\":\"htmlDisplay\"")) { +//// creativeTypeHtmlDisplay = true +//// } +//// if (request.contains("\"omidJsInfo\":{\"omidImplementer\":\"omsdk\",\"serviceVersion\":\"1.3.7-iab2228\"}")) { +//// omidJsInfo = true +//// } +//// if (request.contains("\"app\":{\"libraryVersion\":\"1.3.7-Appnexus\",\"appId\":\"appnexus.com.trackertestapp\"}")) { +//// appLibraryVersion = true +//// } +//// if (request.contains("\"accessMode\":\"limited\"")) { +//// accessMode = true +//// } +//// } +//// Assert.assertTrue(sessionStart) +//// Assert.assertTrue(environmentApp) +//// Assert.assertTrue(adSessionTypeHTML) +//// Assert.assertTrue(supportsClid) +//// Assert.assertTrue(mediaTypeDisplay) +//// Assert.assertTrue(partnerNameAppnexus) +//// Assert.assertTrue(osAndroid) +//// Assert.assertTrue(impressionTypeViewable) +//// Assert.assertTrue(creativeTypeHtmlDisplay) +//// Assert.assertTrue(omidJsInfo) +//// Assert.assertTrue(appLibraryVersion) +//// Assert.assertTrue(accessMode) +//// +//// Espresso.onView(ViewMatchers.withId(R.id.linearLayout)) +//// .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) +// } +// + /* + testBannerVideoOMIDEventPercentageViewableZero: To test the OMID is 0% Viewable + */ + @Test + fun testBannerVideoOMIDEventPercentageViewableZero() { + setupAndLoadBannerVideo() + Thread.sleep(TestResponsesUT.DELAY) + println("DISPATCH: LIST: " + mockDispatcher.arrRequests) + + for (request in mockDispatcher.arrRequests) { + if (request.contains("\"percentageInView\":0")) { + percentageInView0 = true + } + if (request.contains("\"type\":\"geometryChange\"")) { + geometryChange = true + } + } + + Assert.assertTrue(percentageInView0) + Assert.assertTrue(geometryChange) + + Espresso.onView(ViewMatchers.withId(R.id.linearLayout)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + } + + /* + testBannerVideoOMIDEventViewable100Percentage: To test the OMID is 100% Viewable + */ + @Test + fun testBannerVideoOMIDEventPercentageViewable100() { + setupAndLoadBannerVideo() + Thread.sleep(TestResponsesUT.DELAY) + println("DISPATCH: LIST: " + mockDispatcher.arrRequests) + + for (request in mockDispatcher.arrRequests) { + if (request.contains("\"percentageInView\":100")) { + percentageInView100 = true + } + if (request.contains("\"type\":\"geometryChange\"")) { + geometryChange = true + } + } + + Assert.assertTrue(percentageInView100) + Assert.assertTrue(geometryChange) + + Espresso.onView(ViewMatchers.withId(R.id.linearLayout)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + } + + /* + testBannerVideoOMIDVersionEvent: To verify the OMID Start event + */ + @Test + fun testBannerVideoOMIDEventStart() { + setupAndLoadBannerVideo() + Thread.sleep(TestResponsesUT.DELAY) + println("DISPATCH: LIST: " + mockDispatcher.arrRequests) + + for (request in mockDispatcher.arrRequests) { + if (request.contains("\"type\":\"start\"")) { + versionEvent = true + break + } + } + + Assert.assertTrue(versionEvent) + Espresso.onView(ViewMatchers.withId(R.id.linearLayout)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + } + + +// /* +// testBannerVideoOMIDTypeImpression: To test the OMID Media Type and impressionType +// */ +// @Test +// fun testBannerVideoOMIDTypeImpression() { +//// setupAndLoadBannerVideo() +//// Thread.sleep(TestResponsesUT.DELAY) +//// println("DISPATCH: LIST: " + mockDispatcher.arrRequests) +//// +//// for (request in mockDispatcher.arrRequests) { +//// if (request.contains("\"impressionType\":\"viewable\"")) { +//// impressionTypeViewable = true +//// } +//// if (request.contains("\"mediaType\":\"display\"")) { +//// mediaTypeDisplay = true +//// } +//// if (request.contains("\"creativeType\":\"htmlDisplay\"")) { +//// creativeTypeHtmlDisplay = true +//// } +//// } +//// +//// Assert.assertTrue(impressionTypeViewable) +//// Assert.assertTrue(mediaTypeDisplay) +//// Assert.assertTrue(creativeTypeHtmlDisplay) +//// Espresso.onView(ViewMatchers.withId(R.id.linearLayout)) +//// .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) +// } + + /* + testBannerVideoOMIDSessionFinish: To verify Session finish getting fired + */ + @Test + fun testBannerVideoOMIDSessionFinish() { + setupAndLoadBannerVideo() + Thread.sleep(TestResponsesUT.DELAY) + runOnUiThread { + bannerActivity.removeBannerAd() + } + Thread.sleep(TestResponsesUT.DELAY) + println("DISPATCH: LIST: " + mockDispatcher.arrRequests) + for (request in mockDispatcher.arrRequests) { + if (request.contains("\"type\":\"sessionFinish\"")) { + sessionFinish = true + break + } + } + Assert.assertTrue(sessionFinish) + Espresso.onView(ViewMatchers.withId(R.id.linearLayout)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + } + +} diff --git a/tests/TrackerTestApp/app/src/main/AndroidManifest.xml b/tests/TrackerTestApp/app/src/main/AndroidManifest.xml new file mode 100644 index 000000000..9c2f8b585 --- /dev/null +++ b/tests/TrackerTestApp/app/src/main/AndroidManifest.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/TrackerTestApp/app/src/main/java/appnexus/com/trackertestapp/BannerActivity.kt b/tests/TrackerTestApp/app/src/main/java/appnexus/com/trackertestapp/BannerActivity.kt new file mode 100644 index 000000000..201394d35 --- /dev/null +++ b/tests/TrackerTestApp/app/src/main/java/appnexus/com/trackertestapp/BannerActivity.kt @@ -0,0 +1,196 @@ +package appnexus.com.trackertestapp + +import android.os.Build +import android.os.Bundle +import android.os.Handler +import android.os.Looper +import android.view.View +import android.webkit.WebView +import android.widget.ImageView +import android.widget.LinearLayout +import android.widget.TextView +import android.widget.Toast +import androidx.appcompat.app.AppCompatActivity +import androidx.test.espresso.idling.CountingIdlingResource +import appnexus.com.trackertestapp.utility.Utils +import com.appnexus.opensdk.* +import com.appnexus.opensdk.utils.Settings +import com.squareup.picasso.Picasso + +class BannerActivity : AppCompatActivity(), AdListener, AppEventListener, NativeAdEventListener { + + val banner_id: Int = 1234 + lateinit var banner: BannerAdView + var clickUrl: String? = "" + var shouldDisplay = true + var idlingResource: CountingIdlingResource = CountingIdlingResource("Banner Load Count", true) + + override fun onAdClicked(p0: AdView?) { + clickUrl = "" + Toast.makeText(this, "Ad Clicked", Toast.LENGTH_LONG).show() + } + + override fun onAdClicked(p0: AdView?, p1: String?) { + clickUrl = p1 + Toast.makeText(this, "Ad Clicked with URL", Toast.LENGTH_LONG).show() + } + + override fun onAdExpanded(p0: AdView?) { + Toast.makeText(this, "Ad Expanded", Toast.LENGTH_LONG).show() + } + + override fun onAdCollapsed(p0: AdView?) { + Toast.makeText(this, "Ad Collapsed", Toast.LENGTH_LONG).show() + } + + override fun onAdRequestFailed(p0: AdView?, p1: ResultCode?) { + Toast.makeText(this, "Ad Failed: " + p1?.message, Toast.LENGTH_LONG).show() + } + + override fun onLazyAdLoaded(adView: AdView?) { + if (!idlingResource.isIdleNow) + idlingResource.decrement() + } + + override fun onAdLoaded(ad: AdView?) { + Toast.makeText(this, "AdLoaded", Toast.LENGTH_LONG).show() + if (layout.childCount > 0) + layout.removeAllViews() + if (!shouldDisplay) { + banner.visibility = View.GONE + } + layout.addView(ad) + if (!idlingResource.isIdleNow) + idlingResource.decrement() + } + + override fun onAdLoaded(nativeAdResponse: NativeAdResponse?) { + Toast.makeText(this, "Native Ad Loaded", Toast.LENGTH_LONG).show() + handleNativeResponse(nativeAdResponse) + } + + + lateinit var layout: LinearLayout + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_banner) + layout = findViewById(R.id.linearLayout) + + Settings.getSettings().debug_mode = true + Settings.getSettings().useHttps = true + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + WebView.setWebContentsDebuggingEnabled(true); + } + } + + fun triggerAdLoad(placement: String?, width: Int = 300, height: Int = 250, useHttps: Boolean = true, allowNativeDemand: Boolean = false, allowVideoDemand: Boolean = false, rendererId: Int = -1, useNativeRenderer: Boolean = false, clickThroughAction: ANClickThroughAction = ANClickThroughAction.OPEN_SDK_BROWSER, resizeToFitContainer: Boolean = false, expandsToFitScreenWidth: Boolean = false, creativeId: Int? = null, bgTask: Boolean = false, countImpressionOnAdLoad: Boolean = false, onePx: Boolean = false, lazyLoad: Boolean = false) { + + SDKSettings.enableBackgroundThreading(bgTask) + Handler(Looper.getMainLooper()).post { + + idlingResource.increment() + banner = BannerAdView(this) + banner.countImpressionOnAdLoad = countImpressionOnAdLoad + if (lazyLoad) { + banner.enableLazyLoad() + } + banner.appEventListener = this + banner.id = banner_id + banner.placementID = if (placement == null) "17982237" else placement + banner.setAdSize(width, height) + banner.setAllowBannerDemand(true) + banner.setAllowNativeDemand(allowNativeDemand, rendererId) + banner.enableNativeRendering(useNativeRenderer) + banner.loadsInBackground = false + banner.clickThroughAction = ANClickThroughAction.RETURN_URL + banner.allowVideoDemand = allowVideoDemand + banner.resizeAdToFitContainer = resizeToFitContainer + banner.expandsToFitScreenWidth = expandsToFitScreenWidth + SDKSettings.useHttps(useHttps) + banner.adListener = this + if(creativeId != null) { + val utils = Utils() + utils.setForceCreativeId(creativeId, banner = banner); + } + banner.loadAd() + } + } + + fun removeBannerAd(){ + banner.removeAllViews() + banner.destroy() + } + + override fun onDestroy() { + super.onDestroy() + if (banner != null){ + banner.destroy() + } + } + + fun loadLazyAd() { + if (banner.isLazyLoadEnabled) { + banner.loadLazyAd() + idlingResource.increment() + } + } + + private fun handleNativeResponse(response: NativeAdResponse?) { + + val nativeView: View = View.inflate(this, R.layout.layout_native, null) + val icon: ImageView = nativeView.findViewById(R.id.icon) + val image: ImageView = nativeView.findViewById(R.id.image) + val title: TextView = nativeView.findViewById(R.id.title) + val desc: TextView = nativeView.findViewById(R.id.description) + + if (response != null) { + title.setText(response.title) + desc.setText(response.description) + layout.addView(nativeView) + if (!idlingResource.isIdleNow) + idlingResource.decrement() + if (response.iconUrl != null) { + Picasso.get() + .load(response.iconUrl) + .resize(50, 50) + .placeholder(R.drawable.download) + .error(R.drawable.images) + .into(icon) + } + if (response.imageUrl != null) { + Picasso.get() + .load(response.imageUrl) + .placeholder(R.drawable.download) + .error(R.drawable.images) + .into(image) + } + } + + NativeAdSDK.registerTracking(response, findViewById(R.id.main_native), this) + } + + override fun onAdImpression() { + } + + override fun onAdAboutToExpire() { + + } + + override fun onAdWasClicked() { + + } + + override fun onAdWasClicked(clickUrl: String?, fallbackURL: String?) { + } + + override fun onAdExpired() { + } + + override fun onAdWillLeaveApplication() { + } + + override fun onAppEvent(adView: AdView?, name: String?, data: String?) { + + } + +} diff --git a/tests/TrackerTestApp/app/src/main/java/appnexus/com/trackertestapp/InterstitialActivity.kt b/tests/TrackerTestApp/app/src/main/java/appnexus/com/trackertestapp/InterstitialActivity.kt new file mode 100644 index 000000000..c48466694 --- /dev/null +++ b/tests/TrackerTestApp/app/src/main/java/appnexus/com/trackertestapp/InterstitialActivity.kt @@ -0,0 +1,132 @@ +package appnexus.com.trackertestapp + +import android.os.Build +import android.os.Bundle +import android.os.Handler +import android.os.Looper +import android.webkit.WebView +import android.widget.LinearLayout +import android.widget.Toast +import androidx.appcompat.app.AppCompatActivity +import androidx.test.espresso.idling.CountingIdlingResource +import appnexus.com.trackertestapp.utility.Utils +import com.appnexus.opensdk.* +import com.appnexus.opensdk.utils.Settings + +class InterstitialActivity : AppCompatActivity(), AdListener, AppEventListener { + + var isAdCollapsed: Boolean = false + var isAdExpanded: Boolean = false + var idlingResource: CountingIdlingResource = CountingIdlingResource("Interstitial Load Counter", true) + val interstitial_id: Int = 1235 + lateinit var interstitial: InterstitialAdView + var autoDismiss: Int = -1 + + override fun onAdClicked(p0: AdView?) { + Toast.makeText(this, "Ad Clicked", Toast.LENGTH_LONG).show() + } + + override fun onAdClicked(p0: AdView?, p1: String?) { + Toast.makeText(this, "Ad Clicked with URL", Toast.LENGTH_LONG).show() + } + + override fun onAdExpanded(p0: AdView?) { + Toast.makeText(this, "Ad Expanded", Toast.LENGTH_LONG).show() + isAdExpanded = true + } + + override fun onAdCollapsed(p0: AdView?) { + Toast.makeText(this, "Ad Collapsed", Toast.LENGTH_LONG).show() + isAdCollapsed = true +// if (!idlingResource.isIdleNow) +// idlingResource.decrement() + } + + override fun onAdRequestFailed(p0: AdView?, p1: ResultCode?) { + Toast.makeText(this, "Ad Failed: " + p1?.message, Toast.LENGTH_LONG).show() + println(p1?.message) + if (!idlingResource.isIdleNow) + idlingResource.decrement() + } + + override fun onLazyAdLoaded(adView: AdView?) { + } + + override fun onAdLoaded(ad: AdView?) { + Toast.makeText(this, "AdLoaded", Toast.LENGTH_LONG).show() + if (!idlingResource.isIdleNow) + idlingResource.decrement() + showAd() + } + + private fun showAd() { +// if (layout.childCount > 0) +// layout.removeAllViews() +// layout.addView(interstitial) + interstitial.showWithAutoDismissDelay(autoDismiss) +// idlingResource.increment() + } + + override fun onAdLoaded(p0: NativeAdResponse?) { + Toast.makeText(this, "Native Ad Loaded", Toast.LENGTH_LONG).show() + if (!idlingResource.isIdleNow) + idlingResource.decrement() + } + + lateinit var layout: LinearLayout + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_banner) + layout = findViewById(R.id.linearLayout) + + Settings.getSettings().debug_mode = true + Settings.getSettings().useHttps = true + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + WebView.setWebContentsDebuggingEnabled(true); + } + } + + fun triggerAdLoad(placement: String?, useHttps: Boolean = true, autoDismiss: Int = -1, closeButtonDelay: Int = 1, creativeId: Int? = null, bgTask: Boolean = false) { + + SDKSettings.enableBackgroundThreading(bgTask) + Handler(Looper.getMainLooper()).post { + + this.autoDismiss = 10 + interstitial = InterstitialAdView(this) + interstitial.id = interstitial_id + interstitial.placementID = if (placement == null) "17982237" else placement + SDKSettings.useHttps(useHttps) + interstitial.adListener = this + interstitial.closeButtonDelay = 5 + interstitial.clickThroughAction = ANClickThroughAction.RETURN_URL + if(creativeId != null) { + val utils = Utils() + utils.setForceCreativeId(creativeId, interstitial = interstitial); + } + interstitial.loadAd() + idlingResource.increment() + + } + } + + fun performClickAd(){ + interstitial.performClick() + + } + + fun removeInterstitialAd(){ + interstitial.removeAllViews() + interstitial.destroy() + } + + override fun onDestroy() { + super.onDestroy() + if (interstitial != null){ + interstitial.destroy() + } + } + + override fun onAppEvent(adView: AdView?, name: String?, data: String?) { + println("AppEvent: " + name + ", DATA: " + data) + } +} diff --git a/tests/TrackerTestApp/app/src/main/java/appnexus/com/trackertestapp/NativeActivity.kt b/tests/TrackerTestApp/app/src/main/java/appnexus/com/trackertestapp/NativeActivity.kt new file mode 100644 index 000000000..99f2d1297 --- /dev/null +++ b/tests/TrackerTestApp/app/src/main/java/appnexus/com/trackertestapp/NativeActivity.kt @@ -0,0 +1,143 @@ +package appnexus.com.trackertestapp + +import android.os.Bundle +import android.os.Handler +import android.os.Looper +import android.widget.ImageView +import android.widget.TextView +import android.widget.Toast +import androidx.appcompat.app.AppCompatActivity +import androidx.test.espresso.idling.CountingIdlingResource +import appnexus.com.trackertestapp.utility.Utils +import com.appnexus.opensdk.* +import com.appnexus.opensdk.tasksmanager.TasksManager +import com.appnexus.opensdk.utils.Settings +import com.squareup.picasso.Picasso +import java.util.concurrent.ExecutorService + +class NativeActivity : AppCompatActivity(), NativeAdRequestListener, NativeAdEventListener { + + var didLogImpression: Boolean = false + lateinit var nativeAdRequest: NativeAdRequest + var idlingResource: CountingIdlingResource = CountingIdlingResource("Native Load Count", true) + lateinit var nativeResponse: NativeAdResponse + + override fun onAdLoaded(nativeAdResponse: NativeAdResponse?) { + Toast.makeText(this, "Native Ad Loaded", Toast.LENGTH_LONG).show() + TasksManager.getInstance().executeOnMainThread { + handleNativeResponse(nativeAdResponse) + } + } + + override fun onAdFailed(errorcode: ResultCode?, adResponseinfo: ANAdResponseInfo?) { + Toast.makeText(this, "Native Ad Failed", Toast.LENGTH_LONG).show() + if (!idlingResource.isIdleNow) + idlingResource.decrement() + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.layout_native) + Settings.getSettings().useHttps = true + } + + override fun onDestroy() { + super.onDestroy() + if (nativeResponse != null){ + nativeResponse.destroy() + } + } + + fun removeNativeAd(){ + if (nativeResponse != null){ + nativeResponse.destroy() + } + } + + fun triggerAdLoad( + placement: String?, + useHttps: Boolean = true, + creativeId: Int? = null, + bgTask: Boolean = false, + useExecutor: Boolean = false + ) { + SDKSettings.enableBackgroundThreading(bgTask) + Handler(Looper.getMainLooper()).post { + + didLogImpression = false + nativeAdRequest = NativeAdRequest(this, if (placement == null) "17982237" else placement) + nativeAdRequest.clickThroughAction = ANClickThroughAction.RETURN_URL + nativeAdRequest.placementID = if (placement == null) "17982237" else placement + SDKSettings.useHttps(useHttps) + nativeAdRequest.listener = this + if (creativeId != null) { + val utils = Utils() + utils.setForceCreativeId(creativeId, nativeAdRequest = nativeAdRequest); + } + + + if (useExecutor) { + TasksManager.getInstance().executeOnBackgroundThread({ + nativeAdRequest.loadAd() + }) + } else { + nativeAdRequest.loadAd() + } + idlingResource.increment() + } + } + + private fun handleNativeResponse(response: NativeAdResponse?) { + if (response != null) { + nativeResponse = response + val icon: ImageView = findViewById(R.id.icon) + val image: ImageView = findViewById(R.id.image) + val title: TextView = findViewById(R.id.title) + val desc: TextView = findViewById(R.id.description) + title.setText(response?.title) + desc.setText(response?.description) + if (!idlingResource.isIdleNow) + idlingResource.decrement() + + + if (response.iconUrl != null){ + Picasso.get() + .load(response.iconUrl) + .resize(50, 50) + .placeholder(R.drawable.download) + .error(R.drawable.images) + .into(icon) + } + if (response.imageUrl != null){ + Picasso.get() + .load(response.imageUrl) + .placeholder(R.drawable.download) + .error(R.drawable.images) + .into(image) + } + } + + NativeAdSDK.registerTracking(response, findViewById(R.id.main_native), this) + } + + override fun onAdImpression() { + didLogImpression = true + } + + override fun onAdAboutToExpire() { + + } + + override fun onAdWasClicked() { + + } + + override fun onAdWasClicked(clickUrl: String?, fallbackURL: String?) { + } + + override fun onAdExpired() { + } + + override fun onAdWillLeaveApplication() { + } +} diff --git a/tests/TrackerTestApp/app/src/main/java/appnexus/com/trackertestapp/VideoActivity.kt b/tests/TrackerTestApp/app/src/main/java/appnexus/com/trackertestapp/VideoActivity.kt new file mode 100644 index 000000000..918a01bb5 --- /dev/null +++ b/tests/TrackerTestApp/app/src/main/java/appnexus/com/trackertestapp/VideoActivity.kt @@ -0,0 +1,159 @@ +package appnexus.com.trackertestapp + +import android.app.PendingIntent.getActivity +import android.content.Context +import android.net.Uri +import android.os.Build +import androidx.appcompat.app.AppCompatActivity +import android.os.Bundle +import android.os.Handler +import android.os.Looper +import android.view.View +import android.webkit.WebView +import android.widget.* +import androidx.test.espresso.idling.CountingIdlingResource +import appnexus.com.trackertestapp.utility.Utils +import com.appnexus.opensdk.* +import com.appnexus.opensdk.instreamvideo.Quartile +import com.appnexus.opensdk.instreamvideo.VideoAd +import com.appnexus.opensdk.instreamvideo.VideoAdLoadListener +import com.appnexus.opensdk.instreamvideo.VideoAdPlaybackListener +import com.appnexus.opensdk.utils.Clog +import com.appnexus.opensdk.utils.Settings + +class VideoActivity : AppCompatActivity(), VideoAdLoadListener { + + private lateinit var baseContainer: RelativeLayout + private lateinit var playButon: ImageButton + private lateinit var videoPlayer: VideoView + private lateinit var context: Context + + override fun onAdLoaded(videoAd: VideoAd?) { + Toast.makeText( + this, "Ad is ready. Hit on Play button to start", + Toast.LENGTH_SHORT + ).show() + playButon.setVisibility(View.VISIBLE) + idlingResource.decrement() + } + + override fun onAdRequestFailed(videoAd: VideoAd?, errorCode: ResultCode?) { + Toast.makeText(this, "Ad Failed: " + errorCode?.message, Toast.LENGTH_LONG).show() + println(errorCode?.message) + idlingResource.decrement() + } + + lateinit var video: VideoAd + var idlingResource: CountingIdlingResource = CountingIdlingResource("Banner Load Count", true) + + + lateinit var layout: LinearLayout + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_banner) + context = this + Settings.getSettings().useHttps = true + Settings.getSettings().debug_mode = true + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + WebView.setWebContentsDebuggingEnabled(true); + } + layout = findViewById(R.id.linearLayout) + } + + override fun onDestroy() { + super.onDestroy() + if (video != null){ + video.activityOnDestroy() + } + } + + + fun removeVideoAd(){ + video.removeAd() + video.activityOnDestroy() + } + + fun triggerAdLoad(placement: String?, useHttps: Boolean = true, creativeId: Int? = null, bgTask: Boolean = false) { + Handler(Looper.getMainLooper()).post { + SDKSettings.enableBackgroundThreading(bgTask) + video = VideoAd(this, if (placement == null) "13989299" else placement) + video.clickThroughAction = ANClickThroughAction.RETURN_URL + video.adLoadListener = this + SDKSettings.useHttps(useHttps) + // Load and display a Video + // Video Ad elements + val instreamVideoLayout = layoutInflater.inflate(R.layout.fragment_preview_instream, null) + playButon = instreamVideoLayout.findViewById(R.id.play_button) as ImageButton + videoPlayer = instreamVideoLayout.findViewById(R.id.video_player) as VideoView + baseContainer = instreamVideoLayout.findViewById(R.id.instream_container_Layout) as RelativeLayout + layout.addView(baseContainer) + + baseContainer.layoutParams.height = 1000 + baseContainer.layoutParams.width = RelativeLayout.LayoutParams.MATCH_PARENT + + videoPlayer.setVideoURI(Uri.parse(getString(R.string.content_url_1))) + var controller = MediaController(this) + videoPlayer.setMediaController(controller) + + playButon.setOnClickListener { + if (video.isReady()) { + video.playAd(baseContainer) + idlingResource.increment() + } else { + videoPlayer.start() + } + playButon.visibility = View.INVISIBLE + } + + if(creativeId != null) { + Clog.e("VIDEO", creativeId.toString()) + val utils = Utils() + utils.setForceCreativeId(creativeId, video = video); + } + + // Set PlayBack Listener. + video.setVideoPlaybackListener(object : VideoAdPlaybackListener { + + override fun onAdPlaying(videoAd: VideoAd) { +// setIdleState(true) + if (!idlingResource.isIdleNow) + Handler(Looper.getMainLooper()).postDelayed(Runnable { idlingResource.decrement() }, 2000) + Toast.makeText(context, "OnAdPlaying", Toast.LENGTH_SHORT).show() +// Clog.d(Constants.BASE_LOG_TAG, "onAdPlaying::") + } + + override fun onQuartile(view: VideoAd, quartile: Quartile) { +// Clog.d(Constants.BASE_LOG_TAG, "onQuartile::$quartile") + } + + override fun onAdCompleted( + view: VideoAd, + playbackState: VideoAdPlaybackListener.PlaybackCompletionState + ) { + if (playbackState == VideoAdPlaybackListener.PlaybackCompletionState.COMPLETED) { +// Clog.d(Constants.BASE_LOG_TAG, "adCompleted::playbackState") + } else if (playbackState == VideoAdPlaybackListener.PlaybackCompletionState.SKIPPED) { +// Clog.d(Constants.BASE_LOG_TAG, "adSkipped::") + } + videoPlayer.start() + } + + override fun onAdMuted(view: VideoAd, isMute: Boolean) { +// Clog.d(Constants.BASE_LOG_TAG, "isAudioMute::$isMute") + } + + override fun onAdClicked(adView: VideoAd) { +// Clog.d(Constants.BASE_LOG_TAG, "onAdClicked") + } + + override fun onAdClicked(videoAd: VideoAd, clickUrl: String) { +// Clog.d(Constants.BASE_LOG_TAG, "onAdClicked::clickUrl$clickUrl") +// toast("onAdClicked::clickUrl$clickUrl") + } + }) + + video.loadAd() + idlingResource.increment() + } + } +} diff --git a/tests/TrackerTestApp/app/src/main/java/appnexus/com/trackertestapp/adapter/AdRecyclerAdapter.kt b/tests/TrackerTestApp/app/src/main/java/appnexus/com/trackertestapp/adapter/AdRecyclerAdapter.kt new file mode 100644 index 000000000..e244662e7 --- /dev/null +++ b/tests/TrackerTestApp/app/src/main/java/appnexus/com/trackertestapp/adapter/AdRecyclerAdapter.kt @@ -0,0 +1,39 @@ +package appnexus.com.trackertestapp.adapter + +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import appnexus.com.trackertestapp.R +import appnexus.com.trackertestapp.listener.RecyclerItemClickListener +import kotlinx.android.synthetic.main.layout_recycler.view.* + +class AdRecyclerAdapter( + val items: ArrayList, + val context: Context, + val listener: RecyclerItemClickListener +) : RecyclerView.Adapter() { + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + return ViewHolder(LayoutInflater.from(context).inflate( + R.layout.layout_recycler, parent, false + )) + } + + override fun getItemCount(): Int { + return items.size + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + holder.tvAd.text = items.get(position) + holder.tvAd.setOnClickListener { + listener.onItemClick(position) + } + } + + class ViewHolder (view: View) : RecyclerView.ViewHolder(view) { + // Holds the TextView that will add each animal to + val tvAd = view.tvAd + } + +} \ No newline at end of file diff --git a/tests/TrackerTestApp/app/src/main/java/appnexus/com/trackertestapp/adapter/AdViewRecyclerAdapter.kt b/tests/TrackerTestApp/app/src/main/java/appnexus/com/trackertestapp/adapter/AdViewRecyclerAdapter.kt new file mode 100644 index 000000000..72deee71c --- /dev/null +++ b/tests/TrackerTestApp/app/src/main/java/appnexus/com/trackertestapp/adapter/AdViewRecyclerAdapter.kt @@ -0,0 +1,350 @@ +package appnexus.com.trackertestapp.adapter + +import android.annotation.SuppressLint +import android.content.Context +import android.graphics.Bitmap +import android.net.Uri +import android.os.Handler +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.* +import androidx.recyclerview.widget.RecyclerView +import appnexus.com.trackertestapp.R +import com.appnexus.opensdk.AdView +import com.appnexus.opensdk.BannerAdView +import com.appnexus.opensdk.InterstitialAdView +import com.appnexus.opensdk.NativeAdResponse +import com.appnexus.opensdk.instreamvideo.Quartile +import com.appnexus.opensdk.instreamvideo.VideoAd +import com.appnexus.opensdk.instreamvideo.VideoAdPlaybackListener +import com.appnexus.opensdk.utils.Clog +import com.appnexus.opensdk.utils.ViewUtil +import kotlinx.android.synthetic.main.layout_ad_view.view.* +import java.util.* + +class AdViewRecyclerAdapter(val items: ArrayList, val context: Context) : + RecyclerView.Adapter() { + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + return ViewHolder( + LayoutInflater.from(context).inflate( + R.layout.layout_ad_view, parent, false + ) + ) + } + + override fun getItemCount(): Int { + return items.size + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + val currentAd = items.get(position) + if (currentAd is NativeAdResponse) { + handleNativeResponse(currentAd, holder.layoutMain) + } else if (currentAd is AdView) { + if (currentAd is InterstitialAdView) { + holder.layoutMain.setOnClickListener({ + currentAd.show() + }) + } else { + + if (currentAd is BannerAdView) { + if (currentAd.isLazyLoadEnabled && currentAd.getTag( + R.string.button_tag + ) == null + ) { + val btn = Button(context) + btn.setText("Activate") + btn.setOnClickListener { + Clog.e("LAZYLOAD", "Webview Activated") + currentAd.loadLazyAd() + currentAd.setTag(R.string.button_tag, btn) + } + currentAd.setTag(R.string.button_tag, true) + holder.layoutMain.addView(btn) + } else { + if (currentAd.getTag(R.string.button_tag) is Button && currentAd.isLazyLoadEnabled) { + ViewUtil.removeChildFromParent(currentAd.getTag(R.string.button_tag) as Button) + ViewUtil.removeChildFromParent(currentAd) + holder.layoutMain.addView(currentAd) + Clog.e("LAZYLOAD", "Banner Added to the parent view") + currentAd.post({ + Clog.e("WIDTH", "${currentAd.width}") + Clog.e("HEIGHT", "${currentAd.height}") + currentAd.invalidate() + currentAd.visibility = View.VISIBLE + }) + } else { + ViewUtil.removeChildFromParent(currentAd) + holder.layoutMain.addView(currentAd) + Clog.e("LAZYLOAD", "Banner Added to the parent view") + currentAd.post({ + Clog.e("WIDTH", "${currentAd.width}") + Clog.e("HEIGHT", "${currentAd.height}") + currentAd.invalidate() + currentAd.visibility = View.VISIBLE + }) + } + } + } + } + } else if (currentAd is VideoAd) { + handleVideoAd(currentAd, holder.layoutMain) + } + } + + class ViewHolder(view: View) : RecyclerView.ViewHolder(view) { + // Holds the TextView that will add each animal to + val layoutMain = view.layoutMain + } + + private fun handleVideoAd(videoAd: VideoAd, layoutVideo: LinearLayout) { + // Load and display a Video + // Video Ad elements + val instreamVideoLayout = + LayoutInflater.from(context).inflate(R.layout.fragment_preview_instream, null) + val playButon = instreamVideoLayout.findViewById(R.id.play_button) as ImageButton + playButon.visibility = View.VISIBLE + val videoPlayer = instreamVideoLayout.findViewById(R.id.video_player) as VideoView + val baseContainer = + instreamVideoLayout.findViewById(R.id.instream_container_Layout) as RelativeLayout + + layoutVideo.removeAllViews() + layoutVideo.addView(baseContainer) + + baseContainer.layoutParams.height = 1000 + baseContainer.layoutParams.width = RelativeLayout.LayoutParams.MATCH_PARENT + + videoPlayer.setVideoURI(Uri.parse("https://acdn.adnxs.com/mobile/video_test/content/Scenario.mp4")) + val controller = MediaController(context) + videoPlayer.setMediaController(controller) + playButon.setOnClickListener { + if (videoAd.isReady) { + videoAd.playAd(baseContainer) + } else { + videoPlayer.start() + } + playButon.visibility = View.INVISIBLE + } + + // Set PlayBack Listener. + videoAd.videoPlaybackListener = object : VideoAdPlaybackListener { + + override fun onAdPlaying(videoAd: VideoAd) { + Clog.d("VideoAd", "onAdPlaying::") + } + + override fun onQuartile(view: VideoAd, quartile: Quartile) { + Clog.d("VideoAd", "onQuartile::$quartile") + } + + override fun onAdCompleted( + view: VideoAd, + playbackState: VideoAdPlaybackListener.PlaybackCompletionState + ) { + if (playbackState == VideoAdPlaybackListener.PlaybackCompletionState.COMPLETED) { + Clog.d("VideoAd", "adCompleted::playbackState") + } else if (playbackState == VideoAdPlaybackListener.PlaybackCompletionState.SKIPPED) { + Clog.d("VideoAd", "adSkipped::") + } + videoPlayer.start() + } + + override fun onAdMuted(view: VideoAd, isMute: Boolean) { + Clog.d("VideoAd", "isAudioMute::$isMute") + } + + override fun onAdClicked(adView: VideoAd) { + Clog.d("VideoAd", "onAdClicked") + } + + override fun onAdClicked(videoAd: VideoAd, clickUrl: String) { + Clog.d("VideoAd", "onAdClicked::clickUrl$clickUrl") + } + } + } + + private fun handleNativeResponse(response: NativeAdResponse, layoutNative: LinearLayout) { + val builder = NativeAdBuilder(context) + if (response.icon != null) + builder.setIconView(response.icon) + if (response.image != null) + builder.setImageView(response.image) + builder.setTitle(response.title) + builder.setDescription(response.description) + builder.setCallToAction(response.callToAction) + builder.setSponsoredBy(response.sponsoredBy) + + if (response.adStarRating != null) { + builder.setAdStartValue(response.adStarRating.value.toString() + "/" + response.adStarRating.scale.toString()) + } + + // register all the views + if (builder.container != null && builder.container!!.parent != null) + (builder.container!!.parent as ViewGroup).removeAllViews() + + val nativeContainer = builder.container + Handler().post { + layoutNative.removeAllViews() + layoutNative.addView(nativeContainer) + } + } + + internal inner class NativeAdBuilder @SuppressLint("NewApi") + constructor(context: Context) { + var container: RelativeLayout? = null + var iconAndTitle: LinearLayout + var customViewLayout: LinearLayout + var imageView: ImageView + var iconView: ImageView + var title: TextView + var description: TextView + var callToAction: TextView + var adStarRating: TextView + var socialContext: TextView + var sponsoredBy: TextView + var customView = + null // Any Mediated network requiring to render there own view for impression tracking would go in here. + var views: LinkedList? = null + + val allViews: LinkedList + get() { + if (views == null) { + views = LinkedList() + views!!.add(imageView) + views!!.add(iconView) + views!!.add(title) + views!!.add(description) + views!!.add(callToAction) + views!!.add(adStarRating) + views!!.add(socialContext) + views!!.add(sponsoredBy) + } + return views as LinkedList + } + + + init { + container = RelativeLayout(context) + + iconAndTitle = LinearLayout(context) + iconAndTitle.id = View.generateViewId() + iconAndTitle.orientation = LinearLayout.HORIZONTAL + iconView = ImageView(context) + iconAndTitle.addView(iconView) + title = TextView(context) + iconAndTitle.addView(title) + container!!.addView(iconAndTitle) + + + imageView = ImageView(context) + imageView.id = View.generateViewId() + val imageView_params = RelativeLayout.LayoutParams( + ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.WRAP_CONTENT + ) + imageView_params.addRule(RelativeLayout.BELOW, iconAndTitle.id) + + + description = TextView(context) + description.id = View.generateViewId() + val description_params = RelativeLayout.LayoutParams( + ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.WRAP_CONTENT + ) + description_params.addRule(RelativeLayout.BELOW, imageView.id) + + + callToAction = TextView(context) + callToAction.id = View.generateViewId() + val callToAction_params = RelativeLayout.LayoutParams( + ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.WRAP_CONTENT + ) + callToAction_params.addRule(RelativeLayout.BELOW, description.id) + + + adStarRating = TextView(context) + adStarRating.id = View.generateViewId() + val adStarRating_params = RelativeLayout.LayoutParams( + ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.WRAP_CONTENT + ) + adStarRating_params.addRule(RelativeLayout.BELOW, callToAction.id) + + socialContext = TextView(context) + socialContext.id = View.generateViewId() + val socialContext_params = RelativeLayout.LayoutParams( + ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.WRAP_CONTENT + ) + socialContext_params.addRule(RelativeLayout.BELOW, adStarRating.id) + + + sponsoredBy = TextView(context) + sponsoredBy.id = View.generateViewId() + val sponsoredBy_params = RelativeLayout.LayoutParams( + ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.WRAP_CONTENT + ) + sponsoredBy_params.addRule(RelativeLayout.BELOW, socialContext.id) + + + customViewLayout = LinearLayout(context) + customViewLayout.id = View.generateViewId() + customViewLayout.orientation = LinearLayout.HORIZONTAL + val customViewLayout_params = RelativeLayout.LayoutParams( + ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.WRAP_CONTENT + ) + customViewLayout_params.addRule(RelativeLayout.BELOW, sponsoredBy.id) + + + container!!.addView(description, description_params) + container!!.addView(imageView, imageView_params) + container!!.addView(callToAction, callToAction_params) + container!!.addView(adStarRating, adStarRating_params) + container!!.addView(socialContext, socialContext_params) + container!!.addView(sponsoredBy, sponsoredBy_params) + container!!.addView(customViewLayout, customViewLayout_params) + } + + fun setImageView(bitmap: Bitmap) { + imageView.setImageBitmap(bitmap) + } + + fun setIconView(bitmap: Bitmap) { + iconView.setImageBitmap(bitmap) + } + + + fun setCustomView(customView: View) { + this.customViewLayout.addView(customView) + } + + fun setTitle(title: String) { + this.title.text = title + } + + fun setDescription(description: String) { + this.description.text = description + } + + fun setCallToAction(callToAction: String) { + this.callToAction.text = callToAction + } + + fun setSocialContext(socialContext: String) { + this.socialContext.text = socialContext + } + + fun setSponsoredBy(sponsoredBy: String) { + this.sponsoredBy.text = sponsoredBy + } + + fun setAdStartValue(value: String) { + this.adStarRating.text = value + } + } + +} \ No newline at end of file diff --git a/tests/TrackerTestApp/app/src/main/java/appnexus/com/trackertestapp/listener/RecyclerItemClickListener.kt b/tests/TrackerTestApp/app/src/main/java/appnexus/com/trackertestapp/listener/RecyclerItemClickListener.kt new file mode 100644 index 000000000..80e9c212a --- /dev/null +++ b/tests/TrackerTestApp/app/src/main/java/appnexus/com/trackertestapp/listener/RecyclerItemClickListener.kt @@ -0,0 +1,5 @@ +package appnexus.com.trackertestapp.listener + +interface RecyclerItemClickListener { + fun onItemClick (position : Int) +} \ No newline at end of file diff --git a/tests/TrackerTestApp/app/src/main/java/appnexus/com/trackertestapp/utility/Utils.kt b/tests/TrackerTestApp/app/src/main/java/appnexus/com/trackertestapp/utility/Utils.kt new file mode 100644 index 000000000..91efac10e --- /dev/null +++ b/tests/TrackerTestApp/app/src/main/java/appnexus/com/trackertestapp/utility/Utils.kt @@ -0,0 +1,60 @@ +package appnexus.com.trackertestapp.utility + +import com.appnexus.opensdk.BannerAdView +import com.appnexus.opensdk.InterstitialAdView +import com.appnexus.opensdk.MediaType +import com.appnexus.opensdk.NativeAdRequest +import com.appnexus.opensdk.instreamvideo.VideoAd +import com.appnexus.opensdk.ut.UTRequestParameters +import com.appnexus.opensdk.utils.Clog +import java.lang.reflect.InvocationTargetException + +class Utils { + + fun setForceCreativeId( + creative_id: Int, + banner: BannerAdView? = null, + interstitial: InterstitialAdView? = null, + nativeAdRequest: NativeAdRequest? = null, + video: VideoAd? = null + ) { + try { + var requestParameters: UTRequestParameters? = null + + if (video != null) { + val videoAd = VideoAd::class.java + val met = videoAd.getDeclaredMethod("getRequestParameters") + met.isAccessible = true + requestParameters = met.invoke(video) as UTRequestParameters + } else if (banner != null) { + requestParameters = banner?.getRequestParameters() + } else if (interstitial != null) { + requestParameters = interstitial?.getRequestParameters() + } else if (nativeAdRequest != null) { + requestParameters = nativeAdRequest?.getRequestParameters() + } + + Clog.e("REQ_PARAMS", requestParameters.toString()) + + val utParams = UTRequestParameters::class.java + val met = utParams.getDeclaredMethod("setForceCreativeId", Int::class.javaPrimitiveType) + met.isAccessible = true + met.invoke(requestParameters, creative_id) + + + } catch (e: ClassNotFoundException) { + e.printStackTrace() + } catch (e: IllegalAccessException) { + e.printStackTrace() + } catch (e: NoSuchMethodException) { + e.printStackTrace() + } catch (e: InvocationTargetException) { + e.printStackTrace() + } + + } + + fun method(): Int { + return 0 + } +} \ No newline at end of file diff --git a/tests/TrackerTestApp/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/tests/TrackerTestApp/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 000000000..2b068d114 --- /dev/null +++ b/tests/TrackerTestApp/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/tests/TrackerTestApp/app/src/main/res/drawable/download.png b/tests/TrackerTestApp/app/src/main/res/drawable/download.png new file mode 100644 index 000000000..26c2fca45 Binary files /dev/null and b/tests/TrackerTestApp/app/src/main/res/drawable/download.png differ diff --git a/tests/TrackerTestApp/app/src/main/res/drawable/ic_launcher_background.xml b/tests/TrackerTestApp/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 000000000..07d5da9cb --- /dev/null +++ b/tests/TrackerTestApp/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/TrackerTestApp/app/src/main/res/drawable/images.jpeg b/tests/TrackerTestApp/app/src/main/res/drawable/images.jpeg new file mode 100644 index 000000000..9ded1777c Binary files /dev/null and b/tests/TrackerTestApp/app/src/main/res/drawable/images.jpeg differ diff --git a/tests/TrackerTestApp/app/src/main/res/layout/activity_banner.xml b/tests/TrackerTestApp/app/src/main/res/layout/activity_banner.xml new file mode 100644 index 000000000..517c443ee --- /dev/null +++ b/tests/TrackerTestApp/app/src/main/res/layout/activity_banner.xml @@ -0,0 +1,11 @@ + + + + \ No newline at end of file diff --git a/tests/TrackerTestApp/app/src/main/res/layout/fragment_preview_instream.xml b/tests/TrackerTestApp/app/src/main/res/layout/fragment_preview_instream.xml new file mode 100644 index 000000000..1bfd188a2 --- /dev/null +++ b/tests/TrackerTestApp/app/src/main/res/layout/fragment_preview_instream.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/TrackerTestApp/app/src/main/res/layout/layout_ad_view.xml b/tests/TrackerTestApp/app/src/main/res/layout/layout_ad_view.xml new file mode 100644 index 000000000..87e695055 --- /dev/null +++ b/tests/TrackerTestApp/app/src/main/res/layout/layout_ad_view.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/tests/TrackerTestApp/app/src/main/res/layout/layout_native.xml b/tests/TrackerTestApp/app/src/main/res/layout/layout_native.xml new file mode 100644 index 000000000..0488c7cf5 --- /dev/null +++ b/tests/TrackerTestApp/app/src/main/res/layout/layout_native.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/TrackerTestApp/app/src/main/res/layout/layout_recycler.xml b/tests/TrackerTestApp/app/src/main/res/layout/layout_recycler.xml new file mode 100644 index 000000000..5ad4fa88d --- /dev/null +++ b/tests/TrackerTestApp/app/src/main/res/layout/layout_recycler.xml @@ -0,0 +1,15 @@ + + + + + + \ No newline at end of file diff --git a/tests/TrackerTestApp/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/tests/TrackerTestApp/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 000000000..eca70cfe5 --- /dev/null +++ b/tests/TrackerTestApp/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/tests/TrackerTestApp/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/tests/TrackerTestApp/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 000000000..eca70cfe5 --- /dev/null +++ b/tests/TrackerTestApp/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/tests/TrackerTestApp/app/src/main/res/mipmap-hdpi/ic_launcher.png b/tests/TrackerTestApp/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 000000000..a571e6009 Binary files /dev/null and b/tests/TrackerTestApp/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/tests/TrackerTestApp/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/tests/TrackerTestApp/app/src/main/res/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 000000000..61da551c5 Binary files /dev/null and b/tests/TrackerTestApp/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/tests/TrackerTestApp/app/src/main/res/mipmap-mdpi/ic_launcher.png b/tests/TrackerTestApp/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 000000000..c41dd2853 Binary files /dev/null and b/tests/TrackerTestApp/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/tests/TrackerTestApp/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/tests/TrackerTestApp/app/src/main/res/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 000000000..db5080a75 Binary files /dev/null and b/tests/TrackerTestApp/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/tests/TrackerTestApp/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/tests/TrackerTestApp/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 000000000..6dba46dab Binary files /dev/null and b/tests/TrackerTestApp/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/tests/TrackerTestApp/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/tests/TrackerTestApp/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 000000000..da31a871c Binary files /dev/null and b/tests/TrackerTestApp/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/tests/TrackerTestApp/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/tests/TrackerTestApp/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 000000000..15ac68172 Binary files /dev/null and b/tests/TrackerTestApp/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/tests/TrackerTestApp/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/tests/TrackerTestApp/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 000000000..b216f2d31 Binary files /dev/null and b/tests/TrackerTestApp/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/tests/TrackerTestApp/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/tests/TrackerTestApp/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 000000000..f25a41974 Binary files /dev/null and b/tests/TrackerTestApp/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/tests/TrackerTestApp/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/tests/TrackerTestApp/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 000000000..e96783ccc Binary files /dev/null and b/tests/TrackerTestApp/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/tests/TrackerTestApp/app/src/main/res/values/colors.xml b/tests/TrackerTestApp/app/src/main/res/values/colors.xml new file mode 100644 index 000000000..71e5723d7 --- /dev/null +++ b/tests/TrackerTestApp/app/src/main/res/values/colors.xml @@ -0,0 +1,7 @@ + + + #6200EE + #3700B3 + #03DAC5 + #000000 + \ No newline at end of file diff --git a/tests/TrackerTestApp/app/src/main/res/values/strings.xml b/tests/TrackerTestApp/app/src/main/res/values/strings.xml new file mode 100644 index 000000000..d26e15acd --- /dev/null +++ b/tests/TrackerTestApp/app/src/main/res/values/strings.xml @@ -0,0 +1,20 @@ + + TrackerTestApp + http://acdn.adnxs.com/mobile/video_test/content/Scenario.mp4 + Hello from MARActivity! + button_tag + + + Banner + Banner-LazyLoad + Interstitial + Video + Native + + + + RTB + CSM + SSM + + \ No newline at end of file diff --git a/tests/TrackerTestApp/app/src/main/res/values/styles.xml b/tests/TrackerTestApp/app/src/main/res/values/styles.xml new file mode 100644 index 000000000..fac929168 --- /dev/null +++ b/tests/TrackerTestApp/app/src/main/res/values/styles.xml @@ -0,0 +1,10 @@ + + + + + \ No newline at end of file diff --git a/tests/TrackerTestApp/app/src/main/res/xml/network_security_config.xml b/tests/TrackerTestApp/app/src/main/res/xml/network_security_config.xml new file mode 100644 index 000000000..f18e1f040 --- /dev/null +++ b/tests/TrackerTestApp/app/src/main/res/xml/network_security_config.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/tests/TrackerTestApp/build.gradle b/tests/TrackerTestApp/build.gradle new file mode 100644 index 000000000..a4bc24b9c --- /dev/null +++ b/tests/TrackerTestApp/build.gradle @@ -0,0 +1,30 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +buildscript { + ext.kotlin_version = "1.3.72" + repositories { + google() + jcenter() + maven { + url "https://plugins.gradle.org/m2/" + } + } + dependencies { + classpath "com.android.tools.build:gradle:4.0.1" + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath "gradle.plugin.com.browserstack.gradle:browserstack-gradle-plugin:3.0.1" + + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files + } +} + +allprojects { + repositories { + google() + jcenter() + } +} + +task clean(type: Delete) { + delete rootProject.buildDir +} \ No newline at end of file diff --git a/tests/TrackerTestApp/gradle.properties b/tests/TrackerTestApp/gradle.properties new file mode 100644 index 000000000..4d15d015f --- /dev/null +++ b/tests/TrackerTestApp/gradle.properties @@ -0,0 +1,21 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app"s APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Automatically convert third-party libraries to use AndroidX +android.enableJetifier=true +# Kotlin code style for this project: "official" or "obsolete": +kotlin.code.style=official \ No newline at end of file diff --git a/tests/TrackerTestApp/gradle/wrapper/gradle-wrapper.jar b/tests/TrackerTestApp/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 000000000..f6b961fd5 Binary files /dev/null and b/tests/TrackerTestApp/gradle/wrapper/gradle-wrapper.jar differ diff --git a/tests/TrackerTestApp/gradle/wrapper/gradle-wrapper.properties b/tests/TrackerTestApp/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000..c93cf0de8 --- /dev/null +++ b/tests/TrackerTestApp/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Tue Jan 12 16:14:45 IST 2021 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-all.zip diff --git a/tests/TrackerTestApp/gradlew b/tests/TrackerTestApp/gradlew new file mode 100755 index 000000000..cccdd3d51 --- /dev/null +++ b/tests/TrackerTestApp/gradlew @@ -0,0 +1,172 @@ +#!/usr/bin/env sh + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/tests/TrackerTestApp/gradlew.bat b/tests/TrackerTestApp/gradlew.bat new file mode 100644 index 000000000..e95643d6a --- /dev/null +++ b/tests/TrackerTestApp/gradlew.bat @@ -0,0 +1,84 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/tests/TrackerTestApp/runbrowserStackTest.sh b/tests/TrackerTestApp/runbrowserStackTest.sh new file mode 100644 index 000000000..6584e0902 --- /dev/null +++ b/tests/TrackerTestApp/runbrowserStackTest.sh @@ -0,0 +1,38 @@ +echo "M0bil35DK" | sudo -S chown -R `whoami` ~/.npm +echo "M0bil35DK" | sudo chown -R `whoami` /usr/local/lib/node_modules +export PATH=/usr/local/bin:$PATH +# Update homebrew recipes +curl -s http://api.open-notify.org/iss-now.json | jq .timestamp +brew install jq +# Get current working Directory +presentWorkingDirectory=$(pwd) +echo "presentWorkingDirectory==> $presentWorkingDirectory" +# Set Browser Stack userName & accessKey +userName="mobilesdkteam1" +accessKey="eAqGKNyysiKQmX1wDUQ4" +# Add devices list +devices="\"Google Pixel 3-9.0\"" +echo " devcies==>$devices" +# Build the App apk +./gradlew clean assembleDebug +# Upload App APK for browser Stack and get appurl +appurl=$(curl -u "$userName:$accessKey" -X POST "https://api-cloud.browserstack.com/app-automate/upload" -F "file=@$presentWorkingDirectory/app/build/outputs/apk/debug/app-debug.apk" | jq .app_url) +echo "appurl==> $appurl" +# Build the Test Suite apk +./gradlew clean assembleDebugAndroidTest +# Upload Test Suite APK for browser Stack and get testurl +testurl=$(curl -u "$userName:$accessKey" -X POST "https://api-cloud.browserstack.com/app-automate/espresso/test-suite" -F "file=@$presentWorkingDirectory/app/build/outputs/apk/androidTest/debug/app-debug-androidTest.apk" | jq .test_url) +echo "testurl==> $testurl" +echo "<==Execute Espresso tests==>" +# Execute Espresso tests and get buildId +buildid=$(curl -X POST "https://api-cloud.browserstack.com/app-automate/espresso/build" -d "{\"networkLogs\" : \"true\",\"devices\": [$devices], \"app\": $appurl, \"deviceLogs\" : \"true\", \"testSuite\": $testurl}" -H "Content-Type: application/json" -u "$userName:$accessKey"| jq .build_id | tr -d \") +echo "buildid==> $buildid" +# Wait for testcase result +testTrackerTestResult="running" +if [ $testTrackerTestResult == "running" ] ; then result=true; else result=false; fi +while $result; do sleep 1; testTrackerTestResult=$(curl -u "$userName:$accessKey" -X GET "https://api-cloud.browserstack.com/app-automate/espresso/builds/$buildid" | jq '.devices."Google Pixel 3-9.0".status' | tr -d \"); +if [ $testTrackerTestResult == "running" ] ; then result=true; else result=false; fi +echo "Please wait.......\n"; +sleep 60 +done +echo "Test Result Impression, OMID Tracker & Click Tracker.......$testTrackerTestResult\n"; diff --git a/tests/TrackerTestApp/settings.gradle b/tests/TrackerTestApp/settings.gradle new file mode 100644 index 000000000..524cb7ea2 --- /dev/null +++ b/tests/TrackerTestApp/settings.gradle @@ -0,0 +1,8 @@ +include ':app' + +include ':sdk' +project(':sdk').projectDir = new File(settingsDir, '../../sdk') + +include ':instreamvideo' +project(':instreamvideo').projectDir = new File(settingsDir, '../../instreamvideo') +rootProject.name = "TrackerTestApp" \ No newline at end of file