| @@ -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; | |||
| } | |||
| } | |||
| } | |||
| @@ -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); | |||
| } | |||
| } | |||
| @@ -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); | |||
| } | |||
| } | |||
| @@ -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; | |||
| } | |||
| } | |||
| } | |||