[android] Updated MyTarget sdk and Google play services

[android] Added support for MyTarget native ads
This commit is contained in:
alexzatsepin 2017-03-29 11:49:30 +03:00 committed by Vladimir Byko-Ianko
parent 235560f251
commit 12e8f8866d
14 changed files with 349 additions and 140 deletions

Binary file not shown.

View file

@ -37,10 +37,10 @@ dependencies {
compile 'com.android.support:design:23.2.1'
compile 'com.android.support:cardview-v7:23.2.1'
// google play services
compile 'com.google.android.gms:play-services-location:8.4.0'
compile 'com.google.android.gms:play-services-analytics:8.4.0'
compile 'com.google.android.gms:play-services-plus:8.4.0'
compile 'com.google.android.gms:play-services-gcm:8.4.0'
compile 'com.google.android.gms:play-services-location:10.0.1'
compile 'com.google.android.gms:play-services-analytics:10.0.1'
compile 'com.google.android.gms:play-services-plus:10.0.1'
compile 'com.google.android.gms:play-services-gcm:10.0.1'
// statistics
compile 'com.flurry.android:analytics:6.7.0'
// crash reporting
@ -54,6 +54,7 @@ dependencies {
compile 'com.google.code.gson:gson:2.6.1'
compile 'com.pushwoosh:pushwoosh:4.10.7'
compile 'com.my.tracker:mytracker-sdk:1.3.5'
compile 'com.my.target:mytarget-sdk:4.6.9'
compile fileTree(dir: '3rd_party', include: '*.jar')
// BottomSheet
compile project(":3rd_party:BottomSheet")

View file

@ -0,0 +1,16 @@
package com.mapswithme.maps.ads;
abstract class CachedMwmNativeAd implements MwmNativeAd
{
private final long mLoadedTime;
CachedMwmNativeAd(long loadedTime)
{
mLoadedTime = loadedTime;
}
long getLoadedTime()
{
return mLoadedTime;
}
}

View file

@ -0,0 +1,150 @@
package com.mapswithme.maps.ads;
import android.content.Context;
import android.os.SystemClock;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import com.mapswithme.util.log.Logger;
import com.mapswithme.util.log.LoggerFactory;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
abstract class CachedNativeAdLoader extends BaseNativeAdLoader
{
private static final Logger LOGGER = LoggerFactory.INSTANCE.getLogger(LoggerFactory.Type.MISC);
private static final String TAG = CachedNativeAdLoader.class.getSimpleName();
private static final long REQUEST_INTERVAL_MS = 30 * 1000;
private static final Map<String, CachedMwmNativeAd> CACHE = new HashMap<>();
private static final Set<String> PENDING_REQUESTS = new HashSet<>();
@Nullable
private AdTracker mTracker;
@Nullable
private OnAdCacheModifiedListener mCacheListener;
CachedNativeAdLoader(@Nullable AdTracker tracker, @Nullable OnAdCacheModifiedListener listener)
{
mTracker = tracker;
mCacheListener = listener;
}
/**
* Loads an ad for a specified banner id. If there is a cached ad, the caller will be notified
* immediately through {@link NativeAdListener#onAdLoaded(MwmNativeAd)}.
* Otherwise, the caller will be notified once an ad is loaded through the mentioned method.
*
* <br><br><b>Important note: </b> if there is a cached ad for the requested banner id, and that ad
* has a good impression indicator, and there is at least {@link #REQUEST_INTERVAL_MS} between the
* first time that ad was requested and the current time the new ad will be loaded.
*
*/
@Override
public void loadAd(@NonNull Context context, @NonNull String bannerId)
{
LOGGER.d(TAG, "Load the ad for a banner id '" + bannerId + "'");
if (isAdLoading(bannerId))
{
LOGGER.d(TAG, "The ad request for banner id '" + bannerId + "' hasn't been completed yet.");
return;
}
CachedMwmNativeAd cachedAd = getAdByIdFromCache(bannerId);
if (cachedAd == null)
{
LOGGER.d(TAG, "There is no an ad in a cache");
requestAd(context, bannerId);
return;
}
if (mTracker != null && mTracker.isImpressionGood(bannerId)
&& SystemClock.elapsedRealtime() - cachedAd.getLoadedTime() >= REQUEST_INTERVAL_MS)
{
LOGGER.d(TAG, "A new ad will be loaded because the previous one has a good impression");
requestAd(context, bannerId);
}
if (getAdListener() != null)
getAdListener().onAdLoaded(cachedAd);
}
private void requestAd(@NonNull Context context, @NonNull String bannerId)
{
requestAdForBannerId(context, bannerId);
PENDING_REQUESTS.add(bannerId);
}
abstract void requestAdForBannerId(@NonNull Context context, @NonNull String bannerId);
void onError(@NonNull String bannerId)
{
PENDING_REQUESTS.remove(bannerId);
}
void onAdLoaded(@NonNull String bannerId, @NonNull CachedMwmNativeAd ad)
{
LOGGER.d(TAG, "An ad for id '" + bannerId + "' is loaded");
PENDING_REQUESTS.remove(bannerId);
boolean isCacheWasEmpty = isCacheEmptyForId(bannerId);
LOGGER.d(TAG, "Put the ad to cache");
putInCache(bannerId, ad);
if (isCacheWasEmpty && getAdListener() != null)
getAdListener().onAdLoaded(ad);
}
void onAdClicked(@NonNull String bannerId)
{
if (getAdListener() != null)
{
MwmNativeAd nativeAd = getAdByIdFromCache(bannerId);
if (nativeAd == null)
throw new AssertionError("A facebook native ad must be presented in a cache when it's clicked!");
getAdListener().onClick(nativeAd);
}
}
/**
* Indicates whether the ad is loading right now or not.
*
* @param bannerId A banner id that an ad is loading for.
* @return true if an ad is loading, otherwise - false.
*/
public boolean isAdLoading(@NonNull String bannerId)
{
return PENDING_REQUESTS.contains(bannerId);
}
private void putInCache(@NonNull String key, @NonNull CachedMwmNativeAd value)
{
CACHE.put(key, value);
if (mCacheListener != null)
mCacheListener.onPut(key);
}
private void removeFromCache(@NonNull String key, @NonNull CachedMwmNativeAd value)
{
CACHE.remove(key);
if (mCacheListener != null)
mCacheListener.onRemoved(key);
}
@Nullable
private CachedMwmNativeAd getAdByIdFromCache(@NonNull String bannerId)
{
return CACHE.get(bannerId);
}
private boolean isCacheEmptyForId(@NonNull String bannerId)
{
return getAdByIdFromCache(bannerId) == null;
}
}

View file

@ -4,7 +4,6 @@ import android.content.Context;
import android.os.SystemClock;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
import com.facebook.ads.Ad;
import com.facebook.ads.AdError;
@ -15,86 +14,17 @@ import com.mapswithme.util.log.LoggerFactory;
import net.jcip.annotations.NotThreadSafe;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
@NotThreadSafe
class FacebookAdsLoader extends BaseNativeAdLoader implements AdListener
class FacebookAdsLoader extends CachedNativeAdLoader implements AdListener
{
private static final Logger LOGGER = LoggerFactory.INSTANCE.getLogger(LoggerFactory.Type.MISC);
private static final String TAG = FacebookAdsLoader.class.getSimpleName();
private static final long REQUEST_INTERVAL_MS = 30 * 1000;
private static final Map<String, FacebookNativeAd> CACHE = new HashMap<>();
private static final Set<String> PENDING_REQUESTS = new HashSet<>();
@Nullable
private OnAdCacheModifiedListener mCacheListener;
@Nullable
private AdTracker mTracker;
FacebookAdsLoader(@Nullable OnAdCacheModifiedListener cacheListener,
@Nullable AdTracker tracker)
{
mCacheListener = cacheListener;
mTracker = tracker;
}
/**
* Loads an ad for a specified banner id. If there is a cached ad, the caller will be notified
* immediately through {@link NativeAdListener#onAdLoaded(MwmNativeAd)}.
* Otherwise, the caller will be notified once an ad is loaded through the mentioned method.
*
* <br><br><b>Important note: </b> if there is a cached ad for the requested banner id, and that ad
* has a good impression indicator, and there is at least {@link #REQUEST_INTERVAL_MS} between the
* first time that ad was requested and the current time the new ad will be loaded.
*
*/
@UiThread
public void loadAd(@NonNull Context context, @NonNull String bannerId)
{
LOGGER.d(TAG, "Load a facebook ad for a banner id '" + bannerId + "'");
FacebookNativeAd cachedAd = getAdByIdFromCache(bannerId);
if (cachedAd == null)
{
LOGGER.d(TAG, "There is no an ad in a cache");
loadAdInternal(context, bannerId);
return;
}
if (mTracker != null && mTracker.isImpressionGood(bannerId)
&& SystemClock.elapsedRealtime() - cachedAd.getLoadedTime() >= REQUEST_INTERVAL_MS)
{
LOGGER.d(TAG, "A new ad will be loaded because the previous one has a good impression");
loadAdInternal(context, bannerId);
}
if (getAdListener() != null)
getAdListener().onAdLoaded(cachedAd);
}
/**
* Indicates whether the ad is loading right now or not.
*
* @param bannerId A banner id that an ad is loading for.
* @return true if an ad is loading, otherwise - false.
*/
public boolean isAdLoading(@NonNull String bannerId)
{
return PENDING_REQUESTS.contains(bannerId);
}
@Nullable
private FacebookNativeAd getAdByIdFromCache(@NonNull String bannerId)
{
return CACHE.get(bannerId);
}
private boolean isCacheEmptyForId(@NonNull String bannerId)
{
return getAdByIdFromCache(bannerId) == null;
super(tracker, cacheListener);
}
@Override
@ -102,7 +32,8 @@ class FacebookAdsLoader extends BaseNativeAdLoader implements AdListener
{
LOGGER.w(TAG, "A error '" + adError.getErrorMessage() + "' is occurred while loading " +
"an ad for banner id '" + ad.getPlacementId() + "'");
PENDING_REQUESTS.remove(ad.getPlacementId());
onError(ad.getPlacementId());
if (getAdListener() != null)
getAdListener().onError(new FacebookNativeAd((NativeAd)ad), new FacebookAdError(adError));
}
@ -110,58 +41,22 @@ class FacebookAdsLoader extends BaseNativeAdLoader implements AdListener
@Override
public void onAdLoaded(Ad ad)
{
LOGGER.d(TAG, "An ad for id '" + ad.getPlacementId() + "' is loaded");
PENDING_REQUESTS.remove(ad.getPlacementId());
boolean isCacheWasEmpty = isCacheEmptyForId(ad.getPlacementId());
LOGGER.d(TAG, "Put a facebook ad to cache");
MwmNativeAd nativeAd = new FacebookNativeAd((NativeAd) ad, SystemClock.elapsedRealtime());
putInCache(ad.getPlacementId(), (FacebookNativeAd) nativeAd);
if (isCacheWasEmpty && getAdListener() != null)
getAdListener().onAdLoaded(nativeAd);
CachedMwmNativeAd nativeAd = new FacebookNativeAd((NativeAd) ad, SystemClock.elapsedRealtime());
onAdLoaded(ad.getPlacementId(), nativeAd);
}
@Override
public void onAdClicked(Ad ad)
{
if (getAdListener() != null)
{
MwmNativeAd nativeAd = getAdByIdFromCache(ad.getPlacementId());
if (nativeAd == null)
throw new AssertionError("A facebook native ad must be presented in a cache when it's clicked!");
getAdListener().onClick(nativeAd);
}
onAdClicked(ad.getPlacementId());
}
private void loadAdInternal(@NonNull Context context, @NonNull String bannerId)
@Override
void requestAdForBannerId(@NonNull Context context, @NonNull String bannerId)
{
if (PENDING_REQUESTS.contains(bannerId))
{
LOGGER.d(TAG, "The ad request for banner id '" + bannerId + "' hasn't been completed yet.");
return;
}
NativeAd ad = new NativeAd(context, bannerId);
ad.setAdListener(this);
LOGGER.d(TAG, "Loading is started");
ad.loadAd(EnumSet.of(NativeAd.MediaCacheFlag.ICON));
PENDING_REQUESTS.add(bannerId);
}
private void putInCache(@NonNull String key, @NonNull FacebookNativeAd value)
{
CACHE.put(key, value);
if (mCacheListener != null)
mCacheListener.onPut(key);
}
private void removeFromCache(@NonNull String key, @NonNull FacebookNativeAd value)
{
CACHE.remove(key);
if (mCacheListener != null)
mCacheListener.onRemoved(key);
}
}

View file

@ -10,29 +10,23 @@ import com.mapswithme.maps.R;
import java.util.ArrayList;
import java.util.List;
class FacebookNativeAd implements MwmNativeAd
class FacebookNativeAd extends CachedMwmNativeAd
{
@NonNull
private final NativeAd mAd;
private final long mLoadedTime;
FacebookNativeAd(@NonNull NativeAd ad, long timestamp)
{
mLoadedTime = timestamp;
super(timestamp);
mAd = ad;
}
FacebookNativeAd(@NonNull NativeAd ad)
{
mLoadedTime = 0;
super(0);
mAd = ad;
}
long getLoadedTime()
{
return mLoadedTime;
}
@NonNull
@Override
public String getTitle()

View file

@ -11,4 +11,11 @@ public class Factory
{
return new FacebookAdsLoader(cacheListener, tracker);
}
@NonNull
public static NativeAdLoader createMyTargetAdLoader(@Nullable OnAdCacheModifiedListener cacheListener,
@Nullable AdTracker tracker)
{
return new MyTargetAdsLoader(cacheListener, tracker);
}
}

View file

@ -0,0 +1,28 @@
package com.mapswithme.maps.ads;
import android.support.annotation.Nullable;
class MyTargetAdError implements NativeAdError
{
@Nullable
private final String mMessage;
MyTargetAdError(@Nullable String message)
{
mMessage = message;
}
@Nullable
@Override
public String getMessage()
{
return mMessage;
}
@Override
public int getCode()
{
return 0;
}
}

View file

@ -0,0 +1,62 @@
package com.mapswithme.maps.ads;
import android.content.Context;
import android.os.SystemClock;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import com.mapswithme.util.log.Logger;
import com.mapswithme.util.log.LoggerFactory;
import com.my.target.ads.CustomParams;
import com.my.target.nativeads.NativeAd;
class MyTargetAdsLoader extends CachedNativeAdLoader implements NativeAd.NativeAdListener
{
private static final Logger LOGGER = LoggerFactory.INSTANCE.getLogger(LoggerFactory.Type.MISC);
private static final String TAG = MyTargetAdsLoader.class.getSimpleName();
//FIXME: read correct slot from private.h
private static final int SLOT = 93418;
private static final String ZONE_KEY_PARAMETER = "_SITEZONE";
MyTargetAdsLoader(@Nullable OnAdCacheModifiedListener listener, @Nullable AdTracker tracker)
{
super(tracker, listener);
}
@Override
void requestAdForBannerId(@NonNull Context context, @NonNull String bannerId)
{
NativeAd ad = new NativeAd(SLOT, context);
ad.setListener(this);
//TODO: use parametr banner Id
ad.getCustomParams().setCustomParam(ZONE_KEY_PARAMETER, "3");
ad.load();
}
@Override
public void onLoad(NativeAd nativeAd)
{
CustomParams params = nativeAd.getCustomParams();
String bannerId = params.getData().get(ZONE_KEY_PARAMETER);
onAdLoaded(bannerId, new MyTargetNativeAd(nativeAd, SystemClock.elapsedRealtime()));
}
@Override
public void onNoAd(String s, NativeAd nativeAd)
{
LOGGER.w(TAG, "onNoAd s = " + s);
CustomParams params = nativeAd.getCustomParams();
String bannerId = params.getData().get(ZONE_KEY_PARAMETER);
onError(bannerId);
if (getAdListener() != null)
getAdListener().onError(new MyTargetNativeAd(nativeAd, 0), new MyTargetAdError(s));
}
@Override
public void onClick(NativeAd nativeAd)
{
CustomParams params = nativeAd.getCustomParams();
String bannerId = params.getData().get(ZONE_KEY_PARAMETER);
onAdClicked(bannerId);
}
}

View file

@ -0,0 +1,64 @@
package com.mapswithme.maps.ads;
import android.support.annotation.NonNull;
import android.view.View;
import android.widget.ImageView;
import com.my.target.nativeads.NativeAd;
import com.my.target.nativeads.banners.NativePromoBanner;
import com.my.target.nativeads.models.ImageData;
class MyTargetNativeAd extends CachedMwmNativeAd
{
@NonNull
private final NativeAd mAd;
MyTargetNativeAd(@NonNull NativeAd ad, long timestamp)
{
super(timestamp);
mAd = ad;
}
@NonNull
@Override
public String getTitle()
{
return mAd.getBanner().getTitle();
}
@NonNull
@Override
public String getDescription()
{
return mAd.getBanner().getDescription();
}
@NonNull
@Override
public String getAction()
{
return mAd.getBanner().getCtaText();
}
@Override
public void loadIcon(@NonNull View view)
{
NativePromoBanner banner = mAd.getBanner();
ImageData icon = banner.getIcon();
if (icon != null)
NativeAd.loadImageToView(icon, (ImageView) view);
}
@Override
public void registerView(@NonNull View bannerView)
{
mAd.registerView(bannerView);
}
@NonNull
@Override
public String getProvider()
{
return "MY_TARGET";
}
}

View file

@ -35,9 +35,9 @@ import com.mapswithme.util.ThemeUtils;
import com.mapswithme.util.UiUtils;
import com.mapswithme.util.statistics.MytargetHelper;
import com.mapswithme.util.statistics.Statistics;
import com.my.target.nativeads.banners.NativeAppwallBanner;
import com.timehop.stickyheadersrecyclerview.StickyRecyclerHeadersAdapter;
import com.timehop.stickyheadersrecyclerview.StickyRecyclerHeadersDecoration;
import ru.mail.android.mytarget.nativeads.banners.NativeAppwallBanner;
import java.util.ArrayList;
import java.util.Collection;

View file

@ -269,6 +269,7 @@ final class BannerController
@Override
public void onAdLoaded(@NonNull MwmNativeAd ad)
{
LOGGER.d(TAG, "onAdLoaded, title = " + ad.getTitle() + " provider = " + ad.getProvider());
if (mBanner == null || TextUtils.isEmpty(mBanner.getId()))
return;

View file

@ -384,7 +384,7 @@ public class PlacePageView extends RelativeLayout
if (bannerView != null)
{
DefaultAdTracker tracker = new DefaultAdTracker();
NativeAdLoader loader = Factory.createFacebookAdLoader(tracker, tracker);
NativeAdLoader loader = Factory.createMyTargetAdLoader(tracker, tracker);
mBannerController = new BannerController(bannerView, this, loader, tracker);
}

View file

@ -1,20 +1,16 @@
package com.mapswithme.util.statistics;
import android.app.Activity;
import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.WorkerThread;
import com.mapswithme.maps.MwmApplication;
import com.mapswithme.maps.PrivateVariables;
import com.mapswithme.maps.R;
import com.mapswithme.util.ConnectionState;
import com.mapswithme.util.concurrency.ThreadPool;
import com.mapswithme.util.concurrency.UiThread;
import ru.mail.android.mytarget.core.net.Hosts;
import ru.mail.android.mytarget.nativeads.NativeAppwallAd;
import ru.mail.android.mytarget.nativeads.banners.NativeAppwallBanner;
import com.my.target.nativeads.NativeAppwallAd;
import com.my.target.nativeads.banners.NativeAppwallBanner;
import java.io.IOException;
import java.net.HttpURLConnection;
@ -45,11 +41,6 @@ public final class MytargetHelper
void onDataReady(@Nullable T data);
}
static
{
Hosts.setMyComHost();
}
public MytargetHelper(final @NonNull Listener<Void> listener)
{
if (!ConnectionState.isConnected() || !isShowcaseSwitchedOnLocal())