Giter Site home page Giter Site logo

mopub-nativead-adapters's Introduction

MoPub Native Ad Adapters

JitPack Build Status Code Quality Code Coverage Dependency Status Android API License

Custom adapters for MoPub native ads.

Screenshot

Supported Adapters

Introduction

This is an Android library ready for most use cases. You can start getting working ads right away. If you want to understand what the implementation behind it, or you simply prefer to create custom adapters from scratch, please follow the instructions in these articles:

Gradle

Step 1

Add jitpack.io repository in your root project build.gradle:

allprojects {
    repositories {
        ...
        maven { url 'https://jitpack.io' }
    }
}

Step 2

Add the dependencies to your application project:

dependencies {
    ...
    compile 'com.github.ayltai:mopub-nativead-adapters:1.0.1'
}

AndroidManifest.xml

Permissions

<manifest
    xmlns:android="http://schemas.android.com/apk/res/android"
    package="org.github.ayltai.mopub.adapter.app">
    ...
    <!-- Required -->
    <uses-permission android:name="android.permission.INTERNET" />

    <!-- Required by Facebook Audience Network and Flurry Ads -->
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

    <!-- Required by AppLovin -->
    <!-- Recommended for external memory pre-caching -->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

    <!-- Optional -->
    <!-- Recommended for location based ad targeting -->
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    ...
</manifest>

Activities

<manifest
    xmlns:android="http://schemas.android.com/apk/res/android"
    package="org.github.ayltai.mopub.adapter.app">
    ...
    <!-- MoPub -->
    <activity
        android:name="com.mopub.mobileads.MoPubActivity"
        android:configChanges="keyboardHidden|orientation|screenSize" />
    <activity
        android:name="com.mopub.common.MoPubBrowser"
        android:configChanges="keyboardHidden|orientation|screenSize" />

    <!-- Facebook Audience Network -->
    <activity
        android:name="com.facebook.ads.AudienceNetworkActivity"
        android:configChanges="keyboardHidden|orientation|screenSize" />

    <!-- Flurry Ads -->
    <activity
        android:name="com.flurry.android.FlurryFullscreenTakeoverActivity"
        android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize" />

    <!-- AppLovin -->
    <activity android:name="com.applovin.adview.AppLovinInterstitialActivity" />
    <activity android:name="com.applovin.adview.AppLovinConfirmationActivity" />
    ...
</manifest>

ProGuard

You need to update your ProGuard rules in order to successfully make a release build of your application using this library. Please check proguard-rules.pro included in the sample application.

MoPub Administrator Console

You need to configure ad network mediation on MoPub Administrator Console.

Note: You need to disable all ad-blockers addons of your browser in order to perform the following steps successfully.

Preparations

First you need to setup your apps, ad units, orders, marketplace, etc. For details, please check the official documentation.

If you plan to mediate other ad networks, please check the followings accordingly:

MoPub Ad Networks

The most important part of the setup is Network.

Step 1: Create a Custom Native Network

Add a "Custom Native" network by clicking this button:

Add a Network

Select "Custom Native Network":

Select Network

You can change the network title and update its advanced settings, but this is not necessary. Click "Save Section" to save the network and then go back to Network Detail page.

Step 2: Configure Custom Native Event classes

On the Network Detail page, click the pen icon next to the network title to edit it.

Edit Network

Under the corresponding apps, a list of supported ad units is shown. At the bottom, there is a button to let you enter the edit mode. You need to click "Edit Section" button before doing any updates. Otherwise, the changes will not be saved even though it seems they are.

Edit Section

Then you can enter the following information into the text boxes:

Custom Event Class

Custom Event Class: org.github.ayltai.mopub.adapter.FacebookEventNative (or any other BaseEventNative sub-classes supported by this library). If you have extended this classes, you must specify the exact package and class name accordingly.

Custom Event Class Data: {"adUnitId":"xxxxxxxxxx_yyyyyyyyyy"}. Replace it with your Facebook placement ID of your ad (or any other ad unit id you need for your ad network). Note that for some ad network, you may also need to specify apiKey in this field. The format is in JSON.

Coding

Step 4: Design your native ad view layout

Create your native ad view layout just like any other view layouts. You may use any UI components you need. Please check the sample layout.

Step 5: Initialize MoPub

You need to initialize MoPub native ads SDK like this:

moPubNative = new MoPubNative(context, adUnitId, moPubNativeNetworkListener);
moPubNative.registerAdRenderer(new MoPubStaticNativeAdRenderer(new ViewBinder.Builder(R.layout.view_native_ad)
    .titleId(R.id.ad_title)
    .textId(R.id.ad_body)
    .mainImageId(R.id.ad_image)
    .callToActionId(R.id.ad_call_to_action)
    .build()));

Here we specify the view layout resource, view_native_ad, for the ViewBinder, and tell it to use the view IDs for various fields.

