@@ -16,6 +16,8 @@
import java.util.List;
import java.util.Map;

import static com.mopub.nativeads.NativeImageHelper.preCacheImages;

public class MillennialNative extends CustomEventNative {
public static final String DCN_KEY = "dcn";
public static final String APID_KEY = "adUnitID";
@@ -81,9 +83,14 @@ public void run() {
}

try {
NativeAd n = NativeAd.createInstance(placementId, NativeAd.NATIVE_TYPE_INLINE);
final MillennialForwardingNativeAd mmForwardingNativeAd = new MillennialForwardingNativeAd(context, n, listener);
mmForwardingNativeAd.loadAd();
NativeAd nativeAd = NativeAd.createInstance(placementId, NativeAd.NATIVE_TYPE_INLINE);
final MillennialStaticNativeAd millennialStaticNativeAd =
new MillennialStaticNativeAd(context,
nativeAd,
new ImpressionTracker(context),
new NativeClickHandler(context),
listener);
millennialStaticNativeAd.loadAd();
} catch ( MMException e ) {
UI_THREAD_HANDLER.post(new Runnable() {
@Override
@@ -97,108 +104,140 @@ public void run() {
private boolean extrasAreValid(final Map<String, String> serverExtras) {
String placementId = serverExtras.get(APID_KEY);
return (serverExtras.containsKey(APID_KEY) &&
placementId != null & placementId.length() > 0 );
placementId != null && placementId.length() > 0 );
}


static class MillennialForwardingNativeAd extends BaseForwardingNativeAd implements NativeAd.NativeListener {
private Context context;
private NativeAd nativeAd;
private CustomEventNativeListener listener;
private MillennialForwardingNativeAd millennialForwardingNativeAd;

public MillennialForwardingNativeAd(final Context context, final NativeAd nativeAd, final CustomEventNativeListener customEventNativeListener) {
this.context = context.getApplicationContext();
this.nativeAd = nativeAd;
this.listener = customEventNativeListener;
this.millennialForwardingNativeAd = this;
static class MillennialStaticNativeAd extends StaticNativeAd implements NativeAd.NativeListener {
private final Context mContext;
private NativeAd mNativeAd;
private final ImpressionTracker mImpressionTracker;
private final NativeClickHandler mNativeClickHandler;
private final CustomEventNativeListener mListener;
private final MillennialStaticNativeAd mMillennialStaticNativeAd;

public MillennialStaticNativeAd(final Context context,
final NativeAd nativeAd,
final ImpressionTracker impressionTracker,
final NativeClickHandler nativeClickHandler,
final CustomEventNativeListener customEventNativeListener) {
mContext = context.getApplicationContext();
mNativeAd = nativeAd;
mImpressionTracker = impressionTracker;
mNativeClickHandler = nativeClickHandler;
mListener = customEventNativeListener;
mMillennialStaticNativeAd = this;

nativeAd.setListener(this);
}

void loadAd() {
Log.i(LOGCAT_TAG, "Loading native ad...");
try {
nativeAd.load(this.context, null);
mNativeAd.load(mContext, null);
} catch (MMException e) {
Log.w(MillennialNative.LOGCAT_TAG, "Caught configuration error Exception.");
e.printStackTrace();
UI_THREAD_HANDLER.post(new Runnable() {
@Override
public void run() {
listener.onNativeAdFailed(NativeErrorCode.NATIVE_ADAPTER_CONFIGURATION_ERROR);
mListener.onNativeAdFailed(NativeErrorCode
.NATIVE_ADAPTER_CONFIGURATION_ERROR);
}
});
}
}

// BaseForwardingNativeAd
// Lifecycle Handlers
@Override
public void destroy() {
nativeAd.setListener(null);
nativeAd = null;
public void prepare(final View view) {
// Must access these methods directly to get impressions to fire.
mNativeAd.getIconImage();
mNativeAd.getDisclaimer();
mImpressionTracker.addView(view, this);
mNativeClickHandler.setOnClickListener(view, this);
}

@Override
public void prepare(final View view) {
// Must access these methods directly to get impressions to fire.
nativeAd.getIconImage();
nativeAd.getDisclaimer();
public void clear(final View view) {
mImpressionTracker.removeView(view);
mNativeClickHandler.clearOnClickListener(view);
}

@Override
public void handleClick(final View view) {
nativeAd.fireClicked();
Log.i(LOGCAT_TAG, "Millennial native ad clicked!");
public void destroy() {
mImpressionTracker.destroy();
mNativeAd.setListener(null);
mNativeAd = null;
}

// Event Handlers
@Override
public void recordImpression() {
super.recordImpression();
public void recordImpression(final View view) {
notifyAdImpressed();
try {
nativeAd.fireImpression();
mNativeAd.fireImpression();
Log.i(LOGCAT_TAG, "Millennial native impression recorded.");
} catch ( MMException m ) {
Log.e(LOGCAT_TAG, "Millennial native impression NOT tracked: " + m.getMessage() );
}
}

// MM'S Native listener
@Override
public void handleClick(final View view) {
notifyAdClicked();
mNativeClickHandler.openClickDestinationUrl(getClickDestinationUrl(), view);
mNativeAd.fireClicked();
Log.i(LOGCAT_TAG, "Millennial native ad clicked!");
}

// MM'S Native mListener
@Override
public void onLoaded(NativeAd nativeAd) {
// Set assets
String iconImageUrl = nativeAd.getImageUrl(NativeAd.ComponentName.ICON_IMAGE, 1);
String mainImageUrl = nativeAd.getImageUrl(NativeAd.ComponentName.MAIN_IMAGE, 1);

this.setTitle(nativeAd.getTitle().getText().toString());
this.setText(nativeAd.getBody().getText().toString());
this.setCallToAction(nativeAd.getCallToActionButton().getText().toString());
this.setClickDestinationUrl(nativeAd.getCallToActionUrl());
this.setIconImageUrl(iconImageUrl);
this.setMainImageUrl(mainImageUrl);
setTitle(nativeAd.getTitle().getText().toString());
setText(nativeAd.getBody().getText().toString());
setCallToAction(nativeAd.getCallToActionButton().getText().toString());

final String clickDestinationUrl = nativeAd.getCallToActionUrl();
if (clickDestinationUrl == null) {
UI_THREAD_HANDLER.post(new Runnable() {
@Override
public void run() {
Log.d(LOGCAT_TAG,
"Millennial native encountered null destination url. Failing over.");
mListener.onNativeAdFailed(
NativeErrorCode.NATIVE_ADAPTER_CONFIGURATION_ERROR);
}
});
return;
}

setClickDestinationUrl(clickDestinationUrl);
setIconImageUrl(iconImageUrl);
setMainImageUrl(mainImageUrl);

final List<String> urls = new ArrayList<String>();
if ( iconImageUrl != null ) { urls.add(iconImageUrl); }
if ( mainImageUrl != null ) { urls.add(mainImageUrl); }

// Use MoPub's click/impression trackers
this.setOverridingImpressionTracker(false);
this.setOverridingClickTracker(false);

UI_THREAD_HANDLER.post(new Runnable() {
@Override
public void run() {
// This has to be run on the main thread:
preCacheImages(context, urls, new ImageListener() {
preCacheImages(mContext, urls, new NativeImageHelper.ImageListener() {
@Override
public void onImagesCached() {
listener.onNativeAdLoaded(millennialForwardingNativeAd);
mListener.onNativeAdLoaded(mMillennialStaticNativeAd);
Log.i(LOGCAT_TAG, "Millennial native ad loaded");
}

@Override
public void onImagesFailedToCache(NativeErrorCode errorCode) {
listener.onNativeAdFailed(errorCode);
mListener.onNativeAdFailed(errorCode);
}
});

@@ -236,7 +275,7 @@ public void onLoadFailed(NativeAd nativeAd, NativeAd.NativeErrorStatus nativeErr
UI_THREAD_HANDLER.post(new Runnable() {
@Override
public void run() {
listener.onNativeAdFailed(error);
mListener.onNativeAdFailed(error);
}
});
Log.i(LOGCAT_TAG, "Millennial native ad failed: " + nativeErrorStatus.getDescription() );
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.mopub.simpleadsdemo"
android:versionCode="30"
android:versionName="3.13.0">
android:versionCode="31"
android:versionName="4.0.0">
<uses-sdk android:minSdkVersion="9"
android:targetSdkVersion="22"/>

@@ -9,14 +9,14 @@ apply plugin: 'com.android.application'

group = 'com.mopub'
description = '''MoPub Sample App'''
version = '3.13.0'
version = '4.0.0'

android {
compileSdkVersion 22
buildToolsVersion '22.0.1'

defaultConfig {
versionCode 30
versionCode 31
versionName version
minSdkVersion 9
targetSdkVersion 22
@@ -62,13 +62,13 @@
android:paddingBottom="10dp" />

<ImageView
android:id="@+id/native_daa_icon_image"
android:id="@+id/native_privacy_information_icon_image"
android:layout_width="40dp"
android:layout_height="40dp"
android:padding="10dp"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_alignParentTop="true"
android:contentDescription="@string/native_daa_icon_image"/>
android:contentDescription="@string/native_privacy_information_icon_image"/>

</RelativeLayout>
@@ -53,6 +53,6 @@
<ListView
android:id="@+id/native_list_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_height="match_parent"
android:padding="5dp"/>
</LinearLayout>
@@ -8,7 +8,7 @@
<string name="show_ad">Show Ad</string>
<string name="native_icon_image">native_icon_image</string>
<string name="native_main_image">native_main_image</string>
<string name="native_daa_icon_image">DAA icon image</string>
<string name="native_privacy_information_icon_image">Privacy Information Icon image</string>
<string name="learn_more">Learn More</string>
<string name="save">Save Ad Unit</string>
<string name="ad_unit_id">Ad Unit Id:</string>
@@ -67,7 +67,7 @@ public void publish(final LogRecord logRecord) {
if (MoPubErrorCode.WARMUP.toString().equals(logRecord.getMessage())) {
Utils.logToast(mContext, MoPubErrorCode.WARMUP.toString());
}
// Toasts the no connection message if a native response failed due to no internet
// Toasts the no connection message if a native ad failed due to no internet
if (MoPubErrorCode.NO_CONNECTION.toString().equals(logRecord.getMessage())) {
Utils.logToast(mContext, MoPubErrorCode.NO_CONNECTION.toString());
}
@@ -13,7 +13,7 @@
import android.widget.TextView;

import com.mopub.nativeads.MoPubNativeAdLoadedListener;
import com.mopub.nativeads.MoPubNativeAdRenderer;
import com.mopub.nativeads.MoPubStaticNativeAdRenderer;
import com.mopub.nativeads.MoPubStreamAdPlacer;
import com.mopub.nativeads.RequestParameters;
import com.mopub.nativeads.ViewBinder;
@@ -69,21 +69,24 @@ public void onClick(View view) {
views.mAdUnitIdView.setText(adUnitId);
mViewPager = (ViewPager) view.findViewById(R.id.gallery_pager);

// This ad placer is used to automatically insert ads into the ViewPager.
mStreamAdPlacer = new MoPubStreamAdPlacer(getActivity());
final MoPubNativeAdRenderer adRenderer = new MoPubNativeAdRenderer(
// Set up a renderer for a static native ad.
final MoPubStaticNativeAdRenderer moPubStaticNativeAdRenderer = new MoPubStaticNativeAdRenderer(
new ViewBinder.Builder(R.layout.native_ad_list_item)
.titleId(R.id.native_title)
.textId(R.id.native_text)
.mainImageId(R.id.native_main_image)
.iconImageId(R.id.native_icon_image)
.callToActionId(R.id.native_cta)
.daaIconImageId(R.id.native_daa_icon_image)
.privacyInformationIconImageId(R.id.native_privacy_information_icon_image)
.build()
);
mPagerAdapter = new CustomPagerAdapter(getChildFragmentManager(), mStreamAdPlacer);
mStreamAdPlacer.registerAdRenderer(adRenderer);

// This ad placer is used to automatically insert ads into the ViewPager.
mStreamAdPlacer = new MoPubStreamAdPlacer(getActivity());
mStreamAdPlacer.registerAdRenderer(moPubStaticNativeAdRenderer);
mStreamAdPlacer.setAdLoadedListener(this);

mPagerAdapter = new CustomPagerAdapter(getChildFragmentManager(), mStreamAdPlacer);
mViewPager.setAdapter(mPagerAdapter);

return view;
@@ -10,12 +10,13 @@
import android.widget.ListView;

import com.mopub.nativeads.MoPubAdAdapter;
import com.mopub.nativeads.MoPubNativeAdRenderer;
import com.mopub.nativeads.MoPubStaticNativeAdRenderer;
import com.mopub.nativeads.RequestParameters;
import com.mopub.nativeads.ViewBinder;

import java.util.EnumSet;

import static com.mopub.nativeads.MoPubNativeAdPositioning.MoPubServerPositioning;
import static com.mopub.nativeads.RequestParameters.NativeAdAsset;

public class NativeListViewFragment extends Fragment {
@@ -70,22 +71,24 @@ public void onClick(View view) {

// Create an ad adapter that gets its positioning information from the MoPub Ad Server.
// This adapter will be used in place of the original adapter for the ListView.
mAdAdapter = new MoPubAdAdapter(getActivity(), adapter);
mAdAdapter = new MoPubAdAdapter(getActivity(), adapter, new MoPubServerPositioning());

// Set up an renderer that knows how to put ad data in an ad view.
final MoPubNativeAdRenderer adRenderer = new MoPubNativeAdRenderer(
// Set up a renderer that knows how to put ad data in your custom native view.
final MoPubStaticNativeAdRenderer staticAdRender = new MoPubStaticNativeAdRenderer(
new ViewBinder.Builder(R.layout.native_ad_list_item)
.titleId(R.id.native_title)
.textId(R.id.native_text)
.mainImageId(R.id.native_main_image)
.iconImageId(R.id.native_icon_image)
.callToActionId(R.id.native_cta)
.daaIconImageId(R.id.native_daa_icon_image)
.privacyInformationIconImageId(R.id.native_privacy_information_icon_image)
.build());

// Register the renderer with the MoPubAdAdapter and then set the adapter on the ListView.
mAdAdapter.registerAdRenderer(adRenderer);
mAdAdapter.registerAdRenderer(staticAdRender);
listView.setAdapter(mAdAdapter);

mAdAdapter.loadAds(mAdConfiguration.getAdUnitId(), mRequestParameters);
return view;
}

@@ -95,11 +98,4 @@ public void onDestroyView() {
mAdAdapter.destroy();
super.onDestroyView();
}

@Override
public void onResume() {
// MoPub recommends loading knew ads when the user returns to your activity.
mAdAdapter.loadAds(mAdConfiguration.getAdUnitId(), mRequestParameters);
super.onResume();
}
}
@@ -16,6 +16,7 @@

import com.mopub.nativeads.MoPubNativeAdPositioning;
import com.mopub.nativeads.MoPubRecyclerAdapter;
import com.mopub.nativeads.MoPubStaticNativeAdRenderer;
import com.mopub.nativeads.RequestParameters;
import com.mopub.nativeads.ViewBinder;

@@ -82,14 +83,19 @@ public void onClick(final View v) {

mRecyclerAdapter = new MoPubRecyclerAdapter(getActivity(), originalAdapter,
new MoPubNativeAdPositioning.MoPubServerPositioning());
mRecyclerAdapter.registerViewBinder(new ViewBinder.Builder(R.layout.native_ad_list_item)
.titleId(R.id.native_title)
.textId(R.id.native_text)
.mainImageId(R.id.native_main_image)
.iconImageId(R.id.native_icon_image)
.callToActionId(R.id.native_cta)
.daaIconImageId(R.id.native_daa_icon_image)
.build());

MoPubStaticNativeAdRenderer moPubStaticNativeAdRenderer = new MoPubStaticNativeAdRenderer(
new ViewBinder.Builder(R.layout.native_ad_list_item)
.titleId(R.id.native_title)
.textId(R.id.native_text)
.mainImageId(R.id.native_main_image)
.iconImageId(R.id.native_icon_image)
.callToActionId(R.id.native_cta)
.privacyInformationIconImageId(R.id.native_privacy_information_icon_image)
.build()
);

mRecyclerAdapter.registerAdRenderer(moPubStaticNativeAdRenderer);

mRecyclerView.setAdapter(mRecyclerAdapter);
mRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
@@ -25,6 +25,10 @@ repositories {
}
}

configurations {
javadocDeps
}

apply plugin: 'com.android.library'
apply plugin: 'com.github.dcendents.android-maven'
apply plugin: 'org.robolectric'
@@ -35,14 +39,14 @@ task wrapper(type: Wrapper) {

group = 'com.mopub'
description = '''MoPub Android SDK'''
version = '3.13.0'
version = '4.0.0'

android {
compileSdkVersion 22
buildToolsVersion '22.0.1'

defaultConfig {
versionCode 30
versionCode 31
versionName version
minSdkVersion 9
targetSdkVersion 22
@@ -102,8 +106,15 @@ dependencies {
compile 'com.android.support:support-annotations:22.0.0'
compile 'com.android.support:recyclerview-v7:22.0.0'
compile 'com.mopub.volley:mopub-volley:1.1.0'
compile 'com.google.android.exoplayer:exoplayer:r1.4.2'
// anchor: additional dependencies

javadocDeps 'com.google.android.exoplayer:exoplayer:r1.4.2:sources'
javadocDeps 'com.mopub.volley:mopub-volley:1.1.0:sources'
javadocDeps 'com.android.support:support-v4:22.0.0:sources'
javadocDeps 'com.android.support:support-annotations:22.0.0:sources'
javadocDeps 'com.android.support:recyclerview-v7:22.0.0:sources'

testCompile 'junit:junit:4.10'
testCompile 'org.robolectric:robolectric:2.4'
testCompile 'com.squareup:fest-android:1.0.7'
@@ -203,6 +214,7 @@ task sourcesJar(type: Jar) {
task javadoc(type: Javadoc) {
source = android.sourceSets.main.java.srcDirs
classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
classpath += configurations.javadocDeps
}

task javadocJar(type: Jar, dependsOn: javadoc) {
@@ -44,6 +44,13 @@
<version>1.1.0</version>
</dependency>

<dependency>
<groupId>com.google.android.exoplayer</groupId>
<artifactId>exoplayer</artifactId>
<version>r1.4.2</version>
<type>aar</type>
</dependency>

<!--android & oobolectric-->
<dependency>
<groupId>com.google.android</groupId>
@@ -8,7 +8,8 @@
public static final String HTML = "html";
public static final String MRAID = "mraid";
public static final String INTERSTITIAL = "interstitial";
public static final String NATIVE = "json";
public static final String STATIC_NATIVE = "json";
public static final String VIDEO_NATIVE = "json_video";
public static final String CUSTOM = "custom";
public static final String CLEAR = "clear";
}
@@ -8,11 +8,20 @@
public static final String HTML_RESPONSE_BODY_KEY = "Html-Response-Body";
public static final String REDIRECT_URL_KEY = "Redirect-Url";
public static final String CLICKTHROUGH_URL_KEY = "Clickthrough-Url";
public static final String CLICK_TRACKING_URL_KEY = "Click-Tracking-Url";
public static final String SCROLLABLE_KEY = "Scrollable";
public static final String CREATIVE_ORIENTATION_KEY = "com_mopub_orientation";
public static final String JSON_BODY_KEY = "com_mopub_native_json";
public static final String BROADCAST_IDENTIFIER_KEY = "broadcastIdentifier";
public static final String AD_UNIT_ID_KEY = "com_mopub_ad_unit_id";
public static final String AD_WIDTH = "com_mopub_ad_width";
public static final String AD_HEIGHT = "com_mopub_ad_height";

// Native Video fields
public static final String PLAY_VISIBLE_PERCENT = "Play-Visible-Percent";
public static final String PAUSE_VISIBLE_PERCENT = "Pause-Visible-Percent";
public static final String IMPRESSION_MIN_VISIBLE_PERCENT = "Impression-Min-Visible-Percent";
public static final String IMPRESSION_VISIBLE_MS = "Impression-Visible-Ms";
public static final String MAX_BUFFER_MS = "Max-Buffer-Ms";
public static final String EVENT_DETAILS = "Event-Details";
}
@@ -8,7 +8,7 @@
import com.mopub.mobileads.MoPubRewardedVideoManager;

public class MoPub {
public static final String SDK_VERSION = "3.13.0";
public static final String SDK_VERSION = "4.0.0";

public static enum LocationAwareness { NORMAL, TRUNCATED, DISABLED }

@@ -35,6 +35,7 @@

public class MoPubBrowser extends Activity {
public static final String DESTINATION_URL_KEY = "URL";
public static final int MOPUB_BROWSER_REQUEST_CODE = 1;
private static final int INNER_LAYOUT_ID = 1;

private WebView mWebView;
@@ -5,7 +5,6 @@

import com.mopub.common.ClientMetadata;
import com.mopub.common.Preconditions;
import com.mopub.common.VisibleForTesting;

import java.text.SimpleDateFormat;
import java.util.Date;
@@ -63,7 +62,13 @@ public int getType() {
public enum Name {
AD_REQUEST("ad_request"),
IMPRESSION_REQUEST("impression_request"),
CLICK_REQUEST("click_request");
CLICK_REQUEST("click_request"),
DOWNLOAD_START("download_start"),
DOWNLOAD_VIDEO_READY("download_video_ready"),
DOWNLOAD_BUFFERING("download_video_buffering"),
DOWNLOAD_FINISHED("download_finished"),
ERROR_DURING_PLAYBACK("error_during_playback"),
ERROR_FAILED_TO_PLAY("error_failed_to_play");

@NonNull private final String mName;
private Name(@NonNull String name) {
@@ -77,7 +82,8 @@ public String getName() {
}

public enum Category {
REQUESTS("requests");
REQUESTS("requests"),
NATIVE_VIDEO("native_video");

@NonNull private final String mCategory;
private Category(@NonNull String category) {
@@ -91,7 +97,8 @@ public String getCategory() {
}

public enum SamplingRate {
AD_REQUEST(0.1);
AD_REQUEST(0.1),
NATIVE_VIDEO(0.1);

private final double mSamplingRate;
private SamplingRate(double samplingRate) {
@@ -1,6 +1,10 @@
package com.mopub.common.event;

import android.support.annotation.NonNull;
import android.support.annotation.Nullable;

import com.mopub.common.Preconditions;
import com.mopub.common.logging.MoPubLog;

/**
* Immutable data class with client event data.
@@ -21,4 +25,50 @@ public Event build() {
return new Event(this);
}
}

/**
* Creates a BaseEvent from the specified event and the metadata surrounding the event.
*
* @param name Event name: See {@link com.mopub.common.event.BaseEvent.Name} for
* constants.
* @param category Category: See {@link com.mopub.common.event.BaseEvent.Category} for
* constants.
* @param samplingRate The percentage of events to sample. See {@link com.mopub.common.event.BaseEvent.SamplingRate}
* for constants.
* @param eventDetails Data object containing the remaining meta data around this event.
* @return An {@link BaseEvent} with all the parts combined, or {@code null} if there is no
* metadata available.
*/
@Nullable
public static BaseEvent createEventFromDetails(@NonNull final BaseEvent.Name name,
@NonNull final BaseEvent.Category category,
@NonNull final BaseEvent.SamplingRate samplingRate,
@Nullable EventDetails eventDetails) {
Preconditions.checkNotNull(name);
Preconditions.checkNotNull(category);
Preconditions.checkNotNull(samplingRate);

if (eventDetails == null) {
MoPubLog.d("Unable to log event due to no details present");
return null;
}

return new Event.Builder(name,
category,
samplingRate.getSamplingRate())
.withAdUnitId(eventDetails.getAdUnitId())
.withAdCreativeId(eventDetails.getDspCreativeId())
.withAdType(eventDetails.getAdType())
.withAdNetworkType(eventDetails.getAdNetworkType())
.withAdWidthPx(eventDetails.getAdWidthPx())
.withAdHeightPx(eventDetails.getAdHeightPx())
.withGeoLat(eventDetails.getGeoLatitude())
.withGeoLon(eventDetails.getGeoLongitude())
.withGeoAccuracy(eventDetails.getGeoAccuracy())
.withPerformanceDurationMs(eventDetails.getPerformanceDurationMs())
.withRequestId(eventDetails.getRequestId())
.withRequestStatusCode(eventDetails.getRequestStatusCode())
.withRequestUri(eventDetails.getRequestUri())
.build();
}
}
@@ -0,0 +1,256 @@
package com.mopub.common.event;

import android.support.annotation.NonNull;
import android.support.annotation.Nullable;

import com.mopub.common.Preconditions;
import com.mopub.common.util.Json;

import java.util.HashMap;
import java.util.Map;

public class EventDetails {

public static class Builder {
@NonNull private final Map<String, String> eventDetailsMap;

public Builder() {
eventDetailsMap = new HashMap<String, String>();
}

@NonNull
public Builder adUnitId(@Nullable final String adUnitId) {
if (adUnitId != null) {
eventDetailsMap.put(AD_UNIT_ID_KEY, adUnitId);
}
return this;
}

@NonNull
public Builder dspCreativeId(@Nullable final String dspCreativeId) {
if (dspCreativeId != null) {
eventDetailsMap.put(DSP_CREATIVE_ID_KEY, dspCreativeId);
}
return this;
}

@NonNull
public Builder adType(@Nullable final String adType) {
if (adType != null) {
eventDetailsMap.put(AD_TYPE_KEY, adType);
}
return this;
}

@NonNull
public Builder adNetworkType(@Nullable final String adNetworkType) {
if (adNetworkType != null) {
eventDetailsMap.put(AD_NETWORK_TYPE_KEY, adNetworkType);
}
return this;
}

@NonNull
public Builder adWidthPx(@Nullable final Integer adWidthPx) {
if (adWidthPx != null) {
eventDetailsMap.put(AD_WIDTH_PX_KEY, String.valueOf(adWidthPx));
}
return this;
}

@NonNull
public Builder adHeightPx(@Nullable final Integer adHeightPx) {
if (adHeightPx != null) {
eventDetailsMap.put(AD_HEIGHT_PX_KEY, String.valueOf(adHeightPx));
}
return this;
}

@NonNull
public Builder geoLatitude(@Nullable final Double geoLatitude) {
if (geoLatitude != null) {
eventDetailsMap.put(GEO_LATITUDE_KEY, String.valueOf(geoLatitude));
}
return this;
}

@NonNull
public Builder geoLongitude(@Nullable final Double geoLongitude) {
if (geoLongitude != null) {
eventDetailsMap.put(GEO_LONGITUDE_KEY, String.valueOf(geoLongitude));
}
return this;
}

@NonNull
public Builder geoAccuracy(@Nullable final Float geoAccuracy) {
if (geoAccuracy != null) {
eventDetailsMap.put(GEO_ACCURACY_KEY, String.valueOf((double) geoAccuracy));
}
return this;
}

@NonNull
public Builder performanceDurationMs(@Nullable final Long performanceDurationMs) {
if (performanceDurationMs != null) {
eventDetailsMap.put(PERFORMANCE_DURATION_MS_KEY,
String.valueOf((double) performanceDurationMs));
}
return this;
}

@NonNull
public Builder requestId(@Nullable final String requestId) {
if (requestId != null) {
eventDetailsMap.put(REQUEST_ID_KEY, requestId);
}
return this;
}

@NonNull
public Builder requestStatusCode(@Nullable final Integer requestStatusCode) {
if (requestStatusCode != null) {
eventDetailsMap.put(REQUEST_STATUS_CODE_KEY, String.valueOf(requestStatusCode));
}
return this;
}

@NonNull
public Builder requestUri(@Nullable final String requestUri) {
if (requestUri != null) {
eventDetailsMap.put(REQUEST_URI_KEY, requestUri);
}
return this;
}

@NonNull
public EventDetails build() {
return new EventDetails(eventDetailsMap);
}
}


private static final String AD_UNIT_ID_KEY = "ad_unit_id";
private static final String DSP_CREATIVE_ID_KEY = "dsp_creative_id";
private static final String AD_TYPE_KEY = "ad_type";
private static final String AD_NETWORK_TYPE_KEY = "ad_network_type";
private static final String AD_WIDTH_PX_KEY = "ad_width_px";
private static final String AD_HEIGHT_PX_KEY = "ad_height_px_key";
private static final String GEO_LATITUDE_KEY = "geo_latitude";
private static final String GEO_LONGITUDE_KEY = "geo_longitude";
private static final String GEO_ACCURACY_KEY = "geo_accuracy_key";
private static final String PERFORMANCE_DURATION_MS_KEY = "performance_duration_ms";
private static final String REQUEST_ID_KEY = "request_id_key";
private static final String REQUEST_STATUS_CODE_KEY = "request_status_code";
private static final String REQUEST_URI_KEY = "request_uri_key";

@NonNull private final Map<String, String> mEventDetailsMap;

private EventDetails(@NonNull final Map<String, String> eventDetailsMap) {
Preconditions.checkNotNull(eventDetailsMap);
mEventDetailsMap = eventDetailsMap;
}

@Nullable
public String getAdUnitId() {
return mEventDetailsMap.get(AD_UNIT_ID_KEY);
}

@Nullable
public String getDspCreativeId() {
return mEventDetailsMap.get(DSP_CREATIVE_ID_KEY);
}

@Nullable
public String getAdType() {
return mEventDetailsMap.get(AD_TYPE_KEY);
}

@Nullable
public String getAdNetworkType() {
return mEventDetailsMap.get(AD_NETWORK_TYPE_KEY);
}

@Nullable
public Double getAdWidthPx() {
return getNullableDoubleValue(mEventDetailsMap, AD_WIDTH_PX_KEY);
}

@Nullable
public Double getAdHeightPx() {
return getNullableDoubleValue(mEventDetailsMap, AD_HEIGHT_PX_KEY);

}

@Nullable
public Double getGeoLatitude() {
return getNullableDoubleValue(mEventDetailsMap, GEO_LATITUDE_KEY);
}

@Nullable
public Double getGeoLongitude() {
return getNullableDoubleValue(mEventDetailsMap, GEO_LONGITUDE_KEY);
}

@Nullable
public Double getGeoAccuracy() {
return getNullableDoubleValue(mEventDetailsMap, GEO_ACCURACY_KEY);
}

@Nullable
public Double getPerformanceDurationMs() {
return getNullableDoubleValue(mEventDetailsMap, PERFORMANCE_DURATION_MS_KEY);
}

@Nullable
public String getRequestId() {
return mEventDetailsMap.get(REQUEST_ID_KEY);
}

@Nullable
public Integer getRequestStatusCode() {
return getNullableIntegerValue(mEventDetailsMap, REQUEST_STATUS_CODE_KEY);
}

@Nullable
public String getRequestUri() {
return mEventDetailsMap.get(REQUEST_URI_KEY);
}

public String toJsonString() {
return Json.mapToJsonString(mEventDetailsMap);
}

@Override
public String toString() {
return toJsonString();
}

@Nullable
private static Double getNullableDoubleValue(@NonNull final Map<String, String> map,
@NonNull final String key) {
final String value = map.get(key);
if (value == null) {
return null;
}
try {
return Double.parseDouble(value);
} catch (NumberFormatException e) {
return null;
}
}

@Nullable
private static Integer getNullableIntegerValue(@NonNull final Map<String, String> map,
@NonNull final String key) {
final String value = map.get(key);
if (value == null) {
return null;
}
try {
return Integer.parseInt(value);
} catch (NumberFormatException e) {
return null;
}
}
}
@@ -5,14 +5,24 @@
import com.mopub.common.Preconditions;
import com.mopub.common.VisibleForTesting;

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Random;

/**
* Samples events based on rules defined in the sample method.
*/
public class EventSampler {

@VisibleForTesting static final int MAX_SIZE = 100;
private static final float LOAD_FACTOR = 0.75f;
/**
* The capacity is just large enough to hold the max size without rehashing.
*/
private static final int CAPACITY = (int) (MAX_SIZE / LOAD_FACTOR + 2);

@NonNull private Random mRandom;
@NonNull private LinkedHashMap<String, Boolean> mSampleDecisionsCache;

public EventSampler() {
this(new Random());
@@ -21,19 +31,41 @@ public EventSampler() {
@VisibleForTesting
public EventSampler(@NonNull Random random) {
mRandom = random;
mSampleDecisionsCache = new LinkedHashMap<String, Boolean>(CAPACITY, LOAD_FACTOR, true) {
@Override
protected boolean removeEldestEntry(Map.Entry<String, Boolean> eldest) {
return size() > MAX_SIZE;
}
};
}

/**
* Samples events based on custom rules.
* Samples events based on custom rules. Events with the same request ID will either all pass or
* be discarded together.
*
* @param baseEvent The event to be sampled.
*
* @return Will return {@code true} if the event passed sampling and {@code false}
* if it is to be discarded.
* @return Will return {@code true} if the event passed sampling and {@code false} if it is to
* be discarded.
*/
boolean sample(@NonNull BaseEvent baseEvent) {
Preconditions.checkNotNull(baseEvent);

return mRandom.nextDouble() < baseEvent.getSamplingRate();
final String requestId = baseEvent.getRequestId();
if (requestId == null) {
return mRandom.nextDouble() < baseEvent.getSamplingRate();
}

final Boolean existingSample = mSampleDecisionsCache.get(requestId);
if (existingSample != null) {
return existingSample;
}
final boolean newSample = mRandom.nextDouble() < baseEvent.getSamplingRate();
mSampleDecisionsCache.put(requestId, newSample);
return newSample;
}

@VisibleForTesting
int getCacheSize() {
return mSampleDecisionsCache.size();
}
}

Large diffs are not rendered by default.

Oops, something went wrong.
@@ -25,6 +25,13 @@
USER_AGENT("User-Agent"),
ACCEPT_LANGUAGE("Accept-Language"),

// Native Video fields
PLAY_VISIBLE_PERCENT("X-Play-Visible-Percent"),
PAUSE_VISIBLE_PERCENT("X-Pause-Visible-Percent"),
IMPRESSION_MIN_VISIBLE_PERCENT("X-Impression-Min-Visible-Percent"),
IMPRESSION_VISIBLE_MS("X-Impression-Visible-Ms"),
MAX_BUFFER_MS("X-Max-Buffer-Ms"),

@Deprecated CUSTOM_SELECTOR("X-Customselector");

private final String key;
@@ -26,6 +26,7 @@
HTML_INTERSTITIAL("html_interstitial", "com.mopub.mobileads.HtmlInterstitial"),
VAST_VIDEO_INTERSTITIAL("vast_interstitial", "com.mopub.mobileads.VastVideoInterstitial"),
MOPUB_NATIVE("mopub_native", "com.mopub.nativeads.MoPubCustomEventNative"),
MOPUB_VIDEO_NATIVE("mopub_video_native", "com.mopub.nativeads.MoPubCustomEventVideoNative"),

UNSPECIFIED("", null);

@@ -67,8 +68,10 @@ public static String getCustomEventName(@NonNull AdFormat adFormat,
@NonNull Map<String, String> headers) {
if (AdType.CUSTOM.equalsIgnoreCase(adType)) {
return extractHeader(headers, ResponseHeader.CUSTOM_EVENT_NAME);
} else if (AdType.NATIVE.equalsIgnoreCase(adType)){
} else if (AdType.STATIC_NATIVE.equalsIgnoreCase(adType)){
return CustomEventType.MOPUB_NATIVE.toString();
} else if (AdType.VIDEO_NATIVE.equalsIgnoreCase(adType)){
return CustomEventType.MOPUB_VIDEO_NATIVE.toString();
} else if (AdType.HTML.equalsIgnoreCase(adType) || AdType.MRAID.equalsIgnoreCase(adType)) {
return (AdFormat.INTERSTITIAL.equals(adFormat)
? CustomEventType.fromString(adType + INTERSTITIAL_SUFFIX)
@@ -270,11 +270,6 @@ void loadFailUrl(MoPubErrorCode errorCode) {
}
}

@Deprecated
void setFailUrl(String failUrl) {
// Does nothing.
}

void setNotLoading() {
this.mIsLoading = false;
if (mActiveRequest != null) {
@@ -333,21 +328,6 @@ public int getAdHeight() {
return 0;
}

@Deprecated
public String getClickTrackingUrl() {
return mAdResponse == null ? null : mAdResponse.getClickTrackingUrl();
}

@Deprecated
public String getRedirectUrl() {
return mAdResponse == null ? null : mAdResponse.getRedirectUrl();
}

@Deprecated
public String getResponseString() {
return mAdResponse == null ? null : mAdResponse.getStringBody();
}

public boolean getAutorefreshEnabled() {
return mAutoRefreshEnabled;
}
@@ -397,11 +377,6 @@ public void setTesting(boolean enabled) {
mIsTesting = enabled;
}

@Deprecated
Object getAdConfiguration() {
return null;
}

boolean isDestroyed() {
return mIsDestroyed;
}
@@ -587,40 +562,4 @@ Integer getRefreshTimeMillis() {
void setRefreshTimeMillis(@Nullable final Integer refreshTimeMillis) {
mRefreshTimeMillis = refreshTimeMillis;
}

@Deprecated
public void customEventDidLoadAd() {
setNotLoading();
trackImpression();
scheduleRefreshTimerIfEnabled();
}

@Deprecated
public void customEventDidFailToLoadAd() {
loadFailUrl(MoPubErrorCode.UNSPECIFIED);
}

@Deprecated
public void customEventActionWillBegin() {
registerClick();
}

@Deprecated
public void setClickthroughUrl(String clickthroughUrl) {
// Does nothing
}

/**
* @deprecated As of release 2.4
*/
@Deprecated
public boolean isFacebookSupported() {
return false;
}

/**
* @deprecated As of release 2.4
*/
@Deprecated
public void setFacebookSupported(boolean enabled) {}
}
@@ -7,13 +7,15 @@
import android.media.AudioManager;

import com.mopub.common.logging.MoPubLog;
import com.mopub.nativeads.NativeVideoController;
import com.mopub.nativeads.NativeVideoViewController;

import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static com.mopub.common.DataKeys.BROADCAST_IDENTIFIER_KEY;
import static com.mopub.mobileads.VastVideoViewController.VAST_VIDEO_CONFIG;

public class BaseVideoPlayerActivity extends Activity {
static final String VIDEO_CLASS_EXTRAS_KEY = "video_view_class_name";
public static final String VIDEO_CLASS_EXTRAS_KEY = "video_view_class_name";
public static final String VIDEO_URL = "video_url";

public static void startMraid(final Context context, final String videoUrl) {
@@ -37,7 +39,8 @@ static Intent createIntentMraid(final Context context,
static void startVast(final Context context,
final VastVideoConfig vastVideoConfig,
final long broadcastIdentifier) {
final Intent intentVideoPlayerActivity = createIntentVast(context, vastVideoConfig, broadcastIdentifier);
final Intent intentVideoPlayerActivity = createIntentVast(context, vastVideoConfig,
broadcastIdentifier);
try {
context.startActivity(intentVideoPlayerActivity);
} catch (ActivityNotFoundException e) {
@@ -56,6 +59,24 @@ static Intent createIntentVast(final Context context,
return intentVideoPlayerActivity;
}

public static void startNativeVideo(final Context context, final long nativeVideoId, final VastVideoConfig vastVideoConfig) {
final Intent intentVideoPlayerActivity = createIntentNativeVideo(context, nativeVideoId, vastVideoConfig);
try {
context.startActivity(intentVideoPlayerActivity);
} catch (ActivityNotFoundException e) {
MoPubLog.d("Activity MraidVideoPlayerActivity not found. Did you declare it in your AndroidManifest.xml?");
}
}

public static Intent createIntentNativeVideo(final Context context, final long nativeVideoId, final VastVideoConfig vastVideoConfig) {
final Intent intentVideoPlayerActivity = new Intent(context, MraidVideoPlayerActivity.class);
intentVideoPlayerActivity.setFlags(FLAG_ACTIVITY_NEW_TASK);
intentVideoPlayerActivity.putExtra(VIDEO_CLASS_EXTRAS_KEY, "native");
intentVideoPlayerActivity.putExtra(NativeVideoViewController.NATIVE_VIDEO_ID, nativeVideoId);
intentVideoPlayerActivity.putExtra(NativeVideoViewController.NATIVE_VAST_VIDEO_CONFIG, vastVideoConfig);
return intentVideoPlayerActivity;
}

@Override
protected void onDestroy() {
super.onDestroy();
@@ -32,7 +32,7 @@ void onStartActivityForResult(final Class<? extends Activity> clazz,
}

protected BaseVideoViewController(final Context context, @Nullable final Long broadcastIdentifier, final BaseVideoViewControllerListener baseVideoViewControllerListener) {
mContext = context.getApplicationContext();
mContext = context;
mBroadcastIdentifier = broadcastIdentifier;
mBaseVideoViewControllerListener = baseVideoViewControllerListener;
mLayout = new RelativeLayout(mContext);
@@ -51,7 +51,8 @@ protected void onCreate() {
protected abstract void onResume();
protected abstract void onDestroy();
protected abstract void onSaveInstanceState(@NonNull Bundle outState);
protected abstract void onConfigurationChanged(@Nullable Configuration configuration);
protected abstract void onConfigurationChanged(Configuration configuration);
protected abstract void onBackPressed();

public boolean backButtonEnabled() {
return true;
@@ -7,6 +7,7 @@

import com.mopub.common.AdFormat;
import com.mopub.common.MoPub;
import com.mopub.common.VisibleForTesting;
import com.mopub.common.logging.MoPubLog;
import com.mopub.mobileads.factories.CustomEventInterstitialAdapterFactory;

@@ -42,14 +43,6 @@ boolean isReady() {
public void onInterstitialDismissed(MoPubInterstitial interstitial);
}

private MoPubInterstitialListener mListener;

@Deprecated
public interface MoPubInterstitialListener {
public void OnInterstitialLoaded();
public void OnInterstitialFailed();
}

public MoPubInterstitial(Activity activity, String id) {
mActivity = activity;
mAdUnitId = id;
@@ -177,8 +170,6 @@ public void onCustomEventInterstitialLoaded() {

if (mInterstitialAdListener != null) {
mInterstitialAdListener.onInterstitialLoaded(this);
} else if (mListener != null) {
mListener.OnInterstitialLoaded();
}
}

@@ -223,26 +214,6 @@ public void onCustomEventInterstitialDismissed() {
}
}

@Deprecated
public void setLocationAwareness(LocationAwareness locationAwareness) {
MoPub.setLocationAwareness(locationAwareness.getNewLocationAwareness());
}

@Deprecated
public LocationAwareness getLocationAwareness() {
return LocationAwareness.fromMoPubLocationAwareness(MoPub.getLocationAwareness());
}

@Deprecated
public void setLocationPrecision(int precision) {
MoPub.setLocationPrecision(precision);
}

@Deprecated
public int getLocationPrecision() {
return MoPub.getLocationPrecision();
}

////////////////////////////////////////////////////////////////////////////////////////////////

public class MoPubInterstitialView extends MoPubView {
@@ -298,47 +269,9 @@ protected void adFailed(MoPubErrorCode errorCode) {
}
}

@Deprecated // for testing
@VisibleForTesting
@Deprecated
void setInterstitialView(MoPubInterstitialView interstitialView) {
mInterstitialView = interstitialView;
}

@Deprecated
public void setListener(MoPubInterstitialListener listener) {
mListener = listener;
}

@Deprecated
public MoPubInterstitialListener getListener() {
return mListener;
}

@Deprecated
public void customEventDidLoadAd() {
if (mInterstitialView != null) mInterstitialView.trackImpression();
}

@Deprecated
public void customEventDidFailToLoadAd() {
if (mInterstitialView != null) mInterstitialView.loadFailUrl(MoPubErrorCode.UNSPECIFIED);
}

@Deprecated
public void customEventActionWillBegin() {
if (mInterstitialView != null) mInterstitialView.registerClick();
}

/**
* @deprecated As of release 2.4
*/
@Deprecated
public void setFacebookSupported(boolean enabled) {}

/**
* @deprecated As of release 2.4
*/
@Deprecated
public boolean isFacebookSupported() {
return false;
}
}
@@ -14,7 +14,6 @@
import android.widget.FrameLayout;

import com.mopub.common.AdFormat;
import com.mopub.common.MoPub;
import com.mopub.common.logging.MoPubLog;
import com.mopub.common.util.ManifestUtils;
import com.mopub.common.util.Visibility;
@@ -24,7 +23,6 @@
import java.util.Map;
import java.util.TreeMap;

import static com.mopub.common.LocationService.LocationAwareness;
import static com.mopub.mobileads.MoPubErrorCode.ADAPTER_NOT_FOUND;

public class MoPubView extends FrameLayout {
@@ -36,7 +34,6 @@
public void onBannerCollapsed(MoPubView banner);
}

public static final int DEFAULT_LOCATION_PRECISION = 6;
@Nullable
protected AdViewController mAdViewController;
protected CustomEventBannerAdapter mCustomEventBannerAdapter;
@@ -46,13 +43,6 @@
private BroadcastReceiver mScreenStateReceiver;

private BannerAdListener mBannerAdListener;

private OnAdWillLoadListener mOnAdWillLoadListener;
private OnAdLoadedListener mOnAdLoadedListener;
private OnAdFailedListener mOnAdFailedListener;
private OnAdPresentedOverlayListener mOnAdPresentedOverlayListener;
private OnAdClosedListener mOnAdClosedListener;
private OnAdClickedListener mOnAdClickedListener;

public MoPubView(Context context) {
this(context, null);
@@ -213,40 +203,30 @@ protected void adLoaded() {

if (mBannerAdListener != null) {
mBannerAdListener.onBannerLoaded(this);
} else if (mOnAdLoadedListener != null) {
mOnAdLoadedListener.OnAdLoaded(this);
}
}

protected void adFailed(MoPubErrorCode errorCode) {
if (mBannerAdListener != null) {
mBannerAdListener.onBannerFailed(this, errorCode);
} else if (mOnAdFailedListener != null) {
mOnAdFailedListener.OnAdFailed(this);
}
}

protected void adPresentedOverlay() {
if (mBannerAdListener != null) {
mBannerAdListener.onBannerExpanded(this);
} else if (mOnAdPresentedOverlayListener != null) {
mOnAdPresentedOverlayListener.OnAdPresentedOverlay(this);
}
}

protected void adClosed() {
if (mBannerAdListener != null) {
mBannerAdListener.onBannerCollapsed(this);
} else if (mOnAdClosedListener != null) {
mOnAdClosedListener.OnAdClosed(this);
}
}

protected void adClicked() {
if (mBannerAdListener != null) {
mBannerAdListener.onBannerClicked(this);
} else if (mOnAdClickedListener != null) {
mOnAdClickedListener.OnAdClicked(this);
}
}

@@ -293,24 +273,6 @@ public int getAdHeight() {
return (mAdViewController != null) ? mAdViewController.getAdHeight() : 0;
}

public String getResponseString() {
return (mAdViewController != null) ? mAdViewController.getResponseString() : null;
}

@Deprecated
public void setClickthroughUrl(String url) {
// Does nothing.
}

public String getClickTrackingUrl() {
return (mAdViewController != null) ? mAdViewController.getClickTrackingUrl() : null;
}

@Deprecated
public String getClickthroughUrl() {
return getClickTrackingUrl();
}

public Activity getActivity() {
return (Activity) mContext;
}
@@ -384,117 +346,12 @@ public AdFormat getAdFormat() {
}

@Deprecated
public void setLocationAwareness(LocationAwareness locationAwareness) {
MoPub.setLocationAwareness(locationAwareness.getNewLocationAwareness());
}

@Deprecated
public LocationAwareness getLocationAwareness() {
return LocationAwareness.fromMoPubLocationAwareness(MoPub.getLocationAwareness());
}

@Deprecated
public void setLocationPrecision(int precision) {
MoPub.setLocationPrecision(precision);
}

@Deprecated
public int getLocationPrecision() {
return MoPub.getLocationPrecision();
}

@Deprecated
public interface OnAdWillLoadListener {
public void OnAdWillLoad(MoPubView m, String url);
}

@Deprecated
public interface OnAdLoadedListener {
public void OnAdLoaded(MoPubView m);
}

@Deprecated
public interface OnAdFailedListener {
public void OnAdFailed(MoPubView m);
}

@Deprecated
public interface OnAdClosedListener {
public void OnAdClosed(MoPubView m);
}

@Deprecated
public interface OnAdClickedListener {
public void OnAdClicked(MoPubView m);
}

@Deprecated
public interface OnAdPresentedOverlayListener {
public void OnAdPresentedOverlay(MoPubView m);
}

@Deprecated
public void setOnAdWillLoadListener(OnAdWillLoadListener listener) {
mOnAdWillLoadListener = listener;
}

@Deprecated
public void setOnAdLoadedListener(OnAdLoadedListener listener) {
mOnAdLoadedListener = listener;
}

@Deprecated
public void setOnAdFailedListener(OnAdFailedListener listener) {
mOnAdFailedListener = listener;
}

@Deprecated
public void setOnAdPresentedOverlayListener(OnAdPresentedOverlayListener listener) {
mOnAdPresentedOverlayListener = listener;
}

@Deprecated
public void setOnAdClosedListener(OnAdClosedListener listener) {
mOnAdClosedListener = listener;
}

@Deprecated
public void setOnAdClickedListener(OnAdClickedListener listener) {
mOnAdClickedListener = listener;
}

@Deprecated
protected void adWillLoad(String url) {
MoPubLog.d("adWillLoad: " + url);
if (mOnAdWillLoadListener != null) mOnAdWillLoadListener.OnAdWillLoad(this, url);
}

@Deprecated
public void customEventDidLoadAd() {
if (mAdViewController != null) mAdViewController.customEventDidLoadAd();
}

@Deprecated
public void customEventDidFailToLoadAd() {
if (mAdViewController != null) mAdViewController.customEventDidFailToLoadAd();
}

@Deprecated
public void customEventActionWillBegin() {
if (mAdViewController != null) mAdViewController.customEventActionWillBegin();
public String getResponseString() {
return null;
}

/**
* @deprecated As of release 2.4
*/
@Deprecated
public void setFacebookSupported(boolean enabled) {}

/**
* @deprecated As of release 2.4
*/
@Deprecated
public boolean isFacebookSupported() {
return false;
public String getClickTrackingUrl() {
return null;
}
}
@@ -14,6 +14,7 @@
import com.mopub.common.logging.MoPubLog;
import com.mopub.common.util.Intents;
import com.mopub.mraid.MraidVideoViewController;
import com.mopub.nativeads.NativeVideoViewController;

import static com.mopub.common.DataKeys.BROADCAST_IDENTIFIER_KEY;
import static com.mopub.mobileads.EventForwardingBroadcastReceiver.ACTION_INTERSTITIAL_FAIL;
@@ -79,7 +80,7 @@ protected void onSaveInstanceState(@NonNull Bundle outState) {
}

@Override
public void onConfigurationChanged(@Nullable Configuration newConfig) {
public void onConfigurationChanged(final Configuration newConfig) {
super.onConfigurationChanged(newConfig);
if (mBaseVideoController != null) {
mBaseVideoController.onConfigurationChanged(newConfig);
@@ -90,6 +91,7 @@ public void onConfigurationChanged(@Nullable Configuration newConfig) {
public void onBackPressed() {
if (mBaseVideoController != null && mBaseVideoController.backButtonEnabled()) {
super.onBackPressed();
mBaseVideoController.onBackPressed();
}
}

@@ -107,6 +109,8 @@ private BaseVideoViewController createVideoViewController(Bundle savedInstanceSt
return new VastVideoViewController(this, getIntent().getExtras(), savedInstanceState, mBroadcastIdentifier, this);
} else if ("mraid".equals(clazz)) {
return new MraidVideoViewController(this, getIntent().getExtras(), savedInstanceState, this);
} else if ("native".equals(clazz)) {
return new NativeVideoViewController(this, getIntent().getExtras(), savedInstanceState, this);
} else {
throw new IllegalStateException("Unsupported video type: " + clazz);
}
@@ -13,9 +13,9 @@
public abstract class RepeatingHandlerRunnable implements Runnable {
@NonNull protected final Handler mHandler;
private volatile boolean mIsRunning;
private volatile long mUpdateIntervalMillis;
protected volatile long mUpdateIntervalMillis;

RepeatingHandlerRunnable(@NonNull final Handler handler) {
public RepeatingHandlerRunnable(@NonNull final Handler handler) {
Preconditions.checkNotNull(handler);
mHandler = handler;
}
@@ -1,6 +1,7 @@
package com.mopub.mobileads;

import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.TextUtils;

import com.mopub.common.Preconditions;
@@ -21,6 +22,7 @@
private static final String CREATIVE = "Creative";
private static final String COMPANION_ADS = "CompanionAds";
private static final String ERROR = "Error";
private static final String EXTENSIONS = "Extensions";

@NonNull protected final Node mNode;

@@ -140,4 +142,20 @@

return companionAdXmlManagers;
}

/**
* If there is an Extensions section with at least one Extension, return its XML manager.
*
* @return The {@link VastExtensionParentXmlManager} or null if there are no Extensions or
* Extension child nodes.
*/
@Nullable
VastExtensionParentXmlManager getVastExtensionParentXmlManager() {
Node vastExtensionsNode = XmlUtils.getFirstMatchingChildNode(mNode, EXTENSIONS);
if (vastExtensionsNode == null) {
return null;
}

return new VastExtensionParentXmlManager(vastExtensionsNode);
}
}
@@ -0,0 +1,51 @@
package com.mopub.mobileads;

import android.support.annotation.NonNull;

import com.mopub.common.Preconditions;
import com.mopub.mobileads.util.XmlUtils;

import org.w3c.dom.Node;

import java.util.ArrayList;
import java.util.List;

/**
* This XML manager handles Extensions nodes, which may in turn contain Extension nodes.
*/
public class VastExtensionParentXmlManager {

private static final String EXTENSION = "Extension";
@NonNull private final Node mVastExtensionParentNode;

VastExtensionParentXmlManager(@NonNull Node vastExtensionParentNode) {
Preconditions.checkNotNull(vastExtensionParentNode);

mVastExtensionParentNode = vastExtensionParentNode;
}

/**
* If there are Extension sections, return their XML managers.
*
* @return The {@link VastExtensionXmlManager}s or an empty list if there are no Extension
* nodes.
*/
@NonNull
List<VastExtensionXmlManager> getVastExtensionXmlManagers() {
final List<VastExtensionXmlManager> vastExtensionXmlManagers = new
ArrayList<VastExtensionXmlManager>();

final List<Node> vastExtensionNodes =
XmlUtils.getMatchingChildNodes(mVastExtensionParentNode, EXTENSION);
if (vastExtensionNodes == null) {
return vastExtensionXmlManagers;
}

for (Node vastExtensionNode : vastExtensionNodes) {
vastExtensionXmlManagers.add(new VastExtensionXmlManager(vastExtensionNode));
}

return vastExtensionXmlManagers;
}

}
@@ -0,0 +1,69 @@
package com.mopub.mobileads;

import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.TextUtils;

import com.mopub.common.Preconditions;
import com.mopub.mobileads.util.XmlUtils;

import org.w3c.dom.Node;

/**
* This XML manager handles Extension nodes.
*/
public class VastExtensionXmlManager {
// Elements
public static final String VIDEO_VIEWABILITY_TRACKER = "MoPubViewabilityTracker";

// Attributes
public static final String TYPE = "type";

private final Node mExtensionNode;

public VastExtensionXmlManager(@NonNull Node extensionNode) {
Preconditions.checkNotNull(extensionNode);

this.mExtensionNode = extensionNode;
}

/**
* If there is an Extension node with a MoPubViewabilityTracker element, return its data object.
*
* @return The {@link VideoViewabilityTracker} parsed from the given node or null if missing or
* invalid.
*/
@Nullable
VideoViewabilityTracker getVideoViewabilityTracker() {
Node videoViewabilityTrackerNode =
XmlUtils.getFirstMatchingChildNode(mExtensionNode, VIDEO_VIEWABILITY_TRACKER);
if (videoViewabilityTrackerNode == null) {
return null;
}

VideoViewabilityTrackerXmlManager videoViewabilityTrackerXmlManager =
new VideoViewabilityTrackerXmlManager(videoViewabilityTrackerNode);
Integer viewablePlaytime = videoViewabilityTrackerXmlManager.getViewablePlaytimeMS();
Integer percentViewable = videoViewabilityTrackerXmlManager.getPercentViewable();
String videoViewabilityTrackerUrl =
videoViewabilityTrackerXmlManager.getVideoViewabilityTrackerUrl();

if (viewablePlaytime == null || percentViewable == null
|| TextUtils.isEmpty(videoViewabilityTrackerUrl)) {
return null;
}

return new VideoViewabilityTracker(viewablePlaytime, percentViewable,
videoViewabilityTrackerUrl);
}

/**
* If the node has a "type" attribute, return its value.
*
* @return A String with the value of the "type" attribute or null if missing.
*/
@Nullable
String getType() {
return XmlUtils.getAttributeValue(mExtensionNode, TYPE);
}
}
@@ -39,8 +39,11 @@
private double mScreenAspectRatio;
private int mScreenAreaDp;

public VastManager(@NonNull final Context context) {
private final boolean mShouldPreCacheVideo;

public VastManager(@NonNull final Context context, boolean shouldPreCacheVideo) {
initializeScreenDimensions(context);
mShouldPreCacheVideo = shouldPreCacheVideo;
}

/**
@@ -55,6 +58,7 @@ public void prepareVastVideoConfiguration(@Nullable final String vastXml,
@NonNull final Context context) {
Preconditions.checkNotNull(vastManagerListener, "vastManagerListener cannot be null");
Preconditions.checkNotNull(context, "context cannot be null");

if (mVastXmlManagerAggregator == null) {
mVastManagerListener = vastManagerListener;
mVastXmlManagerAggregator = new VastXmlManagerAggregator(this, mScreenAspectRatio,
@@ -92,7 +96,8 @@ public void onAggregationComplete(@Nullable final VastVideoConfig vastVideoConfi
return;
}

if (updateDiskMediaFileUrl(vastVideoConfig)) {
// Return immediately if we already have a cached video or if video precache is not required.
if (!mShouldPreCacheVideo || updateDiskMediaFileUrl(vastVideoConfig)) {
mVastManagerListener.onVastVideoConfigurationPrepared(vastVideoConfig);
return;
}
@@ -18,6 +18,7 @@
import com.mopub.common.util.DeviceUtils;
import com.mopub.common.util.Intents;
import com.mopub.common.util.Strings;
import com.mopub.exceptions.IntentNotResolvableException;

import java.io.Serializable;
import java.util.ArrayList;
@@ -52,6 +53,7 @@
@Nullable private String mCustomSkipText;
@Nullable private String mCustomCloseIconUrl;
@NonNull private DeviceUtils.ForceOrientation mCustomForceOrientation = DeviceUtils.ForceOrientation.FORCE_LANDSCAPE; // Default is forcing landscape
@Nullable private VideoViewabilityTracker mVideoViewabilityTracker;

/**
* Flag to indicate if the VAST xml document has explicitly set the orientation as opposed to
@@ -203,6 +205,12 @@ public void setSkipOffset(@Nullable final String skipOffset) {
}
}

public void setVideoViewabilityTracker(@Nullable final VideoViewabilityTracker videoViewabilityTracker) {
if (videoViewabilityTracker != null) {
mVideoViewabilityTracker = videoViewabilityTracker;
}
}

/**
* Getters
*/
@@ -309,6 +317,11 @@ public String getCustomCloseIconUrl() {
return mCustomCloseIconUrl;
}

@Nullable
public VideoViewabilityTracker getVideoViewabilityTracker() {
return mVideoViewabilityTracker;
}

public boolean isCustomForceOrientationSet() {
return mIsForceOrientationSet;
}
@@ -362,23 +375,50 @@ public void handleImpression(@NonNull final Context context, int contentPlayHead

/**
* Called when the video is clicked. Handles forwarding the user to the specified click through
* url.
* url, calling {@link Activity#onActivityResult(int, int, Intent)} when done.
*
* @param activity This has to be an activity to call startActivityForResult.
* @param activity Used to call startActivityForResult with provided requestCode.
* @param contentPlayHead Current video playback time when clicked.
* @param requestCode The code that identifies what kind of activity request is going to be
* made
* @param requestCode Code that identifies what kind of activity request is going to be
* made.
*/
public void handleClick(@NonNull final Activity activity, final int contentPlayHead,
public void handleClickForResult(@NonNull final Activity activity, final int contentPlayHead,
final int requestCode) {
Preconditions.checkNotNull(activity, "activity cannot be null");
handleClick(activity, contentPlayHead, requestCode);
}

/**
* Called when the video is clicked. Handles forwarding the user to the specified click through
* url. Does not provide any feedback when opened activity is finished.
*
* @param context Used to call startActivity.
* @param contentPlayHead Current video playback time when clicked.
*/
public void handleClickWithoutResult(@NonNull final Context context,
final int contentPlayHead) {
handleClick(context.getApplicationContext(), contentPlayHead, null);
}

/**
* Called when the video is clicked. Handles forwarding the user to the specified click through
* url.
*
* @param context If an Activity context, used to call startActivityForResult with
* provided requestCode; otherwise, used to call startActivity.
* @param contentPlayHead Current video playback time when clicked.
* @param requestCode Required when the context is an Activity; code that identifies what
* kind of activity request is going to be made.
*/
private void handleClick(@NonNull final Context context, final int contentPlayHead,
@Nullable final Integer requestCode) {
Preconditions.checkNotNull(context, "context cannot be null");

makeVastTrackingHttpRequest(
mClickTrackers,
null,
contentPlayHead,
mNetworkMediaFileUrl,
activity
context
);

if (TextUtils.isEmpty(mClickThroughUrl)) {
@@ -404,12 +444,23 @@ public void urlHandlingSucceeded(@NonNull String url,

final Class clazz = MoPubBrowser.class;
final Intent intent = Intents.getStartActivityIntent(
activity, clazz, bundle);
context, clazz, bundle);
try {
activity.startActivityForResult(intent, requestCode);
if (context instanceof Activity) {
// Activity context requires a requestCode
Preconditions.checkNotNull(requestCode);

((Activity) context)
.startActivityForResult(intent, requestCode);
} else {
Intents.startActivity(context, intent);
}
} catch (ActivityNotFoundException e) {
MoPubLog.d("Activity " + clazz.getName() + " not found. Did you " +
"declare it in your AndroidManifest.xml?");
} catch (IntentNotResolvableException e) {
MoPubLog.d("Activity " + clazz.getName() + " not found. Did you " +
"declare it in your AndroidManifest.xml?");
}
}
}
@@ -420,7 +471,7 @@ public void urlHandlingFailed(@NonNull String url,
}
})
.withoutMoPubBrowser()
.build().handleUrl(activity, mClickThroughUrl);
.build().handleUrl(context, mClickThroughUrl);
}

/**
@@ -506,7 +557,7 @@ public void handleComplete(@NonNull Context context, int contentPlayHead) {
* @param context The context. Can be application or activity context.
* @param contentPlayHead Current video playback time.
*/
public void handleError(@NonNull Context context, @NonNull VastErrorCode errorCode,
public void handleError(@NonNull Context context, @Nullable VastErrorCode errorCode,
int contentPlayHead) {
Preconditions.checkNotNull(context, "context cannot be null");
makeVastTrackingHttpRequest(
@@ -14,36 +14,43 @@

public class VastVideoProgressBarWidget extends ImageView {
@NonNull private ProgressBarDrawable mProgressBarDrawable;
private final int mProgressBarHeight;

public VastVideoProgressBarWidget(@NonNull final Context context, final int anchorId) {
public VastVideoProgressBarWidget(@NonNull final Context context) {
super(context);

setId((int) Utils.generateUniqueId());

mProgressBarDrawable = new ProgressBarDrawable(context);
setImageDrawable(mProgressBarDrawable);

final int progressBarHeight
= Dips.dipsToIntPixels(DrawableConstants.ProgressBar.HEIGHT_DIPS, context);
mProgressBarHeight =
Dips.dipsToIntPixels(DrawableConstants.ProgressBar.HEIGHT_DIPS, context);
}

public void setAnchorId(final int anchorId) {
final RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.MATCH_PARENT,
progressBarHeight);

mProgressBarHeight);
layoutParams.addRule(RelativeLayout.ALIGN_BOTTOM, anchorId);

setLayoutParams(layoutParams);

}

void calibrateAndMakeVisible(final int duration, final int skipOffset) {
public void calibrateAndMakeVisible(final int duration, final int skipOffset) {
mProgressBarDrawable.setDurationAndSkipOffset(duration, skipOffset);
setVisibility(View.VISIBLE);
}

void updateProgress(final int progress) {
public void updateProgress(final int progress) {
mProgressBarDrawable.setProgress(progress);
}

public void reset() {
mProgressBarDrawable.reset();
mProgressBarDrawable.setProgress(0);
}

// for testing
@Deprecated
@VisibleForTesting
@@ -22,6 +22,7 @@
import android.widget.RelativeLayout;
import android.widget.VideoView;

import com.mopub.common.MoPubBrowser;
import com.mopub.common.Preconditions;
import com.mopub.common.VisibleForTesting;
import com.mopub.common.util.Dips;
@@ -31,6 +32,7 @@

import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
import static com.mopub.common.MoPubBrowser.MOPUB_BROWSER_REQUEST_CODE;
import static com.mopub.mobileads.EventForwardingBroadcastReceiver.ACTION_INTERSTITIAL_CLICK;
import static com.mopub.mobileads.EventForwardingBroadcastReceiver.ACTION_INTERSTITIAL_DISMISS;
import static com.mopub.mobileads.EventForwardingBroadcastReceiver.ACTION_INTERSTITIAL_SHOW;
@@ -43,7 +45,6 @@

private static final long VIDEO_PROGRESS_TIMER_CHECKER_DELAY = 50;
private static final long VIDEO_COUNTDOWN_UPDATE_INTERVAL = 250;
private static final int MOPUB_BROWSER_REQUEST_CODE = 1;
private static final int SEEKER_POSITION_NOT_INITIALIZED = -1;

/**
@@ -133,7 +134,7 @@ public boolean onTouch(View view, MotionEvent motionEvent) {
if (motionEvent.getAction() == MotionEvent.ACTION_UP && shouldAllowClickThrough()) {
mIsClosing = true;
broadcastAction(ACTION_INTERSTITIAL_CLICK);
mVastVideoConfig.handleClick(activity,
mVastVideoConfig.handleClickForResult(activity,
mIsVideoFinishedPlaying ? mDuration : getCurrentPosition(),
MOPUB_BROWSER_REQUEST_CODE);
}
@@ -258,7 +259,7 @@ protected void onSaveInstanceState(@NonNull Bundle outState) {
}

@Override
protected void onConfigurationChanged(@Nullable final Configuration newConfig) {
protected void onConfigurationChanged(final Configuration newConfig) {
final int orientation = getContext().getResources().getConfiguration().orientation;
mVastCompanionAdConfig = mVastVideoConfig.getVastCompanionAd(orientation);
if (mLandscapeCompanionAdView.getVisibility() == View.VISIBLE ||
@@ -276,6 +277,9 @@ protected void onConfigurationChanged(@Nullable final Configuration newConfig) {
}
}

@Override
protected void onBackPressed() { }

// Enable the device's back button when the video close button has been displayed
@Override
public boolean backButtonEnabled() {
@@ -425,7 +429,8 @@ private void addBottomGradientStripWidget(@NonNull final Context context) {
}

private void addProgressBarWidget(@NonNull final Context context, int initialVisibility) {
mProgressBarWidget = new VastVideoProgressBarWidget(context, mVideoView.getId());
mProgressBarWidget = new VastVideoProgressBarWidget(context);
mProgressBarWidget.setAnchorId(mVideoView.getId());
mProgressBarWidget.setVisibility(initialVisibility);
getLayout().addView(mProgressBarWidget);
}
@@ -40,6 +40,8 @@
*/
public class VastXmlManagerAggregator extends AsyncTask<String, Void, VastVideoConfig> {

private static final String MOPUB = "MoPub";

/**
* Listener for when the xml parsing is done.
*/
@@ -226,6 +228,7 @@ VastVideoConfig evaluateVastXmlManager(@NonNull final String vastXml,
for (VastLinearXmlManager linearXmlManager : linearXmlManagers) {
populateLinearTrackersAndIcon(linearXmlManager, vastVideoConfig);
}
populateVideoViewabilityTracker(vastWrapperXmlManager, vastVideoConfig);

// Only populate a companion ad if we don't already have one from one of the
// redirects
@@ -308,13 +311,40 @@ private VastVideoConfig evaluateInLineXmlManager(
CompanionOrientation.PORTRAIT));
errorTrackers.addAll(vastInLineXmlManager.getErrorTrackers());
vastVideoConfig.addErrorTrackers(errorTrackers);
populateVideoViewabilityTracker(vastInLineXmlManager, vastVideoConfig);

return vastVideoConfig;
}
}

return null;
}

private void populateVideoViewabilityTracker(
@NonNull final VastBaseInLineWrapperXmlManager vastInLineXmlManager,
@NonNull VastVideoConfig vastVideoConfig) {
Preconditions.checkNotNull(vastInLineXmlManager);
Preconditions.checkNotNull(vastVideoConfig);

if (vastVideoConfig.getVideoViewabilityTracker() != null) {
return;
}

final VastExtensionParentXmlManager vastExtensionParentXmlManager =
vastInLineXmlManager.getVastExtensionParentXmlManager();
if (vastExtensionParentXmlManager != null) {
final List<VastExtensionXmlManager> vastExtensionXmlManagers =
vastExtensionParentXmlManager.getVastExtensionXmlManagers();
for (VastExtensionXmlManager vastExtensionXmlManager : vastExtensionXmlManagers) {
if (MOPUB.equals(vastExtensionXmlManager.getType())) {
vastVideoConfig.setVideoViewabilityTracker(vastExtensionXmlManager
.getVideoViewabilityTracker());
break;
}
}
}
}

/**
* Retrieves the Wrapper's redirect uri and follows it to return the next VAST xml String.
*
@@ -0,0 +1,23 @@
package com.mopub.mobileads;

import android.support.annotation.NonNull;

public class VideoViewabilityTracker extends VastTracker {
private final int mViewablePlaytimeMS;
private final int mPercentViewable;

public VideoViewabilityTracker(final int viewablePlaytimeMS, final int percentViewable,
@NonNull final String trackerUrl) {
super(trackerUrl);
mViewablePlaytimeMS = viewablePlaytimeMS;
mPercentViewable = percentViewable;
}

public int getViewablePlaytimeMS() {
return mViewablePlaytimeMS;
}

public int getPercentViewable() {
return mPercentViewable;
}
}
@@ -0,0 +1,112 @@
package com.mopub.mobileads;

import android.support.annotation.NonNull;
import android.support.annotation.Nullable;

import com.mopub.common.Preconditions;
import com.mopub.common.logging.MoPubLog;
import com.mopub.common.util.Strings;
import com.mopub.mobileads.util.XmlUtils;

import org.w3c.dom.Node;

/**
* Data Object for the MoPubViewabilityTracker VAST Custom Extension.
*/
public class VideoViewabilityTrackerXmlManager {
// Attributes
public static final String VIEWABLE_PLAYTIME = "viewablePlaytime";
public static final String PERCENT_VIEWABLE = "percentViewable";

private final Node mVideoViewabilityNode;

VideoViewabilityTrackerXmlManager(@NonNull final Node videoViewabilityNode) {
Preconditions.checkNotNull(videoViewabilityNode);

mVideoViewabilityNode = videoViewabilityNode;
}

/**
* The amount of milliseconds the video must be playing to fire the impression, parsed from the
* "viewablePlaytime" attribute. The following two formats are valid for this attribute:
* HH:MM:SS[.mmm]
* SS[.mmm]
*
* @return The value of the "viewablePlaytime" attribute parsed into milliseconds or null if
* missing or invalid.
*/
@Nullable
Integer getViewablePlaytimeMS() {
String viewablePlaytimeStr =
XmlUtils.getAttributeValue(mVideoViewabilityNode, VIEWABLE_PLAYTIME);
if (viewablePlaytimeStr == null) {
return null;
}

Integer viewablePlaytimeMS = null;
if (Strings.isAbsoluteTracker(viewablePlaytimeStr)) {
try {
viewablePlaytimeMS = Strings.parseAbsoluteOffset(viewablePlaytimeStr);
} catch (NumberFormatException e) {
MoPubLog.d(String.format("Invalid VAST viewablePlaytime format " +
"for \"HH:MM:SS[.mmm]\": %s:", viewablePlaytimeStr));
}
} else {
try {
viewablePlaytimeMS = (int) (Float.parseFloat(viewablePlaytimeStr) * 1000);
} catch (NumberFormatException e) {
MoPubLog.d(String.format("Invalid VAST viewablePlaytime format" +
" for \"SS[.mmm]\": %s:", viewablePlaytimeStr));
}
}

if (viewablePlaytimeMS == null || viewablePlaytimeMS < 0) {
return null;
}

return viewablePlaytimeMS;
}

/**
* The percentage of the video that must be in view for the tracker to be fired, parsed from the
* "percentViewable" attribute. The attribute may or may not have the percentage sign though it
* must be a number between 0 and 100, inclusive. Any decimal digits are discarded (e.g.,
* "99.9%" is parsed into "99").
*
* @return The value of the percentViewable attribute parsed into an integer between 0 and 100,
* or null if missing or invalid.
*/
@Nullable
Integer getPercentViewable() {
String percentViewableStr =
XmlUtils.getAttributeValue(mVideoViewabilityNode, PERCENT_VIEWABLE);
if (percentViewableStr == null) {
return null;
}

Integer percentViewable = null;
try {
percentViewable = (int) (Float.parseFloat(percentViewableStr.replace("%", "")));
} catch (NumberFormatException e) {
MoPubLog.d(String.format("Invalid VAST percentViewable format for \"d{1,3}%%\": %s:",
percentViewableStr));
}

if (percentViewable == null || percentViewable < 0 || percentViewable > 100) {
return null;
}

return percentViewable;
}

/**
* The tracker URL to be fired when the viewablePlaytime and percentViewable values are met,
* parsed from the body of the node.
*
* @return A String with the tracker URL or null if missing.
*/
@Nullable
String getVideoViewabilityTrackerUrl() {
return XmlUtils.getNodeValue(mVideoViewabilityNode);
}
}
@@ -2,12 +2,14 @@

import android.content.Context;

import com.mopub.common.VisibleForTesting;
import com.mopub.mobileads.MoPubView;

public class MoPubViewFactory {
protected static MoPubViewFactory instance = new MoPubViewFactory();

@Deprecated // for testing
@VisibleForTesting
@Deprecated
public static void setInstance(MoPubViewFactory factory) {
instance = factory;
}
@@ -8,11 +8,15 @@
protected static VastManagerFactory instance = new VastManagerFactory();

public static VastManager create(final Context context) {
return instance.internalCreate(context);
return instance.internalCreate(context, true);
}

public VastManager internalCreate(final Context context) {
return new VastManager(context);
public static VastManager create(final Context context, boolean preCacheVideo) {
return instance.internalCreate(context, preCacheVideo);
}

public VastManager internalCreate(final Context context, boolean preCacheVideo) {
return new VastManager(context, preCacheVideo);
}

@Deprecated // for testing
@@ -6,21 +6,32 @@

public class CloseButtonDrawable extends BaseWidgetDrawable {
private final Paint closeButtonPaint;
/**
* Used to ensure that the rounded edges of the X stay in the bounds of the drawable
*/
private final float halfStrokeWidth;

public CloseButtonDrawable() {
this(DrawableConstants.CloseButton.STROKE_WIDTH);
}

public CloseButtonDrawable(float strokeWidth) {
super();

halfStrokeWidth = strokeWidth / 2;
closeButtonPaint = new Paint();
closeButtonPaint.setColor(DrawableConstants.CloseButton.STROKE_COLOR);
closeButtonPaint.setStrokeWidth(DrawableConstants.CloseButton.STROKE_WIDTH);
closeButtonPaint.setStrokeWidth(strokeWidth);
closeButtonPaint.setStrokeCap(DrawableConstants.CloseButton.STROKE_CAP);
}

@Override
public void draw(final Canvas canvas) {
final int w = getBounds().width();
final int h = getBounds().height();
canvas.drawLine(0, h, w, 0, closeButtonPaint);
canvas.drawLine(0, 0, w, h, closeButtonPaint);
canvas.drawLine(0+halfStrokeWidth, h-halfStrokeWidth,
w-halfStrokeWidth, 0+halfStrokeWidth, closeButtonPaint);
canvas.drawLine(0+halfStrokeWidth, 0+halfStrokeWidth,
w-halfStrokeWidth, h-halfStrokeWidth, closeButtonPaint);
}
}
@@ -6,6 +6,8 @@

public class DrawableConstants {

public static final int TRANSPARENT_GRAY = 0x88000000;

public static class ProgressBar {
public static final int HEIGHT_DIPS = 4;
public static final int NUGGET_WIDTH_DIPS = 4;
@@ -82,7 +84,7 @@
public static class GradientStrip {
public static final int GRADIENT_STRIP_HEIGHT_DIPS = 72;
public static final int START_COLOR = Color.argb(102, 0, 0, 0);
public static final int END_COLOR = Color.argb(0, 255, 255, 255);
public static final int END_COLOR = Color.argb(0, 0, 0, 0);
}

public static class BlurredLastVideoFrame {
@@ -63,6 +63,10 @@ public void draw(final Canvas canvas) {
}
}

public void reset() {
mLastProgress = 0;
}

public void setDurationAndSkipOffset(final int duration, final int skipOffset) {
mDuration = duration;
mSkipOffset = skipOffset;
@@ -6,7 +6,6 @@
import android.media.MediaPlayer;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.view.View;
import android.widget.ImageButton;
import android.widget.RelativeLayout;
@@ -88,8 +87,10 @@ protected void onResume() {}
protected void onSaveInstanceState(@NonNull Bundle outState) {}

@Override
protected void onConfigurationChanged(@Nullable Configuration newConfig) {
}
protected void onConfigurationChanged(final Configuration newConfig) {}

@Override
protected void onBackPressed() {}

private void createInterstitialCloseButton() {
mCloseButton = new ImageButton(getContext());
@@ -0,0 +1,90 @@
package com.mopub.nativeads;

import android.support.annotation.NonNull;
import android.support.annotation.Nullable;

import com.mopub.common.Preconditions;

import java.util.ArrayList;

/**
* A data structure providing methods to store and retrieve native ad renderers.
*/
public class AdRendererRegistry {

@NonNull private final ArrayList<MoPubAdRenderer> mMoPubAdRenderers;

public AdRendererRegistry() {
mMoPubAdRenderers = new ArrayList<MoPubAdRenderer>();
}

/**
* Registers an ad renderer for rendering a specific native ad format.
* Note that if multiple ad renderers support a specific native ad format, the first
* one registered will be used.
*/
public void registerAdRenderer(@NonNull final MoPubAdRenderer moPubAdRenderer) {
mMoPubAdRenderers.add(moPubAdRenderer);
}

public int getAdRendererCount() {
return mMoPubAdRenderers.size();
}

@NonNull
public Iterable<MoPubAdRenderer> getRendererIterable() {
return mMoPubAdRenderers;
}

/**
* Returns the view type of the first registered ad renderer that supports rendering the
* {@link NativeAd} passed in. View types reserved for native ads are greater than or equal
* to 1, hence we add 1 when returning the view type.
*
* @param nativeAd The {@link NativeAd} to render.
* @return The integer representing the view type of the first renderer registered that
* supports rendering the {@link NativeAd}.
*/
public int getViewTypeForAd(@NonNull final NativeAd nativeAd) {
Preconditions.checkNotNull(nativeAd);
for (int i = 0; i < mMoPubAdRenderers.size(); ++i) {
if (nativeAd.getMoPubAdRenderer() == mMoPubAdRenderers.get(i)) {
return i + 1;
}
}
return 0;
}

/**
* Returns the first registered ad renderer that supports rendering the native ad passed in.
*
* @param nativeAd The native ad to render.
* @return The renderer that supports rendering the native ad.
*/
@Nullable
public MoPubAdRenderer getRendererForAd(@NonNull final BaseNativeAd nativeAd) {
Preconditions.checkNotNull(nativeAd);
for (MoPubAdRenderer moPubAdRenderer : mMoPubAdRenderers) {
if (moPubAdRenderer.supports(nativeAd)) {
return moPubAdRenderer;
}
}
return null;
}

/**
* Returns the renderer corresponding to view type passed in. View types reserved for native
* ads are greater than or equal to 1, hence we subtract 1 when matching the renderer.
*
* @param viewType The integer representing the view type of renderer.
* @return The renderer mapped to the view type.
*/
@Nullable
public MoPubAdRenderer getRendererForViewType(final int viewType) {
try {
return mMoPubAdRenderers.get(viewType - 1);
} catch (IndexOutOfBoundsException e) {
return null;
}
}
}
@@ -13,8 +13,6 @@

import java.lang.ref.WeakReference;

import static com.mopub.nativeads.MoPubNative.MoPubNativeListener;

/**
* @deprecated As of release 2.4, use {@link com.mopub.nativeads.MoPubAdAdapter} or
* {@link com.mopub.nativeads.MoPubStreamAdPlacer} instead
@@ -43,9 +41,8 @@ public AdapterHelper(@NonNull final Context context, final int start, final int
@NonNull
public View getAdView(@Nullable final View convertView,
@Nullable final ViewGroup parent,
@Nullable final NativeResponse nativeResponse,
@Nullable final ViewBinder viewBinder,
@Nullable @SuppressWarnings("unused") final MoPubNativeListener moPubNativeListener) {
@Nullable final NativeAd nativeAd,
@Nullable final ViewBinder viewBinder) {
final Activity activity = mActivity.get();
if (activity == null) {
MoPubLog.w("Weak reference to Activity Context in"
@@ -57,7 +54,7 @@ public View getAdView(@Nullable final View convertView,
convertView,
parent,
activity,
nativeResponse,
nativeAd,
viewBinder
);
}
Oops, something went wrong.