package com.mopub.mobileads.util.vast; import android.content.Context; import android.view.Display; import android.view.WindowManager; import com.mopub.common.CacheService; import com.mopub.common.logging.MoPubLog; import com.mopub.common.util.AsyncTasks; import com.mopub.mobileads.VastVideoDownloadTask; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; import static com.mopub.mobileads.VastVideoDownloadTask.VastVideoDownloadTaskListener; import static com.mopub.mobileads.util.vast.VastXmlManagerAggregator.VastXmlManagerAggregatorListener; public class VastManager implements VastXmlManagerAggregatorListener { public interface VastManagerListener { public void onVastVideoConfigurationPrepared(final VastVideoConfiguration vastVideoConfiguration); } private static final double ASPECT_RATIO_WEIGHT = 40; private static final double AREA_WEIGHT = 60; private static final List<String> VIDEO_MIME_TYPES = Arrays.asList("video/mp4", "video/3gpp"); private static final List<String> COMPANION_IMAGE_MIME_TYPES = Arrays.asList("image/jpeg", "image/png", "image/bmp", "image/gif"); private VastManagerListener mVastManagerListener; private VastXmlManagerAggregator mVastXmlManagerAggregator; private double mScreenAspectRatio; private int mScreenArea; public VastManager(final Context context) { initializeScreenDimensions(context); } public void prepareVastVideoConfiguration(final String vastXml, final VastManagerListener vastManagerListener) { if (mVastXmlManagerAggregator == null) { mVastManagerListener = vastManagerListener; mVastXmlManagerAggregator = new VastXmlManagerAggregator(this); try { AsyncTasks.safeExecuteOnExecutor(mVastXmlManagerAggregator, vastXml); } catch (Exception e) { MoPubLog.d("Failed to aggregate vast xml", e); mVastManagerListener.onVastVideoConfigurationPrepared(null); } } } public void cancel() { if (mVastXmlManagerAggregator != null) { mVastXmlManagerAggregator.cancel(true); mVastXmlManagerAggregator = null; } } @Override public void onAggregationComplete(final List<VastXmlManager> vastXmlManagers) { mVastXmlManagerAggregator = null; if (vastXmlManagers == null) { mVastManagerListener.onVastVideoConfigurationPrepared(null); return; } final VastVideoConfiguration vastVideoConfiguration = createVastVideoConfigurationFromXml(vastXmlManagers); if (updateDiskMediaFileUrl(vastVideoConfiguration)) { mVastManagerListener.onVastVideoConfigurationPrepared(vastVideoConfiguration); return; } final VastVideoDownloadTask vastVideoDownloadTask = new VastVideoDownloadTask( new VastVideoDownloadTaskListener() { @Override public void onComplete(boolean success) { if (success && updateDiskMediaFileUrl(vastVideoConfiguration)) { mVastManagerListener.onVastVideoConfigurationPrepared(vastVideoConfiguration); } else { mVastManagerListener.onVastVideoConfigurationPrepared(null); } } } ); try { AsyncTasks.safeExecuteOnExecutor( vastVideoDownloadTask, vastVideoConfiguration.getNetworkMediaFileUrl() ); } catch (Exception e) { MoPubLog.d("Failed to download vast video", e); mVastManagerListener.onVastVideoConfigurationPrepared(null); } } private boolean updateDiskMediaFileUrl(final VastVideoConfiguration vastVideoConfiguration) { final String networkMediaFileUrl = vastVideoConfiguration.getNetworkMediaFileUrl(); if (CacheService.containsKeyDiskCache(networkMediaFileUrl)) { final String filePathDiskCache = CacheService.getFilePathDiskCache(networkMediaFileUrl); vastVideoConfiguration.setDiskMediaFileUrl(filePathDiskCache); return true; } return false; } private void initializeScreenDimensions(final Context context) { // This currently assumes that all vast videos will be played in landscape final Display display = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay(); int x = display.getWidth(); int y = display.getHeight(); // For landscape, width is always greater than height int screenWidth = Math.max(x, y); int screenHeight = Math.min(x, y); mScreenAspectRatio = (double) screenWidth / screenHeight; mScreenArea = screenWidth * screenHeight; } private VastVideoConfiguration createVastVideoConfigurationFromXml(final List<VastXmlManager> xmlManagers) { final VastVideoConfiguration vastVideoConfiguration = new VastVideoConfiguration(); final List<VastXmlManager.MediaXmlManager> mediaXmlManagers = new ArrayList<VastXmlManager.MediaXmlManager>(); final List<VastXmlManager.ImageCompanionAdXmlManager> companionXmlManagers = new ArrayList<VastXmlManager.ImageCompanionAdXmlManager>(); for (VastXmlManager xmlManager : xmlManagers) { vastVideoConfiguration.addImpressionTrackers(xmlManager.getImpressionTrackers()); vastVideoConfiguration.addStartTrackers(xmlManager.getVideoStartTrackers()); vastVideoConfiguration.addFirstQuartileTrackers(xmlManager.getVideoFirstQuartileTrackers()); vastVideoConfiguration.addMidpointTrackers(xmlManager.getVideoMidpointTrackers()); vastVideoConfiguration.addThirdQuartileTrackers(xmlManager.getVideoThirdQuartileTrackers()); vastVideoConfiguration.addCompleteTrackers(xmlManager.getVideoCompleteTrackers()); vastVideoConfiguration.addClickTrackers(xmlManager.getClickTrackers()); if (vastVideoConfiguration.getClickThroughUrl() == null) { vastVideoConfiguration.setClickThroughUrl(xmlManager.getClickThroughUrl()); } mediaXmlManagers.addAll(xmlManager.getMediaXmlManagers()); companionXmlManagers.addAll(xmlManager.getCompanionAdXmlManagers()); } vastVideoConfiguration.setNetworkMediaFileUrl(getBestMediaFileUrl(mediaXmlManagers)); vastVideoConfiguration.setVastCompanionAd(getBestCompanionAd(companionXmlManagers)); return vastVideoConfiguration; } String getBestMediaFileUrl(final List<VastXmlManager.MediaXmlManager> managers) { final List<VastXmlManager.MediaXmlManager> mediaXmlManagers = new ArrayList<VastXmlManager.MediaXmlManager>(managers); double bestMediaFitness = Double.POSITIVE_INFINITY; String bestMediaFileUrl = null; final Iterator<VastXmlManager.MediaXmlManager> xmlManagerIterator = mediaXmlManagers.iterator(); while (xmlManagerIterator.hasNext()) { final VastXmlManager.MediaXmlManager mediaXmlManager = xmlManagerIterator.next(); final String mediaType = mediaXmlManager.getType(); final String mediaUrl = mediaXmlManager.getMediaUrl(); if (!VIDEO_MIME_TYPES.contains(mediaType) || mediaUrl == null) { xmlManagerIterator.remove(); continue; } final Integer mediaWidth = mediaXmlManager.getWidth(); final Integer mediaHeight = mediaXmlManager.getHeight(); if (mediaWidth == null || mediaWidth <= 0 || mediaHeight == null || mediaHeight <= 0) { continue; } final double mediaFitness = calculateFitness(mediaWidth, mediaHeight); if (mediaFitness < bestMediaFitness) { bestMediaFitness = mediaFitness; bestMediaFileUrl = mediaUrl; } } if (bestMediaFileUrl == null && !mediaXmlManagers.isEmpty()) { bestMediaFileUrl = mediaXmlManagers.get(0).getMediaUrl(); } return bestMediaFileUrl; } VastCompanionAd getBestCompanionAd(final List<VastXmlManager.ImageCompanionAdXmlManager> managers) { final List<VastXmlManager.ImageCompanionAdXmlManager> companionXmlManagers = new ArrayList<VastXmlManager.ImageCompanionAdXmlManager>(managers); double bestCompanionFitness = Double.POSITIVE_INFINITY; VastXmlManager.ImageCompanionAdXmlManager bestCompanionXmlManager = null; final Iterator<VastXmlManager.ImageCompanionAdXmlManager> xmlManagerIterator = companionXmlManagers.iterator(); while (xmlManagerIterator.hasNext()) { final VastXmlManager.ImageCompanionAdXmlManager companionXmlManager = xmlManagerIterator.next(); final String imageType = companionXmlManager.getType(); final String imageUrl = companionXmlManager.getImageUrl(); if (!COMPANION_IMAGE_MIME_TYPES.contains(imageType) || imageUrl == null) { xmlManagerIterator.remove(); continue; } final Integer imageWidth = companionXmlManager.getWidth(); final Integer imageHeight = companionXmlManager.getHeight(); if (imageWidth == null || imageWidth <= 0 || imageHeight == null || imageHeight <= 0) { continue; } final double companionFitness = calculateFitness(imageWidth, imageHeight); if (companionFitness < bestCompanionFitness) { bestCompanionFitness = companionFitness; bestCompanionXmlManager = companionXmlManager; } } if (bestCompanionXmlManager == null && !companionXmlManagers.isEmpty()) { bestCompanionXmlManager = companionXmlManagers.get(0); } if (bestCompanionXmlManager != null) { return new VastCompanionAd( bestCompanionXmlManager.getWidth(), bestCompanionXmlManager.getHeight(), bestCompanionXmlManager.getImageUrl(), bestCompanionXmlManager.getClickThroughUrl(), new ArrayList<String>(bestCompanionXmlManager.getClickTrackers()) ); } return null; } private double calculateFitness(final int width, final int height) { final double mediaAspectRatio = (double) width / height; final int mediaArea = width * height; final double aspectRatioRatio = mediaAspectRatio / mScreenAspectRatio; final double areaRatio = (double) mediaArea / mScreenArea; return ASPECT_RATIO_WEIGHT * Math.abs(Math.log(aspectRatioRatio)) + AREA_WEIGHT * Math.abs(Math.log(areaRatio)); } @Deprecated // for testing int getScreenArea() { return mScreenArea; } @Deprecated // for testing double getScreenAspectRatio() { return mScreenAspectRatio; } }