adUnitId is the MoPub ad unit ID that you have set up for your application.

moPubNativeNetworkListener contains callbacks that you will use later.

Step 6: Request native ads

moPubNative.makeRequest(new RequestParameters.Builder()
    .desiredAssets(EnumSet.of(
        RequestParameters.NativeAdAsset.TITLE,
        RequestParameters.NativeAdAsset.TEXT,
        RequestParameters.NativeAdAsset.MAIN_IMAGE,
        RequestParameters.NativeAdAsset.CALL_TO_ACTION_TEXT))
    .build());

Here we request a native ad with title text, body text, main image and call-to-action text. You can also request other fields if you want.

You will receive the result of the request in MoPubNativeNetworkListener callbacks.

Step 7: Native ad request callbacks

@Override
public void onNativeLoad(NativeAd nativeAd) {
    nativeAd.setMoPubNativeEventListener(moPubNativeEventListener);
    nativeAd.renderAdView(view);
    nativeAd.prepare();
}

@Override
public void onNativeFail(NativeErrorCode errorCode) {
    // Handle failures here
    //
    // There can be many reasons of failure:
    // - No ads available
    // - Network failure
    // - Configuration error
    // - ... etc
}

If a native ad is returned successfully in onNativeLoad(), you can attach a MoPubNativeEventListener to it to receive further callbacks, such as onImpression(View) and onClick(View). It is not necessary to listen to these callbacks though.

Calling renderAdView(view) will automatically set the title text, body text, main image, etc. to the given view. The view must be inflated with the same layout you specified to the ViewBinder earlier above.

Calling prepare(view) will attach various ad tracking callbacks for you automatically, so that ad impressions and clicks can be tracked correctly.

Note: If you are going to re-use the view for another NativeAd instance, you need to call NativeAd.clear(View) before doing so.

Step 8: Clean up

nativeAd.destroy();
moPubNative.destroy();

When you are done with the ad (e.g. closing an Activity, or even closing the application), you need to destroy all the NativeAd instances you have received in onNativeLoad(NativeAd), otherwise memory leaks may occur. For the same reason, you need to destroy the MoPubNative instance.

Common Problems and Solutions

The main image is very small and does not fit all the available width.

You may try to resize your ImageView after calling renderAdView and prepare methods. Like this:

