From f5b711e09a9714a4b4c026ae3be67ba1431fbc02 Mon Sep 17 00:00:00 2001 From: Roman Romanov Date: Wed, 8 Feb 2017 15:08:33 +0400 Subject: [PATCH 1/6] [android] facebook native ads. --- android/build.gradle | 8 + .../com/mapswithme/maps/UserMarkHelper.cpp | 20 +- android/res/drawable/bg_ads.xml | 9 + android/res/drawable/bg_ads_action.xml | 13 + android/res/drawable/bg_ads_action_night.xml | 13 + android/res/drawable/bg_ads_night.xml | 9 + android/res/layout/place_page_banner.xml | 164 ++++++---- android/res/values/colors.xml | 16 +- android/res/values/dimens.xml | 7 +- android/res/values/font_sizes.xml | 1 + android/res/values/themes-attrs.xml | 7 + android/res/values/themes-base.xml | 14 + .../com/mapswithme/maps/MwmApplication.java | 9 + .../maps/bookmarks/data/Banner.java | 65 +--- .../widget/placepage/BannerController.java | 291 +++++++----------- .../BottomPlacePageAnimationController.java | 3 +- .../maps/widget/placepage/PlacePageView.java | 19 +- android/src/com/mapswithme/util/Config.java | 12 + 18 files changed, 334 insertions(+), 346 deletions(-) create mode 100644 android/res/drawable/bg_ads.xml create mode 100644 android/res/drawable/bg_ads_action.xml create mode 100644 android/res/drawable/bg_ads_action_night.xml create mode 100644 android/res/drawable/bg_ads_night.xml diff --git a/android/build.gradle b/android/build.gradle index b7ba72a8a0..a9e425b277 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -38,6 +38,7 @@ dependencies { compile 'com.android.support:cardview-v7:22.2.1' // google play services compile 'com.google.android.gms:play-services-location:8.4.0' + compile 'com.google.android.gms:play-services-ads: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' @@ -48,6 +49,7 @@ dependencies { compile('com.crashlytics.sdk.android:crashlytics-ndk:1.1.2@aar') { transitive = true } // 3-party compile 'com.facebook.android:facebook-android-sdk:4.8.0' + compile 'com.facebook.android:audience-network-sdk:4.8.0' 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' @@ -62,6 +64,9 @@ dependencies { compile 'com.github.bumptech.glide:glide:3.7.0' // Java concurrency annotations compile 'net.jcip:jcip-annotations:1.0' + + //MultiDex + compile 'com.android.support:multidex:1.0.1' } def getDate() { @@ -107,6 +112,9 @@ android { manifestPlaceholders += ['PW_APPID': pwProps['pwAppId']] buildConfigField 'String', 'PW_APPID', /"${pwProps['pwAppId']}"/ manifestPlaceholders += ['PW_PROJECT_ID': pwProps['pwProjectId']] + + //MultiDex + multiDexEnabled true } sourceSets.main { diff --git a/android/jni/com/mapswithme/maps/UserMarkHelper.cpp b/android/jni/com/mapswithme/maps/UserMarkHelper.cpp index 173f02433d..f42f4096b0 100644 --- a/android/jni/com/mapswithme/maps/UserMarkHelper.cpp +++ b/android/jni/com/mapswithme/maps/UserMarkHelper.cpp @@ -24,20 +24,12 @@ void InjectMetadata(JNIEnv * env, jclass const clazz, jobject const mapObject, f } } -jobject CreateBanner(JNIEnv * env, string const & id, string const & titleId, - string const & messageId, string const & iconId, - string const & url, vector const & sourceTypes) +jobject CreateBanner(JNIEnv * env, string const & id) { - auto const types = strings::JoinStrings(sourceTypes, ", "); - static jmethodID const bannerCtorId = jni::GetConstructorID( - env, g_bannerClazz, - "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;" - "Ljava/lang/String;)V"); + static jmethodID const bannerCtorId = + jni::GetConstructorID(env, g_bannerClazz, "(Ljava/lang/String;)V"); - return env->NewObject(g_bannerClazz, bannerCtorId, jni::ToJavaString(env, id), - jni::ToJavaString(env, titleId), jni::ToJavaString(env, messageId), - jni::ToJavaString(env, iconId), jni::ToJavaString(env, url), - jni::ToJavaString(env, types)); + return env->NewObject(g_bannerClazz, bannerCtorId, jni::ToJavaString(env, id)); } jobject CreateMapObject(JNIEnv * env, int mapObjectType, string const & title, @@ -65,9 +57,7 @@ jobject CreateMapObject(JNIEnv * env, place_page::Info const & info) { jobject jbanner = nullptr; if (info.HasBanner()) - jbanner = CreateBanner(env, info.GetBannerId(), info.GetBannerTitleId(), - info.GetBannerMessageId(), info.GetBannerIconId(), - info.GetBannerUrl(), info.GetRawTypes()); + jbanner = CreateBanner(env, info.GetBanner().m_bannerId); if (info.IsBookmark()) { // public Bookmark(@IntRange(from = 0) int categoryId, @IntRange(from = 0) int bookmarkId, diff --git a/android/res/drawable/bg_ads.xml b/android/res/drawable/bg_ads.xml new file mode 100644 index 0000000000..8f2e1d05ef --- /dev/null +++ b/android/res/drawable/bg_ads.xml @@ -0,0 +1,9 @@ + + + + + + diff --git a/android/res/drawable/bg_ads_action.xml b/android/res/drawable/bg_ads_action.xml new file mode 100644 index 0000000000..db35c9138b --- /dev/null +++ b/android/res/drawable/bg_ads_action.xml @@ -0,0 +1,13 @@ + + + + + + diff --git a/android/res/drawable/bg_ads_action_night.xml b/android/res/drawable/bg_ads_action_night.xml new file mode 100644 index 0000000000..5e255b7e39 --- /dev/null +++ b/android/res/drawable/bg_ads_action_night.xml @@ -0,0 +1,13 @@ + + + + + + diff --git a/android/res/drawable/bg_ads_night.xml b/android/res/drawable/bg_ads_night.xml new file mode 100644 index 0000000000..8759a62c7d --- /dev/null +++ b/android/res/drawable/bg_ads_night.xml @@ -0,0 +1,9 @@ + + + + + + diff --git a/android/res/layout/place_page_banner.xml b/android/res/layout/place_page_banner.xml index 236aff904f..a312a163bf 100644 --- a/android/res/layout/place_page_banner.xml +++ b/android/res/layout/place_page_banner.xml @@ -4,76 +4,110 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="@dimen/placepage_banner_height" + android:paddingLeft="@dimen/margin_base" + android:paddingRight="@dimen/margin_base" + android:paddingTop="@dimen/margin_half" android:background="?bannerBackground" + android:clipToPadding="false" tools:layout_height="wrap_content"> - - - - - - + + + + + + + + + + + + + diff --git a/android/res/values/colors.xml b/android/res/values/colors.xml index c62683f07f..216fbc36ac 100644 --- a/android/res/values/colors.xml +++ b/android/res/values/colors.xml @@ -21,11 +21,17 @@ #8A000000 #61000000 #14000000 + #3D000000 + #99000000 #FFFFFFFF #B3FFFFFF #4CFFFFFF #14FFFFFF - + #3DFFFFFF + #99FFFFFF + #FF999691 + #FF999691 + #FF757575 #FFC4C6C7 @@ -151,9 +157,13 @@ #FFFC5965 - #FFF8E1 - #555A5A + #FFFFFBF3 + #FF555A5A #28000000 #28FFFFFF + #FFF3EBDA + #4CFFFAE1 + #FFFFFBF2 + #FFFFFBF2 diff --git a/android/res/values/dimens.xml b/android/res/values/dimens.xml index 6b884600fb..49453688ef 100644 --- a/android/res/values/dimens.xml +++ b/android/res/values/dimens.xml @@ -163,9 +163,10 @@ 20dp - 46dp - 22dp - 40dp + 64dp + 40dp + 36dp + 5dp 96dp diff --git a/android/res/values/font_sizes.xml b/android/res/values/font_sizes.xml index 3e984fe207..c5f2ab54f1 100644 --- a/android/res/values/font_sizes.xml +++ b/android/res/values/font_sizes.xml @@ -16,6 +16,7 @@ 14sp 10sp 16sp + 8sp diff --git a/android/res/values/themes-attrs.xml b/android/res/values/themes-attrs.xml index 26d6719dda..5cd386ce04 100644 --- a/android/res/values/themes-attrs.xml +++ b/android/res/values/themes-attrs.xml @@ -70,11 +70,18 @@ + + + + + + + diff --git a/android/res/values/themes-base.xml b/android/res/values/themes-base.xml index aae692050f..3efddc2f2f 100644 --- a/android/res/values/themes-base.xml +++ b/android/res/values/themes-base.xml @@ -86,11 +86,18 @@ @color/text_banner_color @color/bg_banner_color + @color/bg_banner_button_color + @drawable/bg_ads + @color/text_ads_color + @drawable/bg_ads_action + @color/black_60 @color/black_secondary @drawable/bg_steady_icon @color/bg_cards @drawable/bg_active_icon + + @color/warm_gray @@ -180,10 +187,17 @@ @color/text_banner_color_night @color/bg_banner_color_night + @color/bg_banner_button_color_night + @drawable/bg_ads_night + @color/text_ads_color_night + @drawable/bg_ads_action_night + @color/white_60 @color/white_secondary @drawable/bg_steady_icon_night @color/bg_cards_night @drawable/bg_active_icon_night + + @color/warm_gray_night diff --git a/android/src/com/mapswithme/maps/MwmApplication.java b/android/src/com/mapswithme/maps/MwmApplication.java index 8889741c37..39187c785a 100644 --- a/android/src/com/mapswithme/maps/MwmApplication.java +++ b/android/src/com/mapswithme/maps/MwmApplication.java @@ -1,6 +1,7 @@ package com.mapswithme.maps; import android.app.Application; +import android.content.Context; import android.content.SharedPreferences; import android.content.pm.PackageManager.NameNotFoundException; import android.os.Environment; @@ -9,6 +10,7 @@ import android.os.Message; import android.preference.PreferenceManager; import android.support.annotation.NonNull; import android.support.annotation.UiThread; +import android.support.multidex.MultiDex; import android.text.TextUtils; import android.util.Log; @@ -167,6 +169,13 @@ public class MwmApplication extends Application Editor.init(); } + @Override + protected void attachBaseContext(Context base) + { + super.attachBaseContext(base); + MultiDex.install(this); + } + public void initNativeCore() { if (mIsFrameworkInitialized) diff --git a/android/src/com/mapswithme/maps/bookmarks/data/Banner.java b/android/src/com/mapswithme/maps/bookmarks/data/Banner.java index df6fed36d9..e0a4983921 100644 --- a/android/src/com/mapswithme/maps/bookmarks/data/Banner.java +++ b/android/src/com/mapswithme/maps/bookmarks/data/Banner.java @@ -6,7 +6,7 @@ import android.support.annotation.Nullable; public final class Banner implements Parcelable { - public static final Banner EMPTY = new Banner(null, null, null, null, null, null); + public static final Banner EMPTY = new Banner((String) null); public static final Creator CREATOR = new Creator() { @@ -25,36 +25,15 @@ public final class Banner implements Parcelable @Nullable private final String mId; - @Nullable - private final String mTitle; - @Nullable - private final String mMessage; - @Nullable - private final String mIconUrl; - @Nullable - private final String mUrl; - @Nullable - private final String mTypes; - public Banner(@Nullable String id, @Nullable String title, @Nullable String message, - @Nullable String iconUrl, @Nullable String url, @Nullable String types) + public Banner(@Nullable String id) { mId = id; - mTitle = title; - mMessage = message; - mIconUrl = iconUrl; - mUrl = url; - mTypes = types; } protected Banner(Parcel in) { mId = in.readString(); - mTitle = in.readString(); - mMessage = in.readString(); - mIconUrl = in.readString(); - mUrl = in.readString(); - mTypes = in.readString(); } @Nullable @@ -63,36 +42,6 @@ public final class Banner implements Parcelable return mId; } - @Nullable - public String getTitle() - { - return mTitle; - } - - @Nullable - public String getMessage() - { - return mMessage; - } - - @Nullable - public String getIconUrl() - { - return mIconUrl; - } - - @Nullable - public String getUrl() - { - return mUrl; - } - - @Nullable - public String getTypes() - { - return mTypes; - } - @Override public int describeContents() { @@ -103,11 +52,6 @@ public final class Banner implements Parcelable public void writeToParcel(Parcel dest, int flags) { dest.writeString(mId); - dest.writeString(mTitle); - dest.writeString(mMessage); - dest.writeString(mIconUrl); - dest.writeString(mUrl); - dest.writeString(mTypes); } @Override @@ -115,11 +59,6 @@ public final class Banner implements Parcelable { return "Banner{" + "mId='" + mId + '\'' + - ", mTitle='" + mTitle + '\'' + - ", mMessage='" + mMessage + '\'' + - ", mIconUrl='" + mIconUrl + '\'' + - ", mUrl='" + mUrl + '\'' + - ", mTypes='" + mTypes + '\'' + '}'; } } diff --git a/android/src/com/mapswithme/maps/widget/placepage/BannerController.java b/android/src/com/mapswithme/maps/widget/placepage/BannerController.java index 04d9b13be0..0003206437 100644 --- a/android/src/com/mapswithme/maps/widget/placepage/BannerController.java +++ b/android/src/com/mapswithme/maps/widget/placepage/BannerController.java @@ -1,40 +1,44 @@ package com.mapswithme.maps.widget.placepage; -import android.animation.Animator; -import android.animation.ValueAnimator; import android.content.res.Resources; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.text.TextUtils; +import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; -import com.bumptech.glide.Glide; -import com.bumptech.glide.load.resource.drawable.GlideDrawable; -import com.bumptech.glide.request.RequestListener; -import com.bumptech.glide.request.target.Target; -import com.mapswithme.maps.MwmApplication; +import com.facebook.ads.Ad; +import com.facebook.ads.AdError; +import com.facebook.ads.AdListener; +import com.facebook.ads.AdSettings; +import com.facebook.ads.NativeAd; +import com.mapswithme.maps.BuildConfig; import com.mapswithme.maps.R; import com.mapswithme.maps.bookmarks.data.Banner; -import com.mapswithme.util.ConnectionState; -import com.mapswithme.util.CrashlyticsUtils; +import com.mapswithme.util.Config; import com.mapswithme.util.UiUtils; +import com.mapswithme.util.log.Logger; +import com.mapswithme.util.log.LoggerFactory; import com.mapswithme.util.statistics.Statistics; +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.List; + import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; import static com.mapswithme.util.SharedPropertiesUtils.isShowcaseSwitchedOnLocal; -final class BannerController implements View.OnClickListener +final class BannerController implements AdListener { - private static final int DURATION_DEFAULT = - MwmApplication.get().getResources().getInteger(R.integer.anim_default); + private static final Logger LOGGER = LoggerFactory.INSTANCE + .getLogger(LoggerFactory.Type.MISC); + private static final String TAG = BannerController.class.getName(); @Nullable private Banner mBanner; - @Nullable - private OnBannerClickListener mListener; @NonNull private final View mFrame; @@ -45,84 +49,44 @@ final class BannerController implements View.OnClickListener @Nullable private final TextView mMessage; @Nullable - private final View mAdMarker; + private final TextView mActionSmall; + @Nullable + private final TextView mActionLarge; private final float mCloseFrameHeight; - private final float mCloseIconSize; - private final float mOpenIconSize; - private final float mMarginBase; - private final float mMarginHalfPlus; - @NonNull - private final Resources mResources; + @Nullable + private NativeAd mNativeAd; private boolean mOpened = false; - @Nullable - private ValueAnimator mIconAnimator; - - BannerController(@NonNull View bannerView, @Nullable OnBannerClickListener listener) + BannerController(@NonNull View bannerView) { mFrame = bannerView; - mFrame.setOnClickListener(this); - mListener = listener; - mResources = mFrame.getResources(); - mCloseFrameHeight = mResources.getDimension(R.dimen.placepage_banner_height); - mCloseIconSize = mResources.getDimension(R.dimen.placepage_banner_icon_size); - mOpenIconSize = mResources.getDimension(R.dimen.placepage_banner_icon_size_full); - mMarginBase = mResources.getDimension(R.dimen.margin_base); - mMarginHalfPlus = mResources.getDimension(R.dimen.margin_half_plus); + Resources resources = mFrame.getResources(); + mCloseFrameHeight = resources.getDimension(R.dimen.placepage_banner_height); mIcon = (ImageView) bannerView.findViewById(R.id.iv__banner_icon); mTitle = (TextView) bannerView.findViewById(R.id.tv__banner_title); mMessage = (TextView) bannerView.findViewById(R.id.tv__banner_message); - mAdMarker = bannerView.findViewById(R.id.tv__banner); + mActionSmall = (TextView) bannerView.findViewById(R.id.tv__action_small); + mActionLarge = (TextView) bannerView.findViewById(R.id.tv__action_large); } void updateData(@Nullable Banner banner) { + UiUtils.hide(mFrame); + mBanner = banner; - boolean showBanner = banner != null && ConnectionState.isConnected() - && isShowcaseSwitchedOnLocal(); - UiUtils.showIf(showBanner, mFrame); - if (!showBanner) + if (mBanner == null || TextUtils.isEmpty(mBanner.getId()) || !isShowcaseSwitchedOnLocal() + || Config.getAdForbidden()) return; - loadIcon(banner); - setLabelSafely(mTitle, mBanner.getTitle()); - setLabelSafely(mMessage, mBanner.getMessage()); - - if (UiUtils.isLandscape(mFrame.getContext())) - open(); - else - Statistics.INSTANCE.trackEvent(Statistics.EventName.PP_BANNER_SHOW, - Statistics.params() - .add("tags:", mBanner.getTypes()) - .add("banner:", mBanner.getId()) - .add("state:", "0")); - } - - private void setLabelSafely(@Nullable TextView label, @Nullable String labelId) - { - if (label == null) - return; - - if (TextUtils.isEmpty(labelId)) - { - CrashlyticsUtils.logException(new Resources.NotFoundException("An empty string id obtained for: " - + mBanner)); - return; - } - - try - { - String packageName = mFrame.getContext().getPackageName(); - String value = mResources.getString(mResources.getIdentifier(labelId, "string", packageName)); - label.setText(value); - } - catch (Resources.NotFoundException e) - { - CrashlyticsUtils.logException(new IllegalStateException("Unknown banner is found: " + mBanner, e)); - } + if (BuildConfig.DEBUG) + AdSettings.addTestDevice("21d362c12af63896f61c16f10d08214d"); + mNativeAd = new NativeAd(mFrame.getContext(), mBanner.getId()); + mNativeAd.setAdListener(BannerController.this); + mNativeAd.loadAd(EnumSet.of(NativeAd.MediaCacheFlag.ICON)); + UiUtils.show(mFrame); } boolean isShowing() @@ -132,50 +96,43 @@ final class BannerController implements View.OnClickListener void open() { - if (!isShowing() || mBanner == null || mOpened) + if (!isShowing() || mNativeAd == null || mBanner == null || mOpened) return; mOpened = true; setFrameHeight(WRAP_CONTENT); - setIconParams(mOpenIconSize, 0, mMarginBase, new Runnable() - { - @Override - public void run() - { - loadIcon(mBanner); - } - }); - UiUtils.show(mMessage, mAdMarker); + loadIcon(mNativeAd); + if (mMessage != null) + mMessage.setMaxLines(Integer.MAX_VALUE); if (mTitle != null) mTitle.setMaxLines(2); + if (mActionSmall != null) + UiUtils.hide(mActionSmall); + if (mActionLarge != null) + UiUtils.show(mActionLarge); Statistics.INSTANCE.trackEvent(Statistics.EventName.PP_BANNER_SHOW, Statistics.params() - .add("tags:", mBanner.getTypes()) .add("banner:", mBanner.getId()) .add("state:", "1")); } boolean close() { - if (!isShowing() || mBanner == null || !mOpened) + if (!isShowing() || mNativeAd == null || mBanner == null || !mOpened) return false; mOpened = false; setFrameHeight((int) mCloseFrameHeight); - setIconParams(mCloseIconSize, mMarginBase, mMarginHalfPlus, new Runnable() - { - @Override - public void run() - { - loadIcon(mBanner); - } - }); - UiUtils.hide(mMessage, mAdMarker); + UiUtils.hide(mIcon); + if (mMessage != null) + mMessage.setMaxLines(3); if (mTitle != null) mTitle.setMaxLines(1); - - mFrame.setOnClickListener(null); + if (mActionSmall != null) + UiUtils.show(mActionSmall); + if (mActionLarge != null) + UiUtils.hide(mActionLarge); return true; } @@ -192,112 +149,74 @@ final class BannerController implements View.OnClickListener mFrame.setLayoutParams(lp); } - private void setIconParams(final float size, final float marginRight, final float marginTop, - final @Nullable Runnable listener) - { - if (mIcon == null || UiUtils.isHidden(mIcon)) - { - if (listener != null) - listener.run(); - return; - } - - if (mIconAnimator != null) - mIconAnimator.cancel(); - - final ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) mIcon.getLayoutParams(); - final float startSize = lp.height; - final float startRight = lp.rightMargin; - final float startTop = lp.topMargin; - mIconAnimator = ValueAnimator.ofFloat(0.0f, 1.0f); - if (mIconAnimator == null) - { - if (listener != null) - listener.run(); - return; - } - - mIconAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() - { - @Override - public void onAnimationUpdate(ValueAnimator animation) - { - float t = (float) animation.getAnimatedValue(); - int newSize = (int) (startSize + t * (size - startSize)); - lp.height = newSize; - lp.width = newSize; - lp.rightMargin = (int) (startRight + t * (marginRight - startRight)); - lp.topMargin = (int) (startTop + t * (marginTop - startTop)); - mIcon.setLayoutParams(lp); - } - }); - mIconAnimator.addListener(new UiUtils.SimpleAnimatorListener() - { - @Override - public void onAnimationEnd(Animator animation) - { - if (listener != null) - listener.run(); - } - }); - mIconAnimator.setDuration(DURATION_DEFAULT); - mIconAnimator.start(); - } - - private void loadIcon(@NonNull Banner banner) + private void loadIcon(@NonNull NativeAd nativeAd) { if (mIcon == null) return; - if (TextUtils.isEmpty(banner.getIconUrl())) - { - UiUtils.hide(mIcon); - return; - } - - Glide.with(mIcon.getContext()) - .load(banner.getIconUrl()) - .centerCrop() - .listener(new RequestListener() - { - @Override - public boolean onException(Exception e, String model, Target target, - boolean isFirstResource) - { - UiUtils.hide(mIcon); - return false; - } - - @Override - public boolean onResourceReady(GlideDrawable resource, String model, - Target target, boolean isFromMemoryCache, - boolean isFirstResource) - { - UiUtils.show(mIcon); - return false; - } - }) - .into(mIcon); + UiUtils.show(mIcon); + NativeAd.Image icon = nativeAd.getAdIcon(); + NativeAd.downloadAndDisplayImage(icon, mIcon); } @Override - public void onClick(View v) + public void onError(Ad ad, AdError adError) { - if (mListener == null || mBanner == null) + UiUtils.hide(mFrame); + LOGGER.e(TAG, adError.getErrorMessage()); + } + + @Override + public void onAdLoaded(Ad ad) + { + if (mNativeAd == null || mBanner == null) return; - if (mOpened) - mListener.onBannerClick(mBanner); + if (mTitle != null) + mTitle.setText(mNativeAd.getAdTitle()); + if (mMessage != null) + mMessage.setText(mNativeAd.getAdBody()); + if (mActionSmall != null) + mActionSmall.setText(mNativeAd.getAdCallToAction()); + if (mActionLarge != null) + mActionLarge.setText(mNativeAd.getAdCallToAction()); + + List clickableViews = new ArrayList<>(); + clickableViews.add(mTitle); + clickableViews.add(mActionSmall); + clickableViews.add(mActionLarge); + mNativeAd.registerViewForInteraction(mFrame, clickableViews); + + if (UiUtils.isLandscape(mFrame.getContext())) + { + open(); + } + else + { + close(); + Statistics.INSTANCE.trackEvent(Statistics.EventName.PP_BANNER_SHOW, + Statistics.params() + .add("banner:", mBanner.getId()) + .add("state:", "0")); + } + } + + @Override + public void onAdClicked(Ad ad) + { + if (mBanner == null) + return; Statistics.INSTANCE.trackEvent(Statistics.EventName.PP_BANNER_CLICK, Statistics.params() - .add("tags:", mBanner.getTypes()) .add("banner:", mBanner.getId()) .add("state:", mOpened ? "1" : "0")); } - interface OnBannerClickListener + boolean isTouchActionButton(@NonNull MotionEvent event) { - void onBannerClick(@NonNull Banner banner); + return (mActionSmall != null && !UiUtils.isHidden(mActionSmall) && UiUtils.isViewTouched(event, mActionSmall)) + || (mActionLarge != null && !UiUtils.isHidden(mActionLarge) && UiUtils.isViewTouched(event, mActionLarge)) + || (mTitle != null && !UiUtils.isHidden(mTitle) && UiUtils.isViewTouched(event, mTitle)); } } diff --git a/android/src/com/mapswithme/maps/widget/placepage/BottomPlacePageAnimationController.java b/android/src/com/mapswithme/maps/widget/placepage/BottomPlacePageAnimationController.java index 65d98991af..cc959dfbb7 100644 --- a/android/src/com/mapswithme/maps/widget/placepage/BottomPlacePageAnimationController.java +++ b/android/src/com/mapswithme/maps/widget/placepage/BottomPlacePageAnimationController.java @@ -99,7 +99,8 @@ class BottomPlacePageAnimationController extends BasePlacePageAnimationControlle break; case MotionEvent.ACTION_UP: final boolean isInside = UiUtils.isViewTouched(event, mDetailsScroll); - if (isInside && mIsGestureStartedInsideView) + final boolean isBannerTouch = mPlacePage.isTouchBannerAction(event); + if (isInside && !isBannerTouch && mIsGestureStartedInsideView) mGestureDetector.onTouchEvent(event); break; } diff --git a/android/src/com/mapswithme/maps/widget/placepage/PlacePageView.java b/android/src/com/mapswithme/maps/widget/placepage/PlacePageView.java index 9d427db16f..d15a89dd3a 100644 --- a/android/src/com/mapswithme/maps/widget/placepage/PlacePageView.java +++ b/android/src/com/mapswithme/maps/widget/placepage/PlacePageView.java @@ -43,7 +43,6 @@ import com.mapswithme.maps.MwmActivity; import com.mapswithme.maps.MwmApplication; import com.mapswithme.maps.R; import com.mapswithme.maps.api.ParsedMwmRequest; -import com.mapswithme.maps.bookmarks.data.Banner; import com.mapswithme.maps.bookmarks.data.Bookmark; import com.mapswithme.maps.bookmarks.data.BookmarkManager; import com.mapswithme.maps.bookmarks.data.DistanceAndAzimut; @@ -98,7 +97,6 @@ public class PlacePageView extends RelativeLayout LineCountTextView.OnLineCountCalculatedListener, RecyclerClickListener, NearbyAdapter.OnItemClickListener, - BannerController.OnBannerClickListener, BottomPlacePageAnimationController.OnBannerOpenListener, EditBookmarkFragment.EditBookmarkListener { @@ -356,7 +354,7 @@ public class PlacePageView extends RelativeLayout View bannerView = findViewById(R.id.banner); if (bannerView != null) - mBannerController = new BannerController(bannerView, this); + mBannerController = new BannerController(bannerView); mButtons = new PlacePageButtons(this, ppButtons, new PlacePageButtons.ItemListener() { @@ -744,13 +742,6 @@ public class PlacePageView extends RelativeLayout }); } - @Override - public void onBannerClick(@NonNull Banner banner) - { - if (!TextUtils.isEmpty(banner.getUrl())) - followUrl(banner.getUrl()); - } - @Override public void onNeedOpenBanner() { @@ -1622,4 +1613,12 @@ public class PlacePageView extends RelativeLayout { setMapObject(BookmarkManager.INSTANCE.getBookmark(categoryId, bookmarkId), true, null); } + + public boolean isTouchBannerAction(@NonNull MotionEvent event) + { + if (mBannerController != null) + return mBannerController.isTouchActionButton(event); + + return false; + } } diff --git a/android/src/com/mapswithme/util/Config.java b/android/src/com/mapswithme/util/Config.java index d5c691b2cd..59e0038f05 100644 --- a/android/src/com/mapswithme/util/Config.java +++ b/android/src/com/mapswithme/util/Config.java @@ -36,6 +36,7 @@ public final class Config private static final String KEY_MISC_USE_MOBILE_DATA = "UseMobileData"; private static final String KEY_MISC_USE_MOBILE_DATA_TIMESTAMP = "UseMobileDataTimestamp"; private static final String KEY_MISC_USE_MOBILE_DATA_ROAMING = "UseMobileDataRoaming"; + private static final String KEY_MISC_AD_FORBIDDEN = "AdForbidden"; private Config() {} @@ -383,6 +384,16 @@ public final class Config setBool(KEY_MISC_USE_MOBILE_DATA_ROAMING, ConnectionState.isInRoaming()); } + public static boolean getAdForbidden() + { + return getBool(KEY_MISC_AD_FORBIDDEN, false); + } + + public static void setAdForbidden(boolean value) + { + setBool(KEY_MISC_AD_FORBIDDEN, value); + } + public static void setMobileDataTimeStamp(long timestamp) { setLong(KEY_MISC_USE_MOBILE_DATA_TIMESTAMP, timestamp); @@ -398,6 +409,7 @@ public final class Config return getBool(KEY_MISC_USE_MOBILE_DATA_ROAMING, false); } + private static native boolean nativeGetBoolean(String name, boolean defaultValue); private static native void nativeSetBoolean(String name, boolean value); private static native int nativeGetInt(String name, int defaultValue); From 2af91106f1751209570db3f128f90eaf9afc7f4d Mon Sep 17 00:00:00 2001 From: Roman Romanov Date: Fri, 10 Feb 2017 15:01:38 +0400 Subject: [PATCH 2/6] [android] Exclude com.google.android.gms group from facebook audience network artifact. Remove multidex. --- android/build.gradle | 11 +++-------- android/src/com/mapswithme/maps/MwmApplication.java | 9 --------- 2 files changed, 3 insertions(+), 17 deletions(-) diff --git a/android/build.gradle b/android/build.gradle index a9e425b277..4a467e14bf 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -38,7 +38,6 @@ dependencies { compile 'com.android.support:cardview-v7:22.2.1' // google play services compile 'com.google.android.gms:play-services-location:8.4.0' - compile 'com.google.android.gms:play-services-ads: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' @@ -49,7 +48,9 @@ dependencies { compile('com.crashlytics.sdk.android:crashlytics-ndk:1.1.2@aar') { transitive = true } // 3-party compile 'com.facebook.android:facebook-android-sdk:4.8.0' - compile 'com.facebook.android:audience-network-sdk:4.8.0' + compile('com.facebook.android:audience-network-sdk:4.8.0') { + exclude group: 'com.google.android.gms' + } 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' @@ -64,9 +65,6 @@ dependencies { compile 'com.github.bumptech.glide:glide:3.7.0' // Java concurrency annotations compile 'net.jcip:jcip-annotations:1.0' - - //MultiDex - compile 'com.android.support:multidex:1.0.1' } def getDate() { @@ -112,9 +110,6 @@ android { manifestPlaceholders += ['PW_APPID': pwProps['pwAppId']] buildConfigField 'String', 'PW_APPID', /"${pwProps['pwAppId']}"/ manifestPlaceholders += ['PW_PROJECT_ID': pwProps['pwProjectId']] - - //MultiDex - multiDexEnabled true } sourceSets.main { diff --git a/android/src/com/mapswithme/maps/MwmApplication.java b/android/src/com/mapswithme/maps/MwmApplication.java index 39187c785a..8889741c37 100644 --- a/android/src/com/mapswithme/maps/MwmApplication.java +++ b/android/src/com/mapswithme/maps/MwmApplication.java @@ -1,7 +1,6 @@ package com.mapswithme.maps; import android.app.Application; -import android.content.Context; import android.content.SharedPreferences; import android.content.pm.PackageManager.NameNotFoundException; import android.os.Environment; @@ -10,7 +9,6 @@ import android.os.Message; import android.preference.PreferenceManager; import android.support.annotation.NonNull; import android.support.annotation.UiThread; -import android.support.multidex.MultiDex; import android.text.TextUtils; import android.util.Log; @@ -169,13 +167,6 @@ public class MwmApplication extends Application Editor.init(); } - @Override - protected void attachBaseContext(Context base) - { - super.attachBaseContext(base); - MultiDex.install(this); - } - public void initNativeCore() { if (mIsFrameworkInitialized) From ab20426626c29e17291a368bba473c1e67285bd4 Mon Sep 17 00:00:00 2001 From: Roman Romanov Date: Mon, 13 Feb 2017 09:10:08 +0400 Subject: [PATCH 3/6] [android] Review fixes --- android/res/layout/place_page_banner.xml | 3 +-- android/res/values/font_sizes.xml | 1 - .../widget/placepage/BannerController.java | 23 ++++++++++++------- .../BottomPlacePageAnimationController.java | 2 +- .../maps/widget/placepage/PlacePageView.java | 9 +++----- 5 files changed, 20 insertions(+), 18 deletions(-) diff --git a/android/res/layout/place_page_banner.xml b/android/res/layout/place_page_banner.xml index a312a163bf..b9ba0c621f 100644 --- a/android/res/layout/place_page_banner.xml +++ b/android/res/layout/place_page_banner.xml @@ -53,7 +53,6 @@ android:layout_height="wrap_content" android:layout_toEndOf="@id/iv__banner_icon" android:layout_toRightOf="@id/iv__banner_icon" - android:gravity="center_vertical"> 14sp 10sp 16sp - 8sp diff --git a/android/src/com/mapswithme/maps/widget/placepage/BannerController.java b/android/src/com/mapswithme/maps/widget/placepage/BannerController.java index 0003206437..d119055004 100644 --- a/android/src/com/mapswithme/maps/widget/placepage/BannerController.java +++ b/android/src/com/mapswithme/maps/widget/placepage/BannerController.java @@ -37,6 +37,11 @@ final class BannerController implements AdListener .getLogger(LoggerFactory.Type.MISC); private static final String TAG = BannerController.class.getName(); + private static boolean isTouched(@Nullable View view, @NonNull MotionEvent event) + { + return view != null && !UiUtils.isHidden(view) && UiUtils.isViewTouched(event, view); + } + @Nullable private Banner mBanner; @@ -82,21 +87,24 @@ final class BannerController implements AdListener return; if (BuildConfig.DEBUG) - AdSettings.addTestDevice("21d362c12af63896f61c16f10d08214d"); + { + AdSettings.addTestDevice("c36b141fff9e11866d8cf9c601d2b7e0"); + AdSettings.addTestDevice("f73c8b5221b977caba1b60cd7a966587"); + } mNativeAd = new NativeAd(mFrame.getContext(), mBanner.getId()); mNativeAd.setAdListener(BannerController.this); mNativeAd.loadAd(EnumSet.of(NativeAd.MediaCacheFlag.ICON)); UiUtils.show(mFrame); } - boolean isShowing() + boolean isBannerVisible() { return !UiUtils.isHidden(mFrame); } void open() { - if (!isShowing() || mNativeAd == null || mBanner == null || mOpened) + if (!isBannerVisible() || mNativeAd == null || mBanner == null || mOpened) return; mOpened = true; @@ -119,7 +127,7 @@ final class BannerController implements AdListener boolean close() { - if (!isShowing() || mNativeAd == null || mBanner == null || !mOpened) + if (!isBannerVisible() || mNativeAd == null || mBanner == null || !mOpened) return false; mOpened = false; @@ -213,10 +221,9 @@ final class BannerController implements AdListener .add("state:", mOpened ? "1" : "0")); } - boolean isTouchActionButton(@NonNull MotionEvent event) + boolean isActionButtonTouched(@NonNull MotionEvent event) { - return (mActionSmall != null && !UiUtils.isHidden(mActionSmall) && UiUtils.isViewTouched(event, mActionSmall)) - || (mActionLarge != null && !UiUtils.isHidden(mActionLarge) && UiUtils.isViewTouched(event, mActionLarge)) - || (mTitle != null && !UiUtils.isHidden(mTitle) && UiUtils.isViewTouched(event, mTitle)); + return isTouched(mActionSmall, event) || isTouched(mActionLarge, event) + || isTouched(mTitle, event); } } diff --git a/android/src/com/mapswithme/maps/widget/placepage/BottomPlacePageAnimationController.java b/android/src/com/mapswithme/maps/widget/placepage/BottomPlacePageAnimationController.java index cc959dfbb7..e3226885b1 100644 --- a/android/src/com/mapswithme/maps/widget/placepage/BottomPlacePageAnimationController.java +++ b/android/src/com/mapswithme/maps/widget/placepage/BottomPlacePageAnimationController.java @@ -99,7 +99,7 @@ class BottomPlacePageAnimationController extends BasePlacePageAnimationControlle break; case MotionEvent.ACTION_UP: final boolean isInside = UiUtils.isViewTouched(event, mDetailsScroll); - final boolean isBannerTouch = mPlacePage.isTouchBannerAction(event); + final boolean isBannerTouch = mPlacePage.isBannerTouched(event); if (isInside && !isBannerTouch && mIsGestureStartedInsideView) mGestureDetector.onTouchEvent(event); break; diff --git a/android/src/com/mapswithme/maps/widget/placepage/PlacePageView.java b/android/src/com/mapswithme/maps/widget/placepage/PlacePageView.java index d15a89dd3a..1423574bab 100644 --- a/android/src/com/mapswithme/maps/widget/placepage/PlacePageView.java +++ b/android/src/com/mapswithme/maps/widget/placepage/PlacePageView.java @@ -839,7 +839,7 @@ public class PlacePageView extends RelativeLayout if (!mIsDocked && !mIsFloating) { // After ninepatch background is set from code, all paddings are lost, so we need to restore it later. - int bottom = mBannerController != null && mBannerController.isShowing() + int bottom = mBannerController != null && mBannerController.isBannerVisible() ? 0 : (int) getResources().getDimension(R.dimen.margin_base); int left = mPreview.getPaddingLeft(); int right = mPreview.getPaddingRight(); @@ -1614,11 +1614,8 @@ public class PlacePageView extends RelativeLayout setMapObject(BookmarkManager.INSTANCE.getBookmark(categoryId, bookmarkId), true, null); } - public boolean isTouchBannerAction(@NonNull MotionEvent event) + public boolean isBannerTouched(@NonNull MotionEvent event) { - if (mBannerController != null) - return mBannerController.isTouchActionButton(event); - - return false; + return mBannerController != null && mBannerController.isActionButtonTouched(event); } } From 4f679e7c9a4b4b17b6414276637c5a55520af93c Mon Sep 17 00:00:00 2001 From: Roman Romanov Date: Wed, 15 Feb 2017 12:28:24 +0400 Subject: [PATCH 4/6] [android] Added facebook native ad test devices. --- .../mapswithme/maps/widget/placepage/BannerController.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/android/src/com/mapswithme/maps/widget/placepage/BannerController.java b/android/src/com/mapswithme/maps/widget/placepage/BannerController.java index d119055004..d5cad504cd 100644 --- a/android/src/com/mapswithme/maps/widget/placepage/BannerController.java +++ b/android/src/com/mapswithme/maps/widget/placepage/BannerController.java @@ -90,6 +90,11 @@ final class BannerController implements AdListener { AdSettings.addTestDevice("c36b141fff9e11866d8cf9c601d2b7e0"); AdSettings.addTestDevice("f73c8b5221b977caba1b60cd7a966587"); + AdSettings.addTestDevice("36dd04f33c4cf92e3b7d21e9a5a9d985"); + AdSettings.addTestDevice("b39d3c00580d17b291ff4e161a423525"); + AdSettings.addTestDevice("51bae0d8b2ba8290659840f9098e3026"); + AdSettings.addTestDevice("583a068aa7293c6743855343251e1fad"); + AdSettings.addTestDevice("c1c9f7f1c06b8c2c4e15607e5c766cb3"); } mNativeAd = new NativeAd(mFrame.getContext(), mBanner.getId()); mNativeAd.setAdListener(BannerController.this); From 534e638bc6302ffd565693a3f10eda5271d6e439 Mon Sep 17 00:00:00 2001 From: Roman Romanov Date: Fri, 17 Feb 2017 10:58:02 +0400 Subject: [PATCH 5/6] [android] fix banner restore state after ad is loaded. --- .../maps/widget/placepage/BannerController.java | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/android/src/com/mapswithme/maps/widget/placepage/BannerController.java b/android/src/com/mapswithme/maps/widget/placepage/BannerController.java index d5cad504cd..0bb0d99213 100644 --- a/android/src/com/mapswithme/maps/widget/placepage/BannerController.java +++ b/android/src/com/mapswithme/maps/widget/placepage/BannerController.java @@ -89,7 +89,7 @@ final class BannerController implements AdListener if (BuildConfig.DEBUG) { AdSettings.addTestDevice("c36b141fff9e11866d8cf9c601d2b7e0"); - AdSettings.addTestDevice("f73c8b5221b977caba1b60cd7a966587"); + AdSettings.addTestDevice("189055740336d9d2687f41a775eaf867"); AdSettings.addTestDevice("36dd04f33c4cf92e3b7d21e9a5a9d985"); AdSettings.addTestDevice("b39d3c00580d17b291ff4e161a423525"); AdSettings.addTestDevice("51bae0d8b2ba8290659840f9098e3026"); @@ -202,9 +202,12 @@ final class BannerController implements AdListener if (UiUtils.isLandscape(mFrame.getContext())) { - open(); + if (!mOpened) + open(); + else + loadIcon(mNativeAd); } - else + else if (!mOpened) { close(); Statistics.INSTANCE.trackEvent(Statistics.EventName.PP_BANNER_SHOW, @@ -212,6 +215,10 @@ final class BannerController implements AdListener .add("banner:", mBanner.getId()) .add("state:", "0")); } + else + { + loadIcon(mNativeAd); + } } @Override From 9bb0c0848d5ff3b9892c0b6f77f06ef2c9d796d9 Mon Sep 17 00:00:00 2001 From: Roman Romanov Date: Fri, 17 Feb 2017 16:18:57 +0400 Subject: [PATCH 6/6] [android] Update banner controller only from setMapObject() method. --- .../src/com/mapswithme/maps/MwmActivity.java | 5 ++- .../maps/widget/placepage/PlacePageView.java | 36 ++++++++++++++----- 2 files changed, 30 insertions(+), 11 deletions(-) diff --git a/android/src/com/mapswithme/maps/MwmActivity.java b/android/src/com/mapswithme/maps/MwmActivity.java index 90d6ae5e61..61f6ca5ede 100644 --- a/android/src/com/mapswithme/maps/MwmActivity.java +++ b/android/src/com/mapswithme/maps/MwmActivity.java @@ -89,7 +89,6 @@ import com.mapswithme.maps.widget.placepage.PlacePageView.State; import com.mapswithme.util.Animations; import com.mapswithme.util.BottomSheetHelper; import com.mapswithme.util.InputUtils; -import com.mapswithme.util.NetworkPolicy; import com.mapswithme.util.ThemeUtils; import com.mapswithme.util.UiUtils; import com.mapswithme.util.Utils; @@ -1597,7 +1596,7 @@ public class MwmActivity extends BaseMwmFragmentActivity updateSearchBar(); } - mPlacePage.refreshViews(null); + mPlacePage.refreshViews(); } private void adjustCompassAndTraffic(int offsetY) @@ -1654,7 +1653,7 @@ public class MwmActivity extends BaseMwmFragmentActivity @Override public void showNavigation(boolean show) { - mPlacePage.refreshViews(null); + mPlacePage.refreshViews(); if (mNavigationController != null) mNavigationController.show(show); refreshFade(); diff --git a/android/src/com/mapswithme/maps/widget/placepage/PlacePageView.java b/android/src/com/mapswithme/maps/widget/placepage/PlacePageView.java index 1423574bab..d92577aebe 100644 --- a/android/src/com/mapswithme/maps/widget/placepage/PlacePageView.java +++ b/android/src/com/mapswithme/maps/widget/placepage/PlacePageView.java @@ -932,13 +932,28 @@ public class PlacePageView extends RelativeLayout return mMapObject != null && (isSponsored() || mMapObject.getBanner() != null); } - public void refreshViews(@Nullable NetworkPolicy policy) + private void refreshViews(@NonNull NetworkPolicy policy) { if (mMapObject == null) return; refreshPreview(policy); + refreshViewsInternal(); + } + + public void refreshViews() + { + if (mMapObject == null) + return; + + refreshPreview(); + refreshViewsInternal(); + } + + private void refreshViewsInternal() + { refreshDetails(); + final Location loc = LocationHelper.INSTANCE.getSavedLocation(); switch (mMapObject.getMapObjectType()) @@ -995,7 +1010,18 @@ public class PlacePageView extends RelativeLayout } } - private void refreshPreview(@Nullable NetworkPolicy policy) + private void refreshPreview(@NonNull NetworkPolicy policy) + { + if (mBannerController != null) + { + mBannerController.updateData(policy.сanUseNetwork() + ? mMapObject.getBanner() : null); + } + + refreshPreview(); + } + + private void refreshPreview() { UiUtils.setTextAndHideIfEmpty(mTvTitle, mMapObject.getTitle()); if (mToolbar != null) @@ -1016,12 +1042,6 @@ public class PlacePageView extends RelativeLayout mTvSponsoredPrice.setText(mSponsoredPrice); UiUtils.showIf(!isPriceEmpty, mTvSponsoredPrice); } - - if (mBannerController != null) - { - mBannerController.updateData(policy != null && policy.сanUseNetwork() - ? mMapObject.getBanner() : null); - } } private boolean isSponsored()