diff --git a/README.md b/README.md
new file mode 100644
index 0000000..e29a30c
--- /dev/null
+++ b/README.md
@@ -0,0 +1,138 @@
+Ad2iction Android Adapter for MoPub
+=================================
+
+Adapter version 1.0.0 - Updated 2017-02-21
+------------------------------------------
+
+This version of the adapter works with MoPub Android SDK 4.12+ and Ad2iction Android SDK 3.3.0.
+Otherwise, please upgrade to the newer versions of both SDKs.
+
+### Mediate Ad2iction Ads through MoPub
+
+To integrate Ad2iction as the Custom Native Network in the MoPub ad serving flow, you need the
+Custom Event Class code incorporated into your application in addition to the Flurry SDK.
+Three quick steps are necessary:
+
+1. Integrate the Ad2iction SDK and Ad2iction adapter for MoPub code into your app
+2. Configure Ad2iction's Ad unit(s)
+3. Configure MoPub to mediate Ad2iction
+
+#### Integrate the Ad2iction SDK and Ad2iction adapter for MoPub code into your app
+
+1. If your application is not yet using Ad2iction, please contact Ad2iction.
+
+2. Add the Ad2iction Android SDK.
+
+3. Add the Google Play Services SDK to your project. This is required for Android Advertising ID
+support. See http://developer.android.com/google/playservices/setup.html.
+
+4. Add the Ad2iction MoPub adapter classes (found in the [com.mopub.nativeads](src/com/mopub/nativeads) package) to your project. Place the following classes in the com.mopub.nativeads package:
+ * [`Ad2ictionCustomEventNative`](src/com/mopub/nativeads/Ad2ictionCustomEventNative.java)
+ * [`Ad2ictionNativeAdRenderer`](src/com/mopub/nativeads/Ad2ictionNativeAdRenderer.java)
+
+5. The steps to integrate Flurry Native Ads via MoPub are similar to those described [here](https://github.com/mopub/mopub-android-sdk/wiki/Native-Ads-Integration):
+ * Create an XML layout for your native ads
+ * Define where ads should be placed within your feed
+ * Create a MoPubAdAdapter to wrap your existing `Adapter` subclass and begin loading ads.
+
+ And you should register the `Ad2ictionNativeAdRenderer` as a custom ad renderer.
+
+ ```java
+ ViewBinder viewBinder = new ViewBinder.Builder(R.layout.native_ad_list_item)
+ // Set up your regular ViewBinder
+ .build();
+
+ // Register the Ad2ictionNativeAdRenderer to handle static native ads
+ final Ad2ictionNativeAdRenderer ad2ictionRenderer = new Ad2ictionNativeAdRenderer(viewBinder);
+ mAdAdapter = new MoPubAdAdapter(getActivity(), adapter);
+ mAdAdapter.registerAdRenderer(ad2ictionRenderer);
+
+ //...register other native ad renderers as required
+ ```
+
+6. Add permissions in AndroidManifest:
+```xml
+
+
+
+
+
+```
+
+7. Add Activity declaration in AndroidManifest:
+```xml
+
+
+
+
+```
+
+8. If you plan to run [ProGuard](http://developer.android.com/tools/help/proguard.html) on your app
+before release, you will need to add the following to your ProGuard configuration file.
+
+```
+# Ad2iction Proguard Config
+# NOTE: You should also include the Android Proguard config found with the build tools:
+# $ANDROID_HOME/tools/proguard/proguard‐android.txt
+
+# Add this line if you use Admob Adapter
+‐libraryjars \libs\ad2iction‐mediation‐adapteradmob.jar
+
+‐libraryjars \libs\ad2iction‐sdk.jar
+
+# Keep public classes and methods.
+‐keepclassmembers class com.ad2iction.** { public *;}
+‐keep public class com.ad2iction.**
+‐keep public class android.webkit.JavascriptInterface {}
+
+‐keep class * extends com.ad2iction.mobileads.CustomEventBanner {}
+‐keep class * extends com.ad2iction.mobileads.CustomEventInterstitial {}
+‐keep class * extends com.ad2iction.nativeads.CustomEventNative {}
+
+‐keep class com.google.android.gms.common.GooglePlayServicesUtil {*;}
+‐keep class com.google.android.gms.ads.identifier.AdvertisingIdClient {*;}
+‐keep class com.google.android.gms.ads.identifier.AdvertisingIdClient$Info {*;}
+ ```
+
+#### Configure Ad2iction Ad unit(s)
+
+For each MoPub ad unit that you would like to mediate Ad2iction through, please contact Ad2iction.
+
+#### Configure MoPub to mediate Ad2iction
+
+Ad2iction's custom events are implemented in accordance with [instructions provided by MoPub](https://github.com/mopub/mopub-android-sdk/wiki/Custom-Events).
+
+After you incorporate the Ad2iction files into your project, you need to
+configure Ad2iction as a Custom Network. Please follow instructions provided by MoPub
+(for [native](https://dev.twitter.com/mopub/ui-setup/network-setup-custom-native)) with any of the Ad2iction custom events class noted below:
+
+* [`com.mopub.nativeads.Ad2ictionCustomEventNative`](src/com/mopub/nativeads/Ad2ictionCustomEventNative.java)
+ for Ad2iction native ads
+
+**NOTE:** An important step to get this integration working is to configure the custom network (as described above) with the
+Ad2iction API key and send them as server extras (in the "Custom Event Class Data" field on the MoPub UI).
+
+The custom event class data should be in the following JSON format:
+
+```json
+{"response_body_key":"YOUR_API_KEY"}
+```
+
+You can also configure the custom event data as a line item in your MoPub order.
+
+![Screenshot showing ad unit/line item config on MoPub's dashboard](imgs/mopub_line_item_config.png)
+
+Changelog
+---------
+#### Version 1.0.0 - 2017-02-21
+* Introduced Ad2iction Native Ad support in the adapter
+* Supports MoPub SDK 4.12+
diff --git a/imgs/mopub_line_item_config.png b/imgs/mopub_line_item_config.png
new file mode 100644
index 0000000..f4b7d39
Binary files /dev/null and b/imgs/mopub_line_item_config.png differ
diff --git a/src/com/mopub/nativeads/Ad2ictionCustomEventNative.java b/src/com/mopub/nativeads/Ad2ictionCustomEventNative.java
new file mode 100644
index 0000000..56fe664
--- /dev/null
+++ b/src/com/mopub/nativeads/Ad2ictionCustomEventNative.java
@@ -0,0 +1,142 @@
+package com.mopub.nativeads;
+
+import android.content.Context;
+import android.support.annotation.NonNull;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.View;
+
+import com.ad2iction.nativeads.Ad2ictionNative;
+import com.ad2iction.nativeads.NativeResponse;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import static com.mopub.nativeads.NativeImageHelper.preCacheImages;
+
+public class Ad2ictionCustomEventNative extends CustomEventNative {
+
+ private static final String LOG_TAG = Ad2ictionCustomEventNative.class.getSimpleName();
+
+ protected void loadNativeAd(@NonNull Context context,
+ @NonNull CustomEventNativeListener customEventNativeListener,
+ @NonNull Map localExtras,
+ @NonNull Map serverExtras) {
+ try {
+ Ad2ictionForwardingNativeAd ad2ictionForwardingNativeAd =
+ new Ad2ictionForwardingNativeAd(context, serverExtras.get("response_body_key"),
+ customEventNativeListener);
+ ad2ictionForwardingNativeAd.loadAd();
+ } catch (IllegalArgumentException e) {
+ customEventNativeListener.onNativeAdFailed(NativeErrorCode.UNSPECIFIED);
+ Log.i(LOG_TAG,
+ "Failed Native AdFetch: Missing required server extras [response_body_key].");
+ }
+ }
+
+ public static class Ad2ictionForwardingNativeAd extends BaseNativeAd
+ implements Ad2ictionNative.Ad2ictionNativeNetworkListener,
+ Ad2ictionNative.Ad2ictionNativeEventListener {
+
+ private final Context mContext;
+ private final String mKey;
+ private final Ad2ictionNative mAd;
+ private final CustomEventNativeListener mCustomEventNativeListener;
+ private NativeResponse mResponse;
+
+ private Ad2ictionForwardingNativeAd(Context context, String key,
+ CustomEventNativeListener customEventNativeListener)
+ throws IllegalArgumentException {
+ mContext = context.getApplicationContext();
+ mKey = key;
+ if (TextUtils.isEmpty(mKey)) {
+ throw new IllegalArgumentException("Key cannot be null");
+ }
+ mAd = new Ad2ictionNative(context, key, "native", this);
+ mCustomEventNativeListener = customEventNativeListener;
+ }
+
+ private void loadAd() {
+ mAd.setNativeEventListener(this);
+ mAd.makeRequest();
+ }
+
+ @Override
+ public void onNativeLoad(NativeResponse nativeResponse) {
+ mResponse = nativeResponse;
+
+ final List imageUrls = new ArrayList<>(2);
+ final String mainImageUrl = nativeResponse.getMainImageUrl();
+ if (mainImageUrl != null) {
+ imageUrls.add(mainImageUrl);
+ Log.d(LOG_TAG, "Ad2iction Native Ad main image found.");
+ }
+
+ final String iconUrl = nativeResponse.getIconImageUrl();
+ if (iconUrl != null) {
+ imageUrls.add(iconUrl);
+ Log.d(LOG_TAG, "Ad2iction Native Ad icon image found.");
+ }
+
+ preCacheImages(mContext, imageUrls, new NativeImageHelper.ImageListener() {
+
+ public void onImagesCached() {
+ mCustomEventNativeListener.onNativeAdLoaded(Ad2ictionForwardingNativeAd.this);
+ }
+
+ public void onImagesFailedToCache(NativeErrorCode errorCode) {
+ mCustomEventNativeListener.onNativeAdFailed(errorCode);
+ }
+ });
+ }
+
+ @Override
+ public void onNativeFail(com.ad2iction.nativeads.NativeErrorCode nativeErrorCode) {
+ if (nativeErrorCode == null) {
+ mCustomEventNativeListener.onNativeAdFailed(NativeErrorCode.UNSPECIFIED);
+ } else if (nativeErrorCode == com.ad2iction.nativeads.NativeErrorCode.NETWORK_NO_FILL) {
+ mCustomEventNativeListener.onNativeAdFailed(NativeErrorCode.NETWORK_NO_FILL);
+ } else if (nativeErrorCode ==
+ com.ad2iction.nativeads.NativeErrorCode.NETWORK_INVALID_STATE) {
+ mCustomEventNativeListener.onNativeAdFailed(NativeErrorCode.NETWORK_INVALID_STATE);
+ } else {
+ mCustomEventNativeListener.onNativeAdFailed(NativeErrorCode.UNSPECIFIED);
+ }
+ }
+
+ @Override
+ public void onNativeImpression(View view) {
+ Log.d(LOG_TAG, "onNativeImpression: Ad2iction native ad impression logged");
+ notifyAdImpressed();
+ }
+
+ @Override
+ public void onNativeClick(View view) {
+ Log.d(LOG_TAG, "onNativeClick: Ad2iction native ad clicked");
+ notifyAdClicked();
+ }
+
+ @Override
+ public void prepare(@NonNull View view) {
+ mResponse.prepare(view);
+ Log.d(LOG_TAG, "prepare(" + mResponse.toString() + " " + view.toString() + ")");
+ }
+
+ @Override
+ public void clear(@NonNull View view) {
+ mResponse.clear(view);
+ Log.d(LOG_TAG, "clear(" + mResponse.toString() + ")");
+ }
+
+ @Override
+ public void destroy() {
+ mResponse.destroy();
+ Log.d(LOG_TAG, "destroy(" + mResponse.toString() + ") started.");
+ }
+
+ NativeResponse getNativeResponse() {
+ return mResponse;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/com/mopub/nativeads/Ad2ictionNativeAdRenderer.java b/src/com/mopub/nativeads/Ad2ictionNativeAdRenderer.java
new file mode 100644
index 0000000..e83ff99
--- /dev/null
+++ b/src/com/mopub/nativeads/Ad2ictionNativeAdRenderer.java
@@ -0,0 +1,145 @@
+package com.mopub.nativeads;
+
+import android.content.Context;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.ad2iction.common.VisibleForTesting;
+import com.ad2iction.nativeads.NativeResponse;
+import com.mopub.common.Preconditions;
+
+import java.util.Iterator;
+import java.util.WeakHashMap;
+
+public class Ad2ictionNativeAdRenderer
+ implements MoPubAdRenderer {
+
+ private static final String LOG_TAG = Ad2ictionNativeAdRenderer.class.getSimpleName();
+
+ @NonNull private final ViewBinder mViewBinder;
+ @NonNull private final WeakHashMap mViewHolderMap;
+
+ public Ad2ictionNativeAdRenderer(@NonNull final ViewBinder viewBinder) {
+ mViewBinder = viewBinder;
+ mViewHolderMap = new WeakHashMap<>();
+ }
+
+ @Override
+ @NonNull
+ public View createAdView(@NonNull final Context context, final ViewGroup parent) {
+ return LayoutInflater.from(context).inflate(this.mViewBinder.layoutId, parent, false);
+ }
+
+ @Override
+ public void renderAdView(@NonNull final View view, @NonNull
+ final Ad2ictionCustomEventNative.Ad2ictionForwardingNativeAd ad2ictionForwardingNativeAd) {
+ Ad2ictionNativeViewHolder nativeViewHolder = mViewHolderMap.get(view);
+ if (nativeViewHolder == null) {
+ nativeViewHolder = Ad2ictionNativeViewHolder.fromViewBinder(view, this.mViewBinder);
+ mViewHolderMap.put(view, nativeViewHolder);
+ }
+ nativeViewHolder.update(ad2ictionForwardingNativeAd.getNativeResponse());
+ nativeViewHolder
+ .updateExtras(view, ad2ictionForwardingNativeAd.getNativeResponse(), mViewBinder);
+ setViewVisibility(nativeViewHolder, View.VISIBLE);
+ }
+
+ private void setViewVisibility(@NonNull final Ad2ictionNativeViewHolder viewHolder,
+ final int visibility) {
+ if (viewHolder.staticNativeViewHolder.mainView != null) {
+ viewHolder.staticNativeViewHolder.mainView.setVisibility(visibility);
+ }
+ }
+
+ @Override
+ public boolean supports(@NonNull final BaseNativeAd nativeAd) {
+ Preconditions.checkNotNull(nativeAd);
+ return nativeAd instanceof Ad2ictionCustomEventNative.Ad2ictionForwardingNativeAd;
+ }
+
+ private static class Ad2ictionNativeViewHolder {
+
+ private final StaticNativeViewHolder staticNativeViewHolder;
+ @Nullable TextView titleView;
+ @Nullable TextView textView;
+ @Nullable TextView callToActionView;
+ @Nullable ImageView mainImageView;
+ @Nullable ImageView iconImageView;
+
+ @VisibleForTesting
+
+ private Ad2ictionNativeViewHolder(final StaticNativeViewHolder staticNativeViewHolder) {
+ this.staticNativeViewHolder = staticNativeViewHolder;
+ }
+
+ static Ad2ictionNativeViewHolder fromViewBinder(final View view,
+ final ViewBinder viewBinder) {
+ StaticNativeViewHolder staticNativeViewHolder =
+ StaticNativeViewHolder.fromViewBinder(view, viewBinder);
+ Ad2ictionNativeViewHolder nativeViewHolder =
+ new Ad2ictionNativeViewHolder(staticNativeViewHolder);
+
+ nativeViewHolder.titleView = staticNativeViewHolder.titleView;
+ nativeViewHolder.textView = staticNativeViewHolder.textView;
+ nativeViewHolder.callToActionView = staticNativeViewHolder.callToActionView;
+ nativeViewHolder.mainImageView = staticNativeViewHolder.mainImageView;
+ nativeViewHolder.iconImageView = staticNativeViewHolder.iconImageView;
+
+ return nativeViewHolder;
+ }
+
+ void update(@NonNull NativeResponse nativeResponse) {
+ this.addTextView(this.titleView, nativeResponse.getTitle());
+ this.addTextView(this.textView, nativeResponse.getText());
+ this.addTextView(this.callToActionView, nativeResponse.getCallToAction());
+ nativeResponse.loadMainImage(this.mainImageView);
+ nativeResponse.loadIconImage(this.iconImageView);
+ }
+
+ void updateExtras(@NonNull View outerView, @NonNull NativeResponse nativeResponse,
+ @NonNull ViewBinder viewBinder) {
+ Iterator i$ = viewBinder.extras.keySet().iterator();
+
+ while (i$.hasNext()) {
+ String key = (String) i$.next();
+ int resourceId = viewBinder.extras.get(key);
+ View view = outerView.findViewById(resourceId);
+ Object content = nativeResponse.getExtra(key);
+ if (view instanceof ImageView) {
+ ((ImageView) view).setImageDrawable(null);
+ nativeResponse.loadExtrasImage(key, (ImageView) view);
+ } else if (view instanceof TextView) {
+ ((TextView) view).setText(null);
+ if (content instanceof String) {
+ this.addTextView((TextView) view, (String) content);
+ }
+ } else {
+ Log.d(LOG_TAG, "View bound to " + key +
+ " should be an instance of TextView or ImageView.");
+ }
+ }
+
+ }
+
+ private void addTextView(@Nullable TextView textView, @Nullable String contents) {
+ if (textView == null) {
+ Log.d(LOG_TAG, "Attempted to add text (" + contents + ") to null TextView.");
+ } else {
+ textView.setText(null);
+ if (contents == null) {
+ Log.d(LOG_TAG, "Attempted to set TextView contents to null.");
+ } else {
+ textView.setText(contents);
+ }
+
+ }
+ }
+ }
+
+}