DisplayMetrics metrics = new DisplayMetrics();
((WindowManager)adContainerView.getContext().getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getMetrics(metrics);

ViewGroup.LayoutParams params = imageView.getLayoutParams();
params.width  = metrics.widthPixels;
params.height = (int)(metrics.widthPixels / 1.91f + 0.5f);

imageView.setLayoutParams(params);

The aspect ratio, 1.91, is the industry standard. But some ad providers may not comply.

How to design an universal view to display native ads from all ad providers?

This is not an easy task. AdMob, for instance, requires you to wrap your layout inside either NativeAppInstallAdView or NativeContentAdView, depending on which kind of ad they send to you. This is very troublesome and forces you to design view layouts specifically for AdMob only.

Here we provide a working example of how to workaround this by inflating ad view layouts dynamically.

In slow network condition, it is slow to fetch ads. Can I pre-fetch ads, or re-use previously displayed ads?

Yes! It is a bit tricky but not hard at all. I would like to leave it to the reader as an exercise.

How to customize ad requests and apply ad targeting?

You can do it by extending BaseEventNative and override onLoadNativeAd(BaseStaticNativeAd) method and put any ad targeting data to BaseStaticNativeAd.

Sample Application

A working example can be found here

Requirements

This library supports Android 4.1 Jelly Bean (API 16) or later.

mopub-nativead-adapters's People

Contributors

alan-tai avatar ayltai avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

Forkers

peter1408

mopub-nativead-adapters's Issues

Unable to load class

i have included your project to my gradle and all was going fine with my implementaion for medaition of MoPub with Facebook : but i got a pop with this error in my log

Caused by: java.lang.ClassNotFoundException: Didn't find class "org.github.ayltai.mopub.adapter.FacebookNativeAd" on path: DexPathList[[zip file "/data/app/com.ezydev.phonecompare-2/base.apk"],nativeLibraryDirectories=[/data/app/com.ezydev.phonecompare-2/lib/arm, /vendor/lib, /system/lib]]

@OverRide
public void onNativeLoad(final com.mopub.nativeads.NativeAd nativeAd) {
Log.i(this.getClass().getSimpleName(), "Native ad loaded");

    nativeAd.setMoPubNativeEventListener(this);
    final StaticNativeAd staticNativeAd = (StaticNativeAd)nativeAd.getBaseNativeAd();
   

    if (staticNativeAd instanceof FacebookNativeAd) {
        Log.i(this.getClass().getSimpleName(), "Native ad loaded FacebookNativeAd");
    }
}

screenshot 28
screenshot 29

Implementing Manual MoPub Integration With RecyclerView

Hi @ayltai, thank you for building this sample app. I'm looking for feedback on implementing the manual MoPub object integration with a RecyclerView?

I've followed the MoPubNative manual setup, and can see via the logs the ad view is received from the getAdView request. However, I am having trouble with Step 2. Render the Native Ad, and doing so within the RecyclerView pattern.

With the existing ContentViewHolderthe content data is bound and parsed in the layout cell_content.xml with the data represented by the Content model. For the MoPub ad view the data is described in the ViewBinder, therefore it seems this should bind the created view to the native_ad_item.xml layout. When an ad cell is reached there is an error due to an incorrect binding. May you please advise on rendering the created ad view using the ViewHolder pattern?

Error

ConstraintLayout cannot be cast to com.mopub.nativeads.ViewBinder

Adapter.kt

class ContentAdapter(var context: Context, var contentViewModel: ContentViewModel,
                 var homeViewModel: HomeViewModel,
                 var moPubAdapterHelper: AdapterHelper) : PagedListAdapter<Content, RecyclerView.ViewHolder>(DIFF_CALLBACK) {

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
    if (viewType == MOPUB_NATIVE_ROW_TYPE) {
        val binding = NativeAdItemBinding
                .inflate(LayoutInflater.from(parent.context), parent, false)
        return AdsViewHolder(binding)
    } else {
        val binding = CellContentBinding
                .inflate(LayoutInflater.from(parent.context), parent, false)
        binding.viewmodel = contentViewModel
        return ContentViewHolder(binding)
    }
}

override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
    if (getItemViewType(position) == MOPUB_NATIVE_ROW_TYPE) {
        val viewBinder = ViewBinder.Builder(app.coinverse.R.layout.native_ad_item)
                .titleId(app.coinverse.R.id.native_title)
                .textId(app.coinverse.R.id.native_text)
                .mainImageId(app.coinverse.R.id.native_main_image)
                .iconImageId(app.coinverse.R.id.native_icon_image)
                .callToActionId(app.coinverse.R.id.native_cta)
                .privacyInformationIconImageId(app.coinverse.R.id.native_privacy_information_icon_image)
                .build()
        if (homeViewModel.moPubInitialized) {
            val moPubNative = MoPubNative(context, AD_UNIT_ID,
                    object : MoPubNative.MoPubNativeNetworkListener {
                        override fun onNativeLoad(nativeAd: NativeAd) {

                            // How to render ad view with RecyclerAdapter?
                            val adView = moPubAdapterHelper.getAdView(null, null,
                                    nativeAd, ViewBinder.Builder(0).build())
                            (holder as AdsViewHolder).bind(adView)

                            nativeAd.setMoPubNativeEventListener(object : NativeAd.MoPubNativeEventListener {
                                override fun onImpression(view: View) {
                                    Log.d("MoPub", "Native ad recorded an impression.")
                                }

                                override fun onClick(view: View) {
                                    Log.d("MoPub", "Native ad recorded a click.")
                                }
                            })
                        }

                        override fun onNativeFail(errorCode: NativeErrorCode?) {
                            Log.e(LOG_TAG, "Native ad fail: ${errorCode}")
                        }
                    })
            moPubNative.registerAdRenderer(MoPubStaticNativeAdRenderer(viewBinder))
            moPubNative.makeRequest()
        }
    } else {
        getItem(position).let { content -> if (content != null) (holder as ContentViewHolder).bind(createOnClickListener(content), content) }
    }
}

override fun getItemCount() = moPubAdapterHelper.shiftedCount(super.getItemCount())

override fun getItem(position: Int) = super.getItem(moPubAdapterHelper.shiftedPosition(position))

override fun getItemViewType(position: Int) =
        if (moPubAdapterHelper.isAdPosition(position)) MOPUB_NATIVE_ROW_TYPE
        else super.getItemViewType(moPubAdapterHelper.shiftedPosition(position))

inner class AdsViewHolder(private var binding: ViewDataBinding) : 
RecyclerView.ViewHolder(binding.root) {
    fun bind(data: Any) {
        binding.setVariable(BR.data, data)
        binding.executePendingBindings()
    }
}

inner class ContentViewHolder(private var binding: ViewDataBinding) : RecyclerView.ViewHolder(binding.root) {
    fun bind(onClickListener: OnClickListener, data: Any) {
        binding.setVariable(BR.data, data)
        binding.setVariable(BR.clickListener, onClickListener)
        binding.root.preview.setTag(ADAPTER_POSITION_KEY, layoutPosition)
        binding.executePendingBindings()
    }
}
}

cell_content.xml

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">

<data>

    <variable
        name="data"
        type="app.coinverse.content.models.Content" />

    <variable
        name="viewmodel"
        type="app.coinverse.content.ContentViewModel" />

    <variable
        name="contentType"
        type="app.coinverse.Enums.ContentType" />

    <variable
        name="clickListener"
        type="android.view.View.OnClickListener" />

</data>

<androidx.cardview.widget.CardView
    android:id="@+id/contentCard"
    style="@style/CellCardStyle">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/cell_content_feed"
        style="@style/CellConstraintStyle">

        <TextView
            android:id="@+id/creator"
            style="@style/CellCreatorStyle"
            android:text='@{data.creator, default="creator name"}'
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <TextView
            android:id="@+id/timeAgo"
            style="@style/TimeAgoStyle"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintTop_toBottomOf="@id/creator"
            app:timePostedAgo="@{data.timestamp.toDate().time}"
            tools:text="time ago posted" />

        <ImageView
            android:id="@+id/contentTypeLogo"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:contentDescription="@string/youtube_content_type_content_description"
            app:contentTypeIcon="@{data.contentType != null ? data.contentType : contentType.NONE}"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="@id/creator" />

        <ImageView
            android:id="@+id/preview"
            style="@style/CellPreviewImageStyle"
            android:onClick="@{clickListener}"
            app:imageUrl="@{data.previewImage}"
            app:layout_constraintBottom_toTopOf="@id/title"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toBottomOf="@id/timeAgo" />

        <ProgressBar
            android:id="@+id/progressBar"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:visibility="@{viewmodel.getLoadingState(data.id)}"
            app:layout_constraintBottom_toBottomOf="@id/preview"
            app:layout_constraintLeft_toLeftOf="@id/preview"
            app:layout_constraintRight_toRightOf="@id/preview"
            app:layout_constraintTop_toTopOf="@id/preview" />

        <TextView
            android:id="@+id/title"
            style="@style/CellTitleStyle"
            android:text='@{data.title, default="content title"}'
            app:layout_constraintBottom_toTopOf="@id/share"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintTop_toBottomOf="@id/preview" />

        <ImageView
            android:id="@+id/openSource"
            style="@style/IconButtonStyle"
            android:background="?attr/selectableItemBackgroundBorderless"
            android:onClick="@{clickListener}"
            android:src="@drawable/ic_open_in_browser_black_24dp"
            android:tooltipText="@string/open_source_tip"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintRight_toLeftOf="@id/share"
            app:layout_constraintTop_toBottomOf="@id/title" />

        <ImageView
            android:id="@+id/share"
            style="@style/IconButtonStyle"
            android:background="?attr/selectableItemBackgroundBorderless"
            android:onClick="@{clickListener}"
            android:src="@drawable/ic_rocket_black"
            android:tooltipText="@string/share_content_tip"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toBottomOf="@id/title" />

    </androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
</layout>

native_ad_item.xml

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">

<data>
    <variable
        name="data"
        type="com.mopub.nativeads.ViewBinder" />

</data>

<androidx.constraintlayout.widget.ConstraintLayout
    android:id="@+id/native_outer_view"
    style="@style/AdContentCardStyle"
    android:textDirection="locale">

    <TextView
        android:id="@+id/native_title"
        style="@style/CellCreatorStyle"
        app:layout_constraintBottom_toBottomOf="@+id/guideline"
        app:layout_constraintLeft_toLeftOf="parent" />

    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/guideline"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintBottom_toTopOf="@id/native_main_image"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/sponsored"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/sponsored"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toBottomOf="@id/guideline" />

    <ImageView
        android:id="@+id/native_icon_image"
        android:layout_width="@dimen/native_icon_image_dimen"
        android:layout_height="@dimen/native_icon_image_dimen"
        android:background="@null"
        android:contentDescription="@string/native_icon_mage"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <ImageView
        android:id="@+id/native_main_image"
        style="@style/CellPreviewImageStyle"
        android:background="@null"
        android:contentDescription="@string/native_main_image"
        android:scaleType="centerCrop"
        app:layout_constraintBottom_toTopOf="@id/native_text"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@id/sponsored" />

    <TextView
        android:id="@+id/native_text"
        style="@style/CellTitleStyle"
        app:layout_constraintBottom_toTopOf="@id/native_cta"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toBottomOf="@id/native_main_image" />

    <TextView
        android:id="@+id/native_cta"
        style="@style/NativeCtaStyle"
        android:background="?attr/selectableItemBackground"
        android:clickable="true"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@id/native_text"
        tools:text="@string/learn_more" />

    <ImageView
        android:id="@+id/native_privacy_information_icon_image"
        android:layout_width="@dimen/native_privacy_info_icon_image_dimen"
        android:layout_height="@dimen/native_privacy_info_icon_image_dimen"
        android:contentDescription="@string/native_privacy_information_icon_image"
        app:layout_constraintBottom_toBottomOf="@id/native_cta"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="@id/native_cta" />

</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.