package com.clockbyte.admobadapter.sampleapp.banner;

import android.app.Activity;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import com.clockbyte.admobadapter.bannerads.AdmobBannerRecyclerAdapterWrapper;
import com.clockbyte.admobadapter.bannerads.BannerAdViewWrappingStrategy;
import com.clockbyte.admobadapter.bannerads.BannerAdViewWrappingStrategyBase;
import com.clockbyte.admobadapter.sampleapp.R;
import com.clockbyte.admobadapter.sampleapp.RecyclerExampleAdapter;
import com.google.android.gms.ads.AdRequest;
import com.google.android.gms.ads.AdSize;
import com.google.android.gms.ads.AdView;
import com.google.android.gms.ads.MobileAds;

import java.util.ArrayList;

public class MainActivity_RecyclerView_Banner extends Activity {

    RecyclerView rvMessages;
    AdmobBannerRecyclerAdapterWrapper adapterWrapper;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main_recycleview);

        //highly-recommended in Firebase docs to initialize things early as possible
        //test_admob_app_id is different with unit_id! you could get it in your Admob console
        MobileAds.initialize(getApplicationContext(), getString(R.string.test_admob_app_id));

        initRecyclerViewItems();
    }

    /**
     * Inits an adapter with items, wrapping your adapter with a {@link AdmobBannerRecyclerAdapterWrapper} and setting the recyclerview to this wrapper
     * FIRST OF ALL Please notice that the following code will work on a real devices but emulator!
     */
    private void initRecyclerViewItems() {
        rvMessages = (RecyclerView) findViewById(R.id.rvMessages);
        rvMessages.setLayoutManager(new LinearLayoutManager(this));

        //creating your adapter, it could be a custom adapter as well
        RecyclerExampleAdapter adapter  = new RecyclerExampleAdapter(this);

        //your test devices' ids
        String[] testDevicesIds = new String[]{getString(R.string.testDeviceID), AdRequest.DEVICE_ID_EMULATOR};
        //when you'll be ready for release please use another ctor with admobReleaseUnitId instead.
        adapterWrapper = AdmobBannerRecyclerAdapterWrapper.builder(this)
                .setLimitOfAds(10)
                .setFirstAdIndex(2)
                .setNoOfDataBetweenAds(10)
                .setTestDeviceIds(testDevicesIds)
                .setAdapter(adapter)
                //Use the following for the default Wrapping behaviour
//                .setAdViewWrappingStrategy(new BannerAdViewWrappingStrategy())
                // Or implement your own custom wrapping behaviour:
                .setAdViewWrappingStrategy(new BannerAdViewWrappingStrategyBase() {
                    @NonNull
                    @Override
                    protected ViewGroup getAdViewWrapper(ViewGroup parent) {
                        return (ViewGroup) LayoutInflater.from(parent.getContext()).inflate(R.layout.native_express_ad_container,
                                parent, false);
                    }

                    @Override
                    protected void recycleAdViewWrapper(@NonNull ViewGroup wrapper, @NonNull AdView ad) {
                        //get the view which directly will contain ad
                        ViewGroup container = (ViewGroup) wrapper.findViewById(R.id.ad_container);
                        //iterating through all children of the container view and remove the first occured {@link NativeExpressAdView}. It could be different with {@param ad}!!!*//*
                        for (int i = 0; i < container.getChildCount(); i++) {
                            View v = container.getChildAt(i);
                            if (v instanceof AdView) {
                                container.removeViewAt(i);
                                break;
                            }
                        }
                    }

                    @Override
                    protected void addAdViewToWrapper(@NonNull ViewGroup wrapper, @NonNull AdView ad) {
                        //get the view which directly will contain ad
                        ViewGroup container = (ViewGroup) wrapper.findViewById(R.id.ad_container);
                        //add the {@param ad} directly to the end of container*//*
                        container.addView(ad);
                    }
                })
                .build();

        rvMessages.setAdapter(adapterWrapper); // setting an AdmobBannerRecyclerAdapterWrapper to a RecyclerView
        //use the following commented block to use a grid layout with spanning ad blocks
        GridLayoutManager mLayoutManager = new GridLayoutManager(this, 2);
        mLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
            @Override
            public int getSpanSize(int position) {
                if(adapterWrapper.getItemViewType(position) == adapterWrapper.getViewTypeAdBanner())
                    return 2;
                else return 1;
            }
        });
        rvMessages.setLayoutManager(mLayoutManager);

        //preparing the collection of data
        final String sItem = "item #";
        ArrayList<String> lst = new ArrayList<String>(100);
        for(int i=1;i<=100;i++)
            lst.add(sItem.concat(Integer.toString(i)));

        //adding a collection of data to your adapter and rising the data set changed event
        adapter.addAll(lst);
        adapter.notifyDataSetChanged();
    }

    /*
    * Seems to be a good practice to destroy all the resources you have used earlier :)
     */
    @Override
    protected void onDestroy() {
        super.onDestroy();
        adapterWrapper.release();
    }

    @Override
    protected void onPause() {
        adapterWrapper.pauseAll();
        super.onPause();
    }

    @Override
    protected void onResume() {
        super.onResume();
        adapterWrapper.resumeAll();
    }
}


/*
 * Copyright (c) 2017 Yahoo Inc. All rights reserved.
 * Copyright (c) 2017 Clockbyte LLC. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.clockbyte.admobadapter;

import android.content.Context;
import android.os.Handler;

import com.google.android.gms.ads.AdRequest;
import com.google.android.gms.ads.NativeExpressAdView;

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;

public abstract class AdmobFetcherBase {

    private final String TAG = AdmobFetcherBase.class.getCanonicalName();

    protected List<AdmobListener> mAdNativeListeners = new ArrayList<AdmobListener>();
    protected int mNoOfFetchedAds;
    protected int mFetchFailCount;
    protected WeakReference<Context> mContext = new WeakReference<Context>(null);
    protected AtomicBoolean lockFetch = new AtomicBoolean();

    protected ArrayList<String> testDeviceId = new ArrayList<String>();
    /*
    *Gets a test device ID. Normally you don't have to set it
     */
    public ArrayList<String> getTestDeviceIds() {
        return testDeviceId;
    }
    /*
    *Sets a test device ID. Normally you don't have to set it
     */
    public void addTestDeviceId(String testDeviceId) {
        this.testDeviceId.add(testDeviceId);
    }

    /**
     * Adds an {@link AdmobListener} that would be notified for any changes to the native ads
     * list.
     *
     * @param listener the listener to be notified
     */
    public synchronized void addListener(AdmobListener listener) {
        mAdNativeListeners.add(listener);
    }

    /**
     * Gets the number of ads that have been fetched so far.
     *
     * @return the number of ads that have been fetched
     */
    public synchronized int getFetchedAdsCount() {
        return mNoOfFetchedAds;
    }
    /**
     * Gets the number of ads that have been fetched and are currently being fetched
     *
     * @return the number of ads that have been fetched and are currently being fetched
     */
    public abstract int getFetchingAdsCount();

    public int getFetchFailCount() {
        return mFetchFailCount;
    }

    /**
     * Fetches a new native ad.
     *
     * @param context the current context.
     * @see #destroyAllAds()
     */
    public synchronized void prefetchAds(Context context) {
        mContext = new WeakReference<Context>(context);
    }

    /**
     * Destroys ads that have been fetched, that are still being fetched and removes all resource
     * references that this instance still has. This should only be called when the Activity that
     * is showing ads is closing, preferably from the {@link android.app.Activity#onDestroy()}.
     * </p>
     * The converse of this call is {@link #prefetchAds(Context)}.
     */
    public synchronized void destroyAllAds() {
        mFetchFailCount = 0;
        mNoOfFetchedAds = 0;
        onAdsCountChanged();
    }

    /**
     * Frees all weak refs and collections
     */
    public void release(){
        destroyAllAds();
        mContext.clear();
    }

    /**
     * Notifies all registered {@link AdmobListener} on a change of ad count.
     */
    protected void onAdsCountChanged() {
        final Context context = mContext.get();
        //context may be null if activity is destroyed
        if(context != null) {
            new Handler(context.getMainLooper()).post(new Runnable() {
                @Override
                public void run() {
                    for (AdmobListener listener : mAdNativeListeners)
                        listener.onAdsCountChanged();
                }
            });
        }
    }

    /**
     * Notifies all registered {@link AdmobListener} on a loaded ad.
     */
    protected void onAdLoaded(final int adIdx) {
        final Context context = mContext.get();
        //context may be null if activity is destroyed
        if(context != null) {
            new Handler(context.getMainLooper()).post(new Runnable() {
                @Override
                public void run() {
                    for (AdmobListener listener : mAdNativeListeners)
                        listener.onAdLoaded(adIdx);
                }
            });
        }
    }

    /**
     * Notifies all registered {@link AdmobListener} on a failed ad.
     */
    protected void onAdFailed(final int adIdx, final int errorCode, final Object adPayload) {
        final Context context = mContext.get();
        //context may be null if activity is destroyed
        if(context != null) {
            for (AdmobListener listener : mAdNativeListeners)
                listener.onAdFailed(adIdx, errorCode, adPayload);
        }
    }

    /**
     * Setup and get an ads request
     */
    protected synchronized AdRequest getAdRequest() {
        AdRequest.Builder adBldr = new AdRequest.Builder();
        for (String id : getTestDeviceIds()) {
            adBldr.addTestDevice(id);
        }
        return adBldr.build();
    }

    /**
     * Listener that is notified when changes to the list of fetched native ads are made.
     */
    public interface AdmobListener {
        /**
         * Raised when the ad has loaded. Adapters that implement this class
         * should notify their data views that the dataset has changed.
         * @param adIdx the index of ad block which state was changed.
         * See {@link AdmobAdapterCalculator} for methods to transform {@param adIdx} to adapter wrapper's indices
         */
        void onAdLoaded(int adIdx);
        /**
         * Raised when the number of ads have changed. Adapters that implement this class
         * should notify their data views that the dataset has changed.
         */
        void onAdsCountChanged();

        /**
         * Raised when the ad has failed to load.
         * @param adIdx the index of ad block which state was changed.
         * @param adPayload filled with some specific for current platform payload
         * (for Admob Native Express it is {@link NativeExpressAdView})
         * See {@link AdmobAdapterCalculator} for methods to transform {@param adIdx} to adapter wrapper's indices
         */
        void onAdFailed(int adIdx, int errorCode, Object adPayload);
    }
}


package com.clockbyte.admobadapter.sampleapp.express;

import android.app.Activity;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import com.clockbyte.admobadapter.expressads.AdViewWrappingStrategyBase;
import com.clockbyte.admobadapter.expressads.AdmobExpressRecyclerAdapterWrapper;
import com.clockbyte.admobadapter.sampleapp.R;
import com.clockbyte.admobadapter.sampleapp.RecyclerExampleAdapter;
import com.google.android.gms.ads.AdRequest;
import com.google.android.gms.ads.MobileAds;
import com.google.android.gms.ads.NativeExpressAdView;

import java.util.ArrayList;

/**
 *
 * @deprecated Use banners instead
 */
@Deprecated
public class MainActivity_RecyclerView_Express extends Activity {

    RecyclerView rvMessages;
    AdmobExpressRecyclerAdapterWrapper adapterWrapper;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main_recycleview);

        //highly-recommended in Firebase docs to initialize things early as possible
        //test_admob_app_id is different with unit_id! you could get it in your Admob console
        MobileAds.initialize(getApplicationContext(), getString(R.string.test_admob_app_id));

        initRecyclerViewItems();
    }

    /**
     * Inits an adapter with items, wrapping your adapter with a {@link AdmobExpressRecyclerAdapterWrapper} and setting the recyclerview to this wrapper
     * FIRST OF ALL Please notice that the following code will work on a real devices but emulator!
     */
    private void initRecyclerViewItems() {
        rvMessages = (RecyclerView) findViewById(R.id.rvMessages);
        rvMessages.setLayoutManager(new LinearLayoutManager(this));

        //creating your adapter, it could be a custom adapter as well
        RecyclerExampleAdapter adapter  = new RecyclerExampleAdapter(this);

        //your test devices' ids
        String[] testDevicesIds = new String[]{getString(R.string.testDeviceID),AdRequest.DEVICE_ID_EMULATOR};
        //when you'll be ready for release please use another ctor with admobReleaseUnitId instead.
        adapterWrapper = AdmobExpressRecyclerAdapterWrapper.builder(this)
                .setLimitOfAds(10)
                .setFirstAdIndex(2)
                .setNoOfDataBetweenAds(10)
                .setTestDeviceIds(testDevicesIds)
                .setAdapter(adapter)
                .setAdViewWrappingStrategy(new AdViewWrappingStrategyBase() {
                    @NonNull
                    @Override
                    protected ViewGroup getAdViewWrapper(ViewGroup parent) {
                        return (ViewGroup) LayoutInflater.from(parent.getContext()).inflate(R.layout.web_ad_container,
                                parent, false);
                    }

                    @Override
                    protected void recycleAdViewWrapper(@NonNull ViewGroup wrapper, @NonNull NativeExpressAdView ad) {
                        //get the view which directly will contain ad
                        ViewGroup container = (ViewGroup) wrapper.findViewById(R.id.ad_container);
                        //iterating through all children of the container view and remove the first occured {@link NativeExpressAdView}. It could be different with {@param ad}!!!*//*
                        for (int i = 0; i < container.getChildCount(); i++) {
                            View v = container.getChildAt(i);
                            if (v instanceof NativeExpressAdView) {
                                container.removeViewAt(i);
                                break;
                            }
                        }
                    }

                    @Override
                    protected void addAdViewToWrapper(@NonNull ViewGroup wrapper, @NonNull NativeExpressAdView ad) {
                        //get the view which directly will contain ad
                        ViewGroup container = (ViewGroup) wrapper.findViewById(R.id.ad_container);
                        //add the {@param ad} directly to the end of container*//*
                        container.addView(ad);
                    }
                })
                .build();

        rvMessages.setAdapter(adapterWrapper); // setting an AdmobBannerRecyclerAdapterWrapper to a RecyclerView
        //use the following commented block to use a grid layout with spanning ad blocks
       /* GridLayoutManager mLayoutManager = new GridLayoutManager(this, 2);
        mLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
            @Override
            public int getSpanSize(int position) {
                if(adapterWrapper.getItemViewType(position) == adapterWrapper.getViewTypeAdExpress())
                    return 2;
                else return 1;
            }
        });
        rvMessages.setLayoutManager(mLayoutManager);*/

        //preparing the collection of data
        final String sItem = "item #";
        ArrayList<String> lst = new ArrayList<String>(100);
        for(int i=1;i<=100;i++)
            lst.add(sItem.concat(Integer.toString(i)));

        //adding a collection of data to your adapter and rising the data set changed event
        adapter.addAll(lst);
        adapter.notifyDataSetChanged();
    }

    /*
    * Seems to be a good practice to destroy all the resources you have used earlier :)
     */
    @Override
    protected void onDestroy() {
        super.onDestroy();
        adapterWrapper.release();
    }
}


/*
 * Copyright (c) 2017 Clockbyte LLC. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.clockbyte.admobadapter;

import java.util.ArrayList;
import java.util.Collection;

/**
 * Created by FILM on 02.10.2016.
 */

public class AdPresetCyclingList extends ArrayList<AdPreset> {

    private int currentIdx = -1;

    public AdPresetCyclingList(){
        super();
    }

    public int getCurrentIdx() {
        return currentIdx;
    }

    /**
     * Gets next ad preset for Admob banners from FIFO.
     * It works like cycling FIFO (first in = first out, cycling from end to start).
     * Each ad block will get one from the queue.
     * If the desired count of ad blocks is greater than this collection size
     * then it will go again to the first item and iterate as much as it required.
     * ID should be active, please check it in your Admob's account.
     */
    public AdPreset get() {
        if(size() == 0) return null;
        if (size() == 1) return get(0);
        currentIdx = ++currentIdx % size();
        return get(currentIdx);
    }

    /**
     * Tries to add an item to collection if it is valid {@link AdPreset#isValid()}
     * @return true if item was added, false - otherwise
     */
    @Override
    public boolean add(AdPreset adPreset) {
        return !(adPreset == null || !adPreset.isValid())
                && super.add(adPreset);
    }

    /**
     * Tries to add items to collection if valid {@link AdPreset#isValid()}
     * @return true if items were added, false - otherwise
     */
    @Override
    public boolean addAll(Collection<? extends AdPreset> c) {
        ArrayList<AdPreset> lst = new ArrayList<AdPreset>();
        for (AdPreset eap : c) {
            if(eap!=null && eap.isValid())
                lst.add(eap);
        }
        return super.addAll(lst);
    }
}


package com.clockbyte.admobadapter.bannerads;

import android.support.annotation.NonNull;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import com.clockbyte.admobadapter.R;
import com.google.android.gms.ads.AdView;

/**
 * Created by L4grange on 14/02/2018
 */

public class BannerAdViewWrappingStrategy extends BannerAdViewWrappingStrategyBase {
    /**
     * Add the Banner {@param ad} to {@param wrapper}.
     * See the super's implementation for instance.
     */
    @Override
    protected void addAdViewToWrapper(@NonNull ViewGroup wrapper, @NonNull AdView ad) {
        wrapper.addView(ad);
    }

    /**
     * This method can be overridden to recycle (remove) {@param ad} from {@param wrapper} view before adding to wrap.
     * By default it will look for {@param ad} in the direct children of {@param wrapper} and remove the first occurrence.
     * See the super's implementation for instance.
     * The NativeExpressHolder recycled by the RecyclerView may be a different
     * instance than the one used previously for this position. Clear the
     * wrapper of any subviews in case it has a different
     * AdView associated with it
     */
    @Override
    protected void recycleAdViewWrapper(@NonNull ViewGroup wrapper, @NonNull AdView ad) {
        for (int i = 0; i < wrapper.getChildCount(); i++) {
            View v = wrapper.getChildAt(i);
            if (v instanceof AdView) {
                wrapper.removeViewAt(i);
                break;
            }
        }
    }

    /**
     * This method can be overridden to wrap the created ad view with a custom {@link ViewGroup}.<br/>
     * For example if you need to wrap the ad with your custom CardView
     * @return The wrapper {@link ViewGroup} for ad, by default {@link AdView} ad would be wrapped with a CardView which is returned by this method
     */
    @Override
    @NonNull
    protected ViewGroup getAdViewWrapper(ViewGroup parent) {
        return (ViewGroup) LayoutInflater.from(parent.getContext()).inflate(R.layout.web_ad_container,
                parent, false);
    }
}


/*
 * Copyright (c) 2017 Yahoo Inc. All rights reserved.
 * Copyright (c) 2017 Clockbyte LLC. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.clockbyte.admobadapter;

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.ViewGroup;

import com.google.android.gms.ads.formats.NativeAd;
import com.google.android.gms.ads.formats.NativeAdView;
import com.google.android.gms.ads.formats.NativeAppInstallAd;
import com.google.android.gms.ads.formats.NativeAppInstallAdView;
import com.google.android.gms.ads.formats.NativeContentAd;
import com.google.android.gms.ads.formats.NativeContentAdView;

import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;

/**
 * Adapter that has common functionality for any adapters that need to show ads in-between
 * other data.
 */
public class AdmobRecyclerAdapterWrapper
        extends RecyclerView.Adapter<RecyclerView.ViewHolder>
        implements AdmobFetcherBase.AdmobListener {

    private final String TAG = AdmobRecyclerAdapterWrapper.class.getCanonicalName();

    private RecyclerView.Adapter<RecyclerView.ViewHolder> mAdapter;

    public RecyclerView.Adapter<RecyclerView.ViewHolder> getAdapter() {
        return mAdapter;
    }

    public void setAdapter(RecyclerView.Adapter<RecyclerView.ViewHolder> adapter) {
        mAdapter = adapter;
        mAdapter.registerAdapterDataObserver(new AdapterWrapperObserver(this, getAdapterCalculator(), adFetcher));
        notifyDataSetChanged();
    }

    private AdmobFetcher adFetcher;
    private Context mContext;
    private AdmobAdapterCalculator AdapterCalculator = new AdmobAdapterCalculator();
    /*
    * Gets an object which incapsulates transformation of the source and ad blocks indices
    */
    public AdmobAdapterCalculator getAdapterCalculator(){return AdapterCalculator;}
    /*
* Injects an object which incapsulates transformation of the source and ad blocks indices. You could override calculations
* by inheritance of AdmobAdapterCalculator class
*/
    public void setAdapterCalculator(AdmobAdapterCalculator adapterCalculatordmob){AdapterCalculator = adapterCalculatordmob;}

    private static final int VIEW_TYPE_AD_CONTENT = 0;
    private static final int VIEW_TYPE_AD_INSTALL = 1;

    private final static int DEFAULT_NO_OF_DATA_BETWEEN_ADS = 10;
    private final static int DEFAULT_LIMIT_OF_ADS = 3;
    private final static int DEFAULT_VIEWTYPE_SOURCE_MAX = 0;

    /**
     * Gets the number of ads that have been fetched so far.
     *
     * @return the number of ads that have been fetched
     */
    public int getFetchedAdsCount() {
        return adFetcher.getFetchedAdsCount();
    }

    /**
     * Gets the number of ads have been fetched so far + currently fetching ads
     *
     * @return the number of already fetched ads + currently fetching ads
     */
    public int getFetchingAdsCount(){
        return adFetcher.getFetchingAdsCount();
    }

    private int getViewTypeAdContent(){
        return getViewTypeBiggestSource() + VIEW_TYPE_AD_CONTENT + 1;
    }

    private int getViewTypeAdInstall(){
        return getViewTypeBiggestSource() + VIEW_TYPE_AD_INSTALL + 1;
    }

    /*
   * Gets the number of your data items between ad blocks, by default it equals to 10.
   * You should set it according to the Admob's policies and rules which says not to
   * display more than one ad block at the visible part of the screen
   * so you should choose this parameter carefully and according to your item's height and screen resolution of a target devices
   */
    public int getNoOfDataBetweenAds() {
        return AdapterCalculator.getNoOfDataBetweenAds();
    }
    /*
    * Sets the number of your data items between ad blocks, by default it equals to 10.
    * You should set it according to the Admob's policies and rules which says not to
    * display more than one ad block at the visible part of the screen
    * so you should choose this parameter carefully and according to your item's height and screen resolution of a target devices
    */
    public void setNoOfDataBetweenAds(int mNoOfDataBetweenAds) {
        AdapterCalculator.setNoOfDataBetweenAds(mNoOfDataBetweenAds);
    }

    public int getFirstAdIndex() {
        return AdapterCalculator.getFirstAdIndex();
    }
    /*
    * Sets the first ad block index (zero-based) in the adapter, by default it equals to 0
    */
    public void setFirstAdIndex(int firstAdIndex) {
        AdapterCalculator.setFirstAdIndex(firstAdIndex);
    }

    /*
    * Gets the max count of ad blocks per dataset, by default it equals to 3 (according to the Admob's policies and rules)
    */
    public int getLimitOfAds() {
        return AdapterCalculator.getLimitOfAds();
    }

    /*
    * Sets the max count of ad blocks per dataset, by default it equals to 3 (according to the Admob's policies and rules)
    */
    public void setLimitOfAds(int mLimitOfAds) {
        AdapterCalculator.setLimitOfAds(mLimitOfAds);
    }

    private int viewTypeBiggestSource;
    /*
   * Gets the biggest value among all the view types in the underlying source adapter, by default it equals to 0.
   */
    public int getViewTypeBiggestSource() {
        return viewTypeBiggestSource;
    }

    /*
    * Sets the biggest value among all the view types in the underlying source adapter, by default it equals to 0.
    */
    public void setViewTypeBiggestSource(int viewTypeBiggestSource) {
        this.viewTypeBiggestSource = viewTypeBiggestSource;
    }

    private NativeAdLayoutContext mContentAdsLayoutContext;

    /*
    * Gets the context (the res layout id and a strategy of inflating and binding) for published content ads {@link https://support.google.com/admob/answer/6240809}
    */
    public NativeAdLayoutContext getContentAdsLayoutContext() {
        return mContentAdsLayoutContext;
    }

    /*
    * Sets the context (the res layout id and a strategy of inflating and binding) for published content ads {@link https://support.google.com/admob/answer/6240809}
    */
    public void setContentAdsLayoutContext(NativeAdLayoutContext mContentAdsLayoutContext) {
        this.mContentAdsLayoutContext = mContentAdsLayoutContext;
    }

    private NativeAdLayoutContext mInstallAdsLayoutContext;

    /*
    * Gets the context (the res layout id and a strategy of inflating and binding) for published install app ads {@link https://support.google.com/admob/answer/6240809}
    */
    public NativeAdLayoutContext getInstallAdsLayoutContext() {
        return mInstallAdsLayoutContext;
    }

    /*
    * Sets the context (the res layout id and a strategy of inflating and binding) for published install app ads {@link https://support.google.com/admob/answer/6240809}
    */
    public void setInstallAdsLayoutContext(NativeAdLayoutContext mInstallAdsLayoutContext) {
        this.mInstallAdsLayoutContext = mInstallAdsLayoutContext;
    }

    /**
     * Use this constructor for test purposes. if you are going to release the live version
     * please use the appropriate constructor
     * @see #AdmobRecyclerAdapterWrapper(Context, String)
     * @param testDevicesId sets a devices ID to test ads interaction.
     * You could pass null but it's better to set ids for all your test devices
     * including emulators. for emulator just use the
     * @see {AdRequest.DEVICE_ID_EMULATOR}
     */
    public AdmobRecyclerAdapterWrapper(Context context, String[] testDevicesId) {
        this(context, testDevicesId, EnumSet.allOf(EAdType.class));
    }
    /**
     * @param admobReleaseUnitId sets a release unit ID for admob banners.
     * If you are testing the ads please pass null
     * ID should be active, please check it in your Admob's account.
     * Be careful: don't set it or set to null if you still haven't deployed a Release.
     * Otherwise your Admob account could be banned
     */
    public AdmobRecyclerAdapterWrapper(Context context, String admobReleaseUnitId) {
        this(context, admobReleaseUnitId, EnumSet.allOf(EAdType.class));
    }

    /**
     * @param admobReleaseUnitIds sets a release unit IDs for admob banners.
     * It works like FIFO (first in = first out). Each ad block will get one from the queue.
     * If the desired count of ad blocks is greater than this collection size
     * then the last entry will be duplicated to remaining ad blocks.
     * If you are testing the ads please use constructor for tests
     * @see #AdmobRecyclerAdapterWrapper(Context, String[])
     * ID should be active, please check it in your Admob's account.
     * Be careful: don't set it or set to null if you still haven't deployed a Release.
     * Otherwise your Admob account could be banned
     */
    public AdmobRecyclerAdapterWrapper(Context context, Collection<String> admobReleaseUnitIds) {
        this(context, admobReleaseUnitIds, EnumSet.allOf(EAdType.class));
    }

    /**
     * Use this constructor for test purposes. if you are going to release the live version
     * please use the appropriate constructor
     * @see #AdmobRecyclerAdapterWrapper(Context, String)
     * @param testDevicesId sets a devices ID to test ads interaction.
     * You could pass null but it's better to set ids for all your test devices
     * including emulators. for emulator just use the
     * @see {AdRequest.DEVICE_ID_EMULATOR}
     * @param adTypesToShow sets the types of ads to show in the list.
     * By default all types are loaded by wrapper.
     * i.e. pass EnumSet.of(EAdType.ADVANCED_INSTALLAPP) to show only install app ads
     */
    public AdmobRecyclerAdapterWrapper(Context context, String[] testDevicesId, EnumSet<EAdType> adTypesToShow) {
        init(context, null, testDevicesId, adTypesToShow);
    }

    /**
     * @param admobReleaseUnitId sets a release unit ID for admob banners.
     * If you are testing the ads please use constructor for tests
     * @see #AdmobRecyclerAdapterWrapper(Context, String[])
     * ID should be active, please check it in your Admob's account.
     * Be careful: don't set it or set to null if you still haven't deployed a Release.
     * Otherwise your Admob account could be banned
     * @param adTypesToShow sets the types of ads to show in the list.
     * By default all types are loaded by wrapper.
     * i.e. pass EnumSet.of(EAdType.ADVANCED_INSTALLAPP) to show only install app ads
     */
    public AdmobRecyclerAdapterWrapper(Context context, String admobReleaseUnitId, EnumSet<EAdType> adTypesToShow) {
        Collection<String> releaseUnitIds = admobReleaseUnitId==null
                ? null
                : Collections.singletonList(admobReleaseUnitId);
        init(context, releaseUnitIds, null, adTypesToShow);
    }

    /**
     * @param admobReleaseUnitIds sets a release unit ID for admob banners.
     * It works like FIFO (first in = first out). Each ad block will get one from the queue.
     * If the desired count of ad blocks is greater than this collection size
     * then the last entry will be duplicated to remaining ad blocks.
     * If you are testing the ads please use constructor for tests
     * @see #AdmobRecyclerAdapterWrapper(Context, String[])
     * ID should be active, please check it in your Admob's account.
     * Be careful: don't set it or set to null if you still haven't deployed a Release.
     * Otherwise your Admob account could be banned
     * @param adTypesToShow sets the types of ads to show in the list.
     * By default all types are loaded by wrapper.
     * i.e. pass EnumSet.of(EAdType.ADVANCED_INSTALLAPP) to show only install app ads
     */
    public AdmobRecyclerAdapterWrapper(Context context, Collection<String> admobReleaseUnitIds, EnumSet<EAdType> adTypesToShow) {
        init(context, admobReleaseUnitIds, null, adTypesToShow);
    }

    /**
     * @param admobReleaseUnitId sets a release unit ID for admob banners.
     * If you are testing the ads please use constructor for tests
     * @see #AdmobRecyclerAdapterWrapper(Context, String[]) or supply a
     * test ID here.
     * ID should be active, please check it in your Admob's account.
     * Be careful: don't set it or set to null if you still haven't deployed a Release.
     * Otherwise your Admob account could be banned
     * @param adTypesToShow sets the types of ads to show in the list.
     * By default all types are loaded by wrapper.
     * i.e. pass EnumSet.of(EAdType.ADVANCED_INSTALLAPP) to show only install app ads
     */
    public AdmobRecyclerAdapterWrapper(Context context, String admobReleaseUnitId, String[] testDevicesId, EnumSet<EAdType> adTypesToShow) {
        init(context, Collections.singletonList(admobReleaseUnitId), testDevicesId, adTypesToShow);
    }

    private void init(Context context, Collection<String> admobReleaseUnitIds, String[] testDevicesId, EnumSet<EAdType> adTypesToShow){
        setViewTypeBiggestSource(DEFAULT_VIEWTYPE_SOURCE_MAX);
        setNoOfDataBetweenAds(DEFAULT_NO_OF_DATA_BETWEEN_ADS);
        setLimitOfAds(DEFAULT_LIMIT_OF_ADS);
        setContentAdsLayoutContext(ContentAdLayoutContext.getDefault());
        setInstallAdsLayoutContext(InstallAppAdLayoutContext.getDefault());
        mContext = context;

        adFetcher = new AdmobFetcher();
        if(testDevicesId!=null)
            for (String testId: testDevicesId)
                adFetcher.addTestDeviceId(testId);
        if(admobReleaseUnitIds!=null)
            adFetcher.setReleaseUnitIds(admobReleaseUnitIds);
        adFetcher.setAdTypeToFetch(adTypesToShow == null || adTypesToShow.isEmpty()
                ?  EnumSet.allOf(EAdType.class): adTypesToShow);
        adFetcher.addListener(this);
        // Start prefetching ads
        adFetcher.prefetchAds(context.getApplicationContext());
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
        if (viewHolder==null)
            return;
        int itemViewType = viewHolder.getItemViewType();
        if(itemViewType == getViewTypeAdInstall()) {
            NativeAppInstallAdView lvi1 = (NativeAppInstallAdView) viewHolder.itemView;
            NativeAppInstallAd ad1 = (NativeAppInstallAd) getItem(position);
            getInstallAdsLayoutContext().bind(lvi1, ad1);
        }
        else if(itemViewType == getViewTypeAdContent()) {
            NativeContentAdView lvi2 = (NativeContentAdView) viewHolder.itemView;
            NativeContentAd ad2 = (NativeContentAd) getItem(position);
            getContentAdsLayoutContext().bind(lvi2, ad2);
        }
        else{
            int origPos = AdapterCalculator.getOriginalContentPosition(position,
                    adFetcher.getFetchedAdsCount(), mAdapter.getItemCount());
            mAdapter.onBindViewHolder(viewHolder, origPos);
        }
    }

    @Override
    public final RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        if (viewType == getViewTypeAdContent() || viewType == getViewTypeAdInstall()) {
            return new NativeHolder(onCreateItemView(parent, viewType));
        }
        else{
            return mAdapter.onCreateViewHolder(parent, viewType);
        }
    }

    private NativeAdView onCreateItemView(ViewGroup parent, int viewType) {
        if (viewType == getViewTypeAdInstall())
                return getInstallAdsLayoutContext().inflateView(parent);
        else if (viewType == getViewTypeAdContent())
                return getContentAdsLayoutContext().inflateView(parent);
        else return null;
    }

    /**
     *
Gets the count of all data, including interspersed ads.

     *

     *
If data size is 10 and an ad is to be showed after every 5 items starting at the index 0, this method
     * will return 12.

     *
     * @return the total number of items this adapter can show, including ads.
     * @see AdmobRecyclerAdapterWrapper#setNoOfDataBetweenAds(int)
     * @see AdmobRecyclerAdapterWrapper#getNoOfDataBetweenAds()
     */
    @Override
    public int getItemCount() {

        if (mAdapter != null) {
            /*
            No of currently fetched ads, as long as it isn't more than no of max ads that can
            fit dataset.
             */
            int noOfAds = AdapterCalculator.getAdsCountToPublish(adFetcher.getFetchedAdsCount(), mAdapter.getItemCount());
            return mAdapter.getItemCount() > 0 ? mAdapter.getItemCount() + noOfAds : 0;
        } else {
            return 0;
        }
    }

    /**
     * Gets the item in a given position in the dataset. If an ad is to be returned,
     * a {@link NativeAd} object is returned.
     *
     *
     * @return the object or ad contained in this adapter position
     */
    public Object getItem(int position) {

        if (AdapterCalculator.canShowAdAtPosition(position, adFetcher.getFetchedAdsCount())) {
            int adPos = AdapterCalculator.getAdIndex(position);
            return adFetcher.getAdForIndex(adPos);
        }
        else return null;
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public int getItemViewType(int position) {
        if (AdapterCalculator.canShowAdAtPosition(position, adFetcher.getFetchedAdsCount())) {
            int adPos = AdapterCalculator.getAdIndex(position);
            NativeAd ad = adFetcher.getAdForIndex(adPos);
            return ad instanceof NativeAppInstallAd ? getViewTypeAdInstall() : getViewTypeAdContent();
        } else {
            int origPos = AdapterCalculator.getOriginalContentPosition(position,
                    adFetcher.getFetchedAdsCount(), mAdapter.getItemCount());
            return mAdapter.getItemViewType(origPos);
        }
    }

    /**
     * Destroys all currently fetched ads
     */
    public void destroyAds() {
        adFetcher.destroyAllAds();
    }

    /**
     * Clears all currently displaying ads to update them
     */
    public void requestUpdateAd() {
        adFetcher.clearMapAds();
    }

    @Override
    public void onAdLoaded(int adIdx) {
        notifyDataSetChanged();
    }

    @Override
    public void onAdsCountChanged() {
        notifyDataSetChanged();
    }

    @Override
    public void onAdFailed(int adIdx, int errorCode, Object adPayload) {
        notifyDataSetChanged();
    }

}


/*
 * Copyright (c) 2017 Yahoo Inc. All rights reserved.
 * Copyright (c) 2017 Clockbyte LLC. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.clockbyte.admobadapter;

public class AdmobAdapterCalculator {

    protected int mNoOfDataBetweenAds;
    /*
    * Gets the number of your data items between ad blocks, by default it equals to 10.
    * You should set it according to the Admob's policies and rules which says not to
    * display more than one ad block at the visible part of the screen
    * so you should choose this parameter carefully and according to your item's height and screen resolution of a target devices
    */
    public int getNoOfDataBetweenAds() {
        return mNoOfDataBetweenAds;
    }

    /*
    * Sets the number of your data items between ad blocks, by default it equals to 10.
    * You should set it according to the Admob's policies and rules which says not to
    * display more than one ad block at the visible part of the screen
    * so you should choose this parameter carefully and according to your item's height and screen resolution of a target devices
    */
    public void setNoOfDataBetweenAds(int mNoOfDataBetweenAds) {
        this.mNoOfDataBetweenAds = mNoOfDataBetweenAds;
    }

    protected int firstAdIndex = 0;

    public int getFirstAdIndex() {
        return firstAdIndex;
    }

    /*
    * Sets the first ad block index (zero-based) in the adapter, by default it equals to 0
    */
    public void setFirstAdIndex(int firstAdIndex) {
        this.firstAdIndex = firstAdIndex;
    }

    protected int mLimitOfAds;

    /*
    * Gets the max count of ad blocks per dataset, by default it equals to 3 (according to the Admob's policies and rules)
    */
    public int getLimitOfAds() {
        return mLimitOfAds;
    }

    /*
    * Sets the max count of ad blocks per dataset, by default it equals to 3 (according to the Admob's policies and rules)
    */
    public void setLimitOfAds(int mLimitOfAds) {
        this.mLimitOfAds = mLimitOfAds;
    }

    /**
     * Gets the count of ads that could be published
     *
     * @param fetchedAdsCount the count of completely fetched ads that are ready to be published
     * @param sourceItemsCount the count of items in the source collection
     * @return the original position that the adapter position would have been without ads
     */
    public int getAdsCountToPublish(int fetchedAdsCount, int sourceItemsCount){
        if(fetchedAdsCount <= 0 || getNoOfDataBetweenAds() <= 0) return 0;
        int expected = 0;
        if(sourceItemsCount > 0 && sourceItemsCount >= getOffsetValue()+1)
            expected = (sourceItemsCount - getOffsetValue()) / getNoOfDataBetweenAds() + 1;
        expected = Math.max(0, expected);
        expected = Math.min(fetchedAdsCount, expected);
        return Math.min(expected, getLimitOfAds());
    }

    /**
     * Translates an adapter position to an actual position within the underlying dataset.
     *
     * @param position the adapter position
     * @param fetchedAdsCount the count of completely fetched ads that are ready to be published
     * @param sourceItemsCount the count of items in the source collection
     * @return the original position that the adapter position would have been without ads
     */
    public int getOriginalContentPosition(int position, int fetchedAdsCount, int sourceItemsCount) {
        int noOfAds = getAdsCountToPublish(fetchedAdsCount, sourceItemsCount);
        // No of spaces for ads in the dataset, according to ad placement rules
        int adSpacesCount = (getAdIndex(position) + 1);
        int originalPosition = position - Math.min(adSpacesCount, noOfAds);
        //Log.d("POSITION", position + " is originally " + originalPosition);
        return originalPosition;
    }

    /**
     * Translates an ad position to an actual position within the adapter wrapper.
     *
     * @param adPos the ad's position in the fetched list
     * @return the position of the adapter wrapper item
     */
    public int translateAdToWrapperPosition(int adPos) {
        int wrappedPosition = adPos*(getNoOfDataBetweenAds() + 1) + getOffsetValue();
        return wrappedPosition;
    }

    /**
     * Translates the source position to an actual position within the adapter wrapper.
     * @param fetchedAdsCount the count of completely fetched ads that are ready to be published
     * @param sourcePos the source index
     *
     * @return the position of the adapter wrapper item
     */
    public int translateSourceIndexToWrapperPosition(int sourcePos, int fetchedAdsCount) {
        int adSpacesCount = 0;
        if(sourcePos >= getOffsetValue() && getNoOfDataBetweenAds() > 0)
            adSpacesCount = (sourcePos - getOffsetValue())/getNoOfDataBetweenAds() + 1;
        adSpacesCount = Math.min(fetchedAdsCount, adSpacesCount);
        adSpacesCount = Math.max(0, adSpacesCount);
        adSpacesCount = Math.min(adSpacesCount, getLimitOfAds());
        int wrappedPosition = sourcePos + adSpacesCount;
        return wrappedPosition;
    }

    /**
     * Determines if an ad can be shown at the given position. Checks if the position is for
     * an ad, using the preconfigured ad positioning rules; and if a native ad object is
     * available to place in that position.
     *
     * @param position the adapter position
     * @param fetchedAdsCount the count of completely fetched ads that are ready to be published
     * @return true if ads can
     */
    public boolean canShowAdAtPosition(int position, int fetchedAdsCount) {

        // Is this a valid position for an ad?
        // Is an ad for this position available?
        return isAdPosition(position) && isAdAvailable(position, fetchedAdsCount);
    }

    /**
     * Gets the ad index for this adapter position within the list of currently fetched ads.
     *
     * @param position the adapter position
     * @return the index of the ad within the list of fetched ads
     */
    public int getAdIndex(int position) {
        int index = -1;
        if(position >= getOffsetValue())
            index = (position - getOffsetValue()) / (getNoOfDataBetweenAds()+1);
        //Log.d("POSITION", "index " + index + " for position " + position);
        return index;
    }

    /**
     * Checks if adapter position is an ad position.
     *
     * @param position the adapter position
     * @return {@code true} if an ad position, {@code false} otherwise
     */
    public boolean isAdPosition(int position) {
        int result = (position - getOffsetValue()) % (getNoOfDataBetweenAds() + 1);
        return result == 0;
    }

    public int getOffsetValue() {
        return getFirstAdIndex() > 0 ? getFirstAdIndex() : 0;
    }

    /**
     * Checks if an ad is available for this position.
     *
     * @param position the adapter position
     * @param fetchedAdsCount the count of completely fetched ads that are ready to be published
     * @return {@code true} if an ad is available, {@code false} otherwise
     */
    public boolean isAdAvailable(int position, int fetchedAdsCount) {
        if(fetchedAdsCount == 0) return false;
        int adIndex = getAdIndex(position);
        int firstAdPos = getOffsetValue();

        return position >= firstAdPos && adIndex >= 0 && adIndex < getLimitOfAds() && adIndex < fetchedAdsCount;
    }

    /**
     * Checks if we have to request the next ad block for this position.
     *
     * @param position the adapter position
     * @param fetchingAdsCount the count of fetched and currently fetching ads
     * @return {@code true} if an ad is not available to publish and we should fetch one, {@code false} otherwise
     */
    public boolean hasToFetchAd(int position, int fetchingAdsCount){
        int adIndex = getAdIndex(position);
        int firstAdPos = getOffsetValue();
        return  position >= firstAdPos && adIndex >= 0 && adIndex < getLimitOfAds() && adIndex >= fetchingAdsCount;
    }
}


package com.clockbyte.admobadapter.expressads;

import android.support.annotation.NonNull;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import com.clockbyte.admobadapter.R;
import com.google.android.gms.ads.NativeExpressAdView;

/**
 * Created by fim on 31.08.2017.
 * @deprecated Use banners instead
 */
@Deprecated
public class AdViewWrappingStrategy extends AdViewWrappingStrategyBase {
    /**
     * Add the Native Express {@param ad} to {@param wrapper}.
     * See the super's implementation for instance.
     */
    @Override
    protected void addAdViewToWrapper(@NonNull ViewGroup wrapper, @NonNull NativeExpressAdView ad) {
        wrapper.addView(ad);
    }

    /**
     * This method can be overriden to recycle (remove) {@param ad} from {@param wrapper} view before adding to wrap.
     * By default it will look for {@param ad} in the direct children of {@param wrapper} and remove the first occurence.
     * See the super's implementation for instance.
     * The NativeExpressHolder recycled by the RecyclerView may be a different
     * instance than the one used previously for this position. Clear the
     * wrapper of any subviews in case it has a different
     * AdView associated with it
     */
    @Override
    protected void recycleAdViewWrapper(@NonNull ViewGroup wrapper, @NonNull NativeExpressAdView ad) {
        for (int i = 0; i < wrapper.getChildCount(); i++) {
            View v = wrapper.getChildAt(i);
            if (v instanceof NativeExpressAdView) {
                wrapper.removeViewAt(i);
                break;
            }
        }
    }

    /**
     * This method can be overriden to wrap the created ad view with a custom {@link ViewGroup}.<br/>
     * For example if you need to wrap the ad with your custom CardView
     * @return The wrapper {@link ViewGroup} for ad, by default {@link NativeExpressAdView} ad would be wrapped with a CardView which is returned by this method
     */
    @Override
    @NonNull
    protected ViewGroup getAdViewWrapper(ViewGroup parent) {
        return (ViewGroup) LayoutInflater.from(parent.getContext()).inflate(R.layout.web_ad_container,
                parent, false);
    }
}


package com.clockbyte.admobadapter.sampleapp;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;

import com.clockbyte.admobadapter.AdmobAdapterCalculator;
import com.clockbyte.admobadapter.AdmobAdapterWrapper;
import com.google.android.gms.ads.AdRequest;
import com.google.android.gms.ads.MobileAds;

import java.util.ArrayList;
import java.util.Timer;
import java.util.TimerTask;

public class MainActivity_ListView extends Activity {

    ListView lvMessages;
    AdmobAdapterWrapper adapterWrapper;
    Timer updateAdsTimer;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main_listview);

        //highly-recommended in Firebase docs to initialize things early as possible
        //test_admob_app_id is different with unit_id! you could get it in your Admob console
        MobileAds.initialize(getApplicationContext(), getString(R.string.test_admob_app_id));

        initListViewItems();
        initUpdateAdsTimer();
    }

    /**
     * Inits an adapter with items, wrapping your adapter with a {@link AdmobAdapterWrapper} and setting the listview to this wrapper
     * FIRST OF ALL Please notice that the following code will work on a real devices but emulator!
     */
    private void initListViewItems() {
        lvMessages = (ListView) findViewById(R.id.lvMessages);

        //creating your adapter, it could be a custom adapter as well
        ArrayAdapter<String> adapter  = new ArrayAdapter<String>(this,
                android.R.layout.simple_list_item_1);

        //your test devices' ids
        String[] testDevicesIds = new String[]{getString(R.string.testDeviceID),AdRequest.DEVICE_ID_EMULATOR};
        //when you'll be ready for release please use another ctor with admobReleaseUnitId instead.
        adapterWrapper = new AdmobAdapterWrapper(this, testDevicesIds);
        //By default both types of ads are loaded by wrapper.
        // To set which of them to show in the list you should use an appropriate ctor
        //adapterWrapper = new AdmobAdapterWrapper(this, testDevicesIds, EnumSet.of(EAdType.ADVANCED_INSTALLAPP));

        //wrapping your adapter with a AdmobAdapterWrapper.
        adapterWrapper.setAdapter(adapter);
        //inject your custom layout and strategy of binding for installapp/content  ads
        //here you should pass the extended NativeAdLayoutContext
        //by default it has a value InstallAppAdLayoutContext.getDefault()
        //adapterWrapper.setInstallAdsLayoutContext(...);
        //by default it has a value ContentAdLayoutContext.getDefault()
        //adapterWrapper.setContentAdsLayoutContext(...);

        //Sets the max count of ad blocks per dataset, by default it equals to 3 (according to the Admob's policies and rules)
        adapterWrapper.setLimitOfAds(3);

        //Sets the number of your data items between ad blocks, by default it equals to 10.
        //You should set it according to the Admob's policies and rules which says not to
        //display more than one ad block at the visible part of the screen,
        // so you should choose this parameter carefully and according to your item's height and screen resolution of a target devices
        adapterWrapper.setNoOfDataBetweenAds(10);

        adapterWrapper.setFirstAdIndex(2);

        lvMessages.setAdapter(adapterWrapper); // setting an AdmobAdapterWrapper to a ListView
        lvMessages.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                final AdmobAdapterCalculator adapterCalc = adapterWrapper.getAdapterCalculator();
                int fetchedAdsCnt = adapterWrapper.getFetchedAdsCount();
                int sourceCnt = adapterWrapper.getAdapter().getCount();
                int originalPos = adapterCalc.getOriginalContentPosition(position, fetchedAdsCnt, sourceCnt);
                Toast.makeText(getApplicationContext(),
                        "Click: " + String.valueOf(originalPos),
                        Toast.LENGTH_SHORT).show();
            }
        });

        //preparing the collection of data
        final String sItem = "item #";
        ArrayList<String> lst = new ArrayList<String>(100);
        for(int i=1;i<=100;i++)
            lst.add(sItem.concat(Integer.toString(i)));

        //adding a collection of data to your adapter and rising the data set changed event
        adapter.addAll(lst);
        adapter.notifyDataSetChanged();
    }

    /*
    * Could be omitted. It's only for updating an ad blocks in each 60 seconds without refreshing the list
     */
    private void initUpdateAdsTimer() {
        updateAdsTimer = new Timer();
        updateAdsTimer.schedule(new TimerTask() {
            @Override
            public void run() {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        adapterWrapper.requestUpdateAd();
                    }
                });
            }
        }, 60*1000, 60*1000);
    }

    /*
    * Seems to be a good practice to destroy all the resources you have used earlier :)
     */
    @Override
    protected void onDestroy() {
        super.onDestroy();
        if(updateAdsTimer!=null)
            updateAdsTimer.cancel();
        adapterWrapper.destroyAds();
    }
}


/*
 * Copyright (c) 2017 Yahoo Inc. All rights reserved.
 * Copyright (c) 2017 Clockbyte LLC. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.clockbyte.admobadapter.expressads;

import android.content.Context;
import android.os.Handler;
import android.util.Log;

import com.clockbyte.admobadapter.AdPreset;
import com.clockbyte.admobadapter.AdPresetCyclingList;
import com.clockbyte.admobadapter.AdmobFetcherBase;
import com.google.android.gms.ads.AdListener;
import com.google.android.gms.ads.NativeExpressAdView;

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

/**
 * @deprecated Use banners instead
 */
@Deprecated
public class AdmobFetcherExpress extends AdmobFetcherBase {

    private final String TAG = AdmobFetcherExpress.class.getCanonicalName();

    /**
     * Maximum number of ads to prefetch.
     */
    public static final int PREFETCHED_ADS_SIZE = 2;

    /**
     * Maximum number of times to try fetch an ad after failed attempts.
     */
    public static final int MAX_FETCH_ATTEMPT = 4;

    public AdmobFetcherExpress(Context context){
        super();
        mContext = new WeakReference<>(context);
    }

    private List<NativeExpressAdView> mPrefetchedAds = new ArrayList<>();
    private AdPresetCyclingList mAdPresetCyclingList = new AdPresetCyclingList();

    /*
* Gets next ad preset for Admob banners from FIFO .
* It works like cycling FIFO (first in = first out, cycling from end to start).
* Each ad block will get one from the queue.
* If the desired count of ad blocks is greater than this collection size
* then it will go again to the first item and iterate as much as it required.
* ID should be active, please check it in your Admob's account.
 */
    public AdPreset takeNextAdPreset() {
        return this.mAdPresetCyclingList.get();
    }

    /*
   * Sets an unit IDs for admob banners.
* It works like cycling FIFO (first in = first out, cycling from end to start).
* Each ad block will get one from the queue.
* If the desired count of ad blocks is greater than this collection size
* then it will go again to the first item and iterate as much as it required.
* ID should be active, please check it in your Admob's account.
 */
    public void setAdPresets(Collection<AdPreset> adPresets) {
        Collection<AdPreset> mAdPresets = adPresets==null||adPresets.size() == 0
                ? Collections.singletonList(AdPreset.DEFAULT)
                :adPresets;
        mAdPresetCyclingList.clear();
        mAdPresetCyclingList.addAll(mAdPresets);
    }

    public int getAdPresetsCount(){
        return this.mAdPresetCyclingList.size();
    }

    public AdPreset getAdPresetSingleOr(AdPreset defaultValue){
        return this.mAdPresetCyclingList.size() == 1 ? this.mAdPresetCyclingList.get() : defaultValue;
    }

    /**
     * Gets native ad at a particular index in the fetched ads list.
     *
     * @param adPos the index of ad in the fetched ads list
     * @return the native ad in the list
     * @see #getFetchedAdsCount()
     */
    public synchronized NativeExpressAdView getAdForIndex(int adPos) {
        if(adPos >= 0 && mPrefetchedAds.size() > adPos)
            return mPrefetchedAds.get(adPos);
        return null;
    }

    /**
     * Fetches a new native ad.
     */
    protected synchronized void fetchAd(final NativeExpressAdView adView) {
        if(mFetchFailCount > MAX_FETCH_ATTEMPT)
            return;

        Context context = mContext.get();

        if (context != null) {
            Log.i(TAG, "Fetching Ad now");
            new Handler(context.getMainLooper()).post(new Runnable() {
                @Override
                public void run() {
                    adView.loadAd(getAdRequest()); //Fetching the ads item
                }
            });
        } else {
            mFetchFailCount++;
            Log.i(TAG, "Context is null, not fetching Ad");
        }
    }

    /**
     * Subscribing to the native ads events
     * @param adView
     */
    protected synchronized void setupAd(final NativeExpressAdView adView) {
        if(mFetchFailCount > MAX_FETCH_ATTEMPT)
            return;

        if(!mPrefetchedAds.contains(adView))
            mPrefetchedAds.add(adView);
        adView.setAdListener(new AdListener() {
            @Override
            public void onAdFailedToLoad(int errorCode) {
                super.onAdFailedToLoad(errorCode);
                // Handle the failure by logging, altering the UI, etc.
                onFailedToLoad(adView, errorCode);
            }
            @Override
            public void onAdLoaded() {
                super.onAdLoaded();
                onFetched(adView);
            }
        });
    }

    /**
     * A handler for received native ads
     * @param adView
     */
    private synchronized void onFetched(NativeExpressAdView adView) {
        Log.i(TAG, "onAdFetched");
        mFetchFailCount = 0;
        mNoOfFetchedAds++;
        onAdLoaded(mNoOfFetchedAds - 1);
    }

    /**
     * A handler for failed native ads
     * @param adView
     */
    private synchronized void onFailedToLoad(NativeExpressAdView adView, int errorCode) {
        Log.i(TAG, "onAdFailedToLoad " + errorCode);
        mFetchFailCount++;
        mNoOfFetchedAds = Math.max(mNoOfFetchedAds - 1, 0);
        //Since Fetch Ad is only called once without retries
        //hide ad row / rollback its count if still not added to list
        mPrefetchedAds.remove(adView);
        onAdFailed(mNoOfFetchedAds - 1, errorCode, adView);
    }

    @Override
    public synchronized int getFetchingAdsCount() {
        return mPrefetchedAds.size();
    }

    @Override
    public synchronized void destroyAllAds() {
        super.destroyAllAds();
        for(NativeExpressAdView ad:mPrefetchedAds){
            ad.destroy();
        }
        mPrefetchedAds.clear();
    }

    @Override
    public void release() {
        super.release();
        mAdPresetCyclingList.clear();
    }
}


package com.hannesdorfmann.parcelableplease.processor.codegenerator;

import com.hannesdorfmann.parcelableplease.annotation.Bagger;
import com.hannesdorfmann.parcelableplease.processor.ProcessorMessage;
import com.hannesdorfmann.parcelableplease.processor.codegenerator.android.BundleCodeGen;
import com.hannesdorfmann.parcelableplease.processor.codegenerator.android.ParcelableCodeGen;
import com.hannesdorfmann.parcelableplease.processor.codegenerator.android.SparseBooleanCodeGen;
import com.hannesdorfmann.parcelableplease.processor.codegenerator.collection.AbsListCodeGen;
import com.hannesdorfmann.parcelableplease.processor.codegenerator.collection.ParcelableArrayCodeGen;
import com.hannesdorfmann.parcelableplease.processor.codegenerator.collection.PrimitiveArrayCodeGen;
import com.hannesdorfmann.parcelableplease.processor.codegenerator.collection.StringListCodeGen;
import com.hannesdorfmann.parcelableplease.processor.codegenerator.other.DateCodeGen;
import com.hannesdorfmann.parcelableplease.processor.codegenerator.other.SerializeableCodeGen;
import com.hannesdorfmann.parcelableplease.processor.codegenerator.primitives.BooleanCodeGen;
import com.hannesdorfmann.parcelableplease.processor.codegenerator.primitiveswrapper.AbsPrimitiveWrapperCodeGen;
import com.hannesdorfmann.parcelableplease.processor.codegenerator.primitiveswrapper.BooleanWrapperCodeGen;
import com.hannesdorfmann.parcelableplease.processor.util.CodeGenInfo;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.ArrayType;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;

/**
 * The supported types and the corresponding code generator classes
 *
 * @author Hannes Dorfmann
 */
public class SupportedTypes {

  private static final String TYPE_KEY_PARCELABLE = "android.os.Parcelable";
  private static final String TYPE_KEY_SERIALIZABLE = "java.io.Serializable";
  private static final String TYPE_KEY_PARCELABLE_ARRAYLIST = "Parcelable-ArrayList";
  private static final String TYPE_KEY_PARCELABLE_LINKEDLIST = "Parcelable-LinkedList";
  private static final String TYPE_KEY_PARCELABLE_LIST = "Parcelable-List";
  private static final String TYPE_KEY_STRING_LIST = "String-List";
  private static final String TYPE_KEY_BUNDLE = "android.os.Bundle";
  private static final String TYPE_KEY_DOUBLE_ARRAY = "Array-Double";
  private static final String TYPE_KEY_FLOAT_ARRAY = "Array-Float";
  private static final String TYPE_KEY_INT_ARRAY = "Array-Int";
  private static final String TYPE_KEY_BOOL_ARRAY = "Array-Boolean";
  private static final String TYPE_KEY_BYTE_ARRAY = "Array-Byte";
  private static final String TYPE_KEY_CHAR_ARRAY = "Array-Char";
  private static final String TYPE_KEY_LONG_ARRAY = "Array-Long";
  private static final String TYPE_KEY_STRING_ARRAY = "Array-String";
  private static final String TYPE_KEY_PARCELABLE_ARRAY = "Array-Parcelable";
  private static final String TYPE_KEY_SPARSE_BOOLEAN_ARRAY = "android.util.SparseBooleanArray";
  private static final String TYPE_KEY_SPARSE_ARRAY = "android.util.SparseArray";

  private static final String TYPE_KEY_PARCELABLE_COPYONWRITEARRAYLIST =
      "Parcelable-CopyOnWriteArrayList";

  private static Map<String, FieldCodeGen> typeMap;

  static {
    typeMap = new HashMap<String, FieldCodeGen>();

    // primitives
    typeMap.put(byte.class.getCanonicalName(), new AbsCodeGen("Byte"));
    typeMap.put(boolean.class.getCanonicalName(), new BooleanCodeGen());
    typeMap.put(double.class.getCanonicalName(), new AbsCodeGen("Double"));
    typeMap.put(float.class.getCanonicalName(), new AbsCodeGen("Float"));
    typeMap.put(int.class.getCanonicalName(), new AbsCodeGen("Int"));
    typeMap.put(long.class.getCanonicalName(), new AbsCodeGen("Long"));
    typeMap.put(String.class.getCanonicalName(), new AbsCodeGen("String"));

    // Wrapper classes
    typeMap.put(Byte.class.getCanonicalName(), new AbsPrimitiveWrapperCodeGen("Byte"));
    typeMap.put(Boolean.class.getCanonicalName(), new BooleanWrapperCodeGen());
    typeMap.put(Double.class.getCanonicalName(), new AbsPrimitiveWrapperCodeGen("Double"));
    typeMap.put(Float.class.getCanonicalName(), new AbsPrimitiveWrapperCodeGen("Float"));
    typeMap.put(Integer.class.getCanonicalName(), new AbsPrimitiveWrapperCodeGen("Int"));
    typeMap.put(Long.class.getCanonicalName(), new AbsPrimitiveWrapperCodeGen("Long"));

    // Android
    typeMap.put(TYPE_KEY_PARCELABLE, new ParcelableCodeGen());
    typeMap.put(TYPE_KEY_BUNDLE, new BundleCodeGen());
    typeMap.put(TYPE_KEY_SPARSE_BOOLEAN_ARRAY, new SparseBooleanCodeGen());
    // typeMap.put(TYPE_KEY_SPARSE_ARRAY, new SparseArrayCodeGen()); // TODO implement

    // Lists
    typeMap.put(TYPE_KEY_STRING_LIST, new StringListCodeGen());
    typeMap.put(TYPE_KEY_PARCELABLE_LIST, new AbsListCodeGen(ArrayList.class.getName()));
    typeMap.put(TYPE_KEY_PARCELABLE_ARRAYLIST, new AbsListCodeGen(ArrayList.class.getName()));
    typeMap.put(TYPE_KEY_PARCELABLE_LINKEDLIST, new AbsListCodeGen(LinkedList.class.getName()));
    typeMap.put(TYPE_KEY_PARCELABLE_COPYONWRITEARRAYLIST,
        new AbsListCodeGen(CopyOnWriteArrayList.class.getName()));

    // Arrays
    typeMap.put(TYPE_KEY_BOOL_ARRAY, new PrimitiveArrayCodeGen("BooleanArray", "boolean"));
    typeMap.put(TYPE_KEY_BYTE_ARRAY, new PrimitiveArrayCodeGen("ByteArray", "byte"));
    typeMap.put(TYPE_KEY_CHAR_ARRAY, new PrimitiveArrayCodeGen("CharArray", "char"));
    typeMap.put(TYPE_KEY_DOUBLE_ARRAY, new PrimitiveArrayCodeGen("DoubleArray", "double"));
    typeMap.put(TYPE_KEY_FLOAT_ARRAY, new PrimitiveArrayCodeGen("FloatArray", "float"));
    typeMap.put(TYPE_KEY_LONG_ARRAY, new PrimitiveArrayCodeGen("LongArray", "long"));
    typeMap.put(TYPE_KEY_INT_ARRAY, new PrimitiveArrayCodeGen("IntArray", "int"));
    typeMap.put(TYPE_KEY_STRING_ARRAY, new PrimitiveArrayCodeGen("StringArray", "String"));
    typeMap.put(TYPE_KEY_PARCELABLE_ARRAY, new ParcelableArrayCodeGen());

    // Other common classes
    typeMap.put(TYPE_KEY_SERIALIZABLE, new SerializeableCodeGen());
    typeMap.put(Date.class.getCanonicalName(), new DateCodeGen());
  }

  /**
   * Collect information about the element and the corresponding code generator
   */
  public static CodeGenInfo getCodeGenInfo(VariableElement element, Elements elements,
                                           Types types) {

    // Special classes, primitive, wrappers, etc.
    String typeKey = element.asType().toString();
    if (typeMap.get(typeKey) != null) {
      return new CodeGenInfo(typeMap.get(typeKey));
    }

    // Check if its a simple parcelable
    if (isOfType(element, "android.os.Parcelable", elements, types)) {
      return new CodeGenInfo(typeMap.get(TYPE_KEY_PARCELABLE));
    }

    // Lists
    if (isOfWildCardType(element, ArrayList.class.getName(), "android.os.Parcelable", elements,
        types)) {

      return new CodeGenInfo(typeMap.get(TYPE_KEY_PARCELABLE_ARRAYLIST),
          hasGenericsTypeArgumentOf(element, "android.os.Parcelable", elements, types));
    }

    if (isOfWildCardType(element, LinkedList.class.getName(), "android.os.Parcelable", elements,
        types)) {
      return new CodeGenInfo(typeMap.get(TYPE_KEY_PARCELABLE_LINKEDLIST),
          hasGenericsTypeArgumentOf(element, "android.os.Parcelable", elements, types));
    }

    if (isOfWildCardType(element, CopyOnWriteArrayList.class.getName(), "android.os.Parcelable",
        elements, types)) {
      return new CodeGenInfo(typeMap.get(TYPE_KEY_PARCELABLE_COPYONWRITEARRAYLIST),
          hasGenericsTypeArgumentOf(element, "android.os.Parcelable", elements, types));
    }

    if (isOfWildCardType(element, List.class.getName(), "android.os.Parcelable", elements, types)) {
      return new CodeGenInfo(typeMap.get(TYPE_KEY_PARCELABLE_LIST),
          hasGenericsTypeArgumentOf(element, "android.os.Parcelable", elements, types));
    }

    if (isOfWildCardType(element, List.class.getName(), String.class.getName(), elements, types)) {
      return new CodeGenInfo(typeMap.get(TYPE_KEY_STRING_LIST));
    }

    // Arrays
    if (element.asType().getKind() == TypeKind.ARRAY) {

      ArrayType arrayType = (ArrayType) element.asType();

      TypeMirror arrayOf = arrayType.getComponentType();

      if (arrayOf.getKind() == TypeKind.CHAR) {
        return new CodeGenInfo(typeMap.get(TYPE_KEY_CHAR_ARRAY));
      }

      if (arrayOf.getKind() == TypeKind.BOOLEAN) {
        return new CodeGenInfo(typeMap.get(TYPE_KEY_BOOL_ARRAY));
      }

      if (arrayOf.getKind() == TypeKind.BYTE) {
        return new CodeGenInfo(typeMap.get(TYPE_KEY_BYTE_ARRAY));
      }

      if (arrayOf.getKind() == TypeKind.DOUBLE) {
        return new CodeGenInfo(typeMap.get(TYPE_KEY_DOUBLE_ARRAY));
      }

      if (arrayOf.getKind() == TypeKind.FLOAT) {
        return new CodeGenInfo(typeMap.get(TYPE_KEY_FLOAT_ARRAY));
      }

      if (arrayOf.getKind() == TypeKind.INT) {
        return new CodeGenInfo(typeMap.get(TYPE_KEY_INT_ARRAY));
      }

      if (arrayOf.getKind() == TypeKind.LONG) {
        return new CodeGenInfo(typeMap.get(TYPE_KEY_LONG_ARRAY));
      }

      if (arrayOf.toString().equals(String.class.getName())) {
        return new CodeGenInfo(typeMap.get(TYPE_KEY_STRING_ARRAY));
      }

      if (isOfType(arrayOf, "android.os.Parcelable", elements, types)) {
        // It's an array of parcelable
        return new CodeGenInfo(typeMap.get(TYPE_KEY_PARCELABLE_ARRAY), arrayOf);
      }

      // Unsupported Array Type
      ProcessorMessage.error(element, "Unsuppored type %s as Array type for field %s. "
              + "You could write your own Serialization mechanism by using @%s ",
          element.asType().toString(), element.getSimpleName(), Bagger.class.getSimpleName());
    }

    // TODO SparseArray
    // if (isOfWildCardType(element, "android.util.SparseArray", "android.os.Parcelable", elements,
    //     types)) {
    //  return new CodeGenInfo(typeMap.get(TYPE_KEY_SPARSE_ARRAY),
    //      hasGenericsTypeArgumentOf(element, null, elements, types));
    // }

    // Serializable as last
    if (isOfType(element, "java.io.Serializable", elements, types)) {
      return new CodeGenInfo(typeMap.get(TYPE_KEY_SERIALIZABLE));
    }

    // Unsupported type
    ProcessorMessage.error(element, "Unsuppored type %s for field %s. "
            + "You could write your own Serialization mechanism by using @%s ",
        element.asType().toString(), element.getSimpleName(), Bagger.class.getSimpleName());

    return new CodeGenInfo(null);
  }

  /**
   * Get the wildcardType
   */
  public static TypeMirror getWildcardType(String type, String elementType, Elements elements,
                                           Types types) {
    TypeElement arrayList = elements.getTypeElement(type);
    TypeMirror elType = elements.getTypeElement(elementType).asType();
    return types.getDeclaredType(arrayList, types.getWildcardType(elType, null));
  }

  private static boolean isOfWildCardType(Element element, String type, String wildcardtype,
                                          Elements elements, Types types) {
    return types.isAssignable(element.asType(),
        getWildcardType(type, wildcardtype, elements, types));
  }

  private static boolean isOfType(Element element, String type, Elements elements, Types types) {
    return isOfType(element.asType(), type, elements, types);
  }

  private static boolean isOfType(TypeMirror typeMirror, String type, Elements elements,
                                  Types types) {
    return types.isAssignable(typeMirror, elements.getTypeElement(type).asType());
  }

  /**
   * Checks if the variabel element has generics arguments that matches the expected type
   */
  public static TypeMirror hasGenericsTypeArgumentOf(Element element, String typeToCheck,
                                                     Elements elements, Types types) {

    if (element.asType().getKind() != TypeKind.DECLARED
        || !(element.asType() instanceof DeclaredType)) {
      ProcessorMessage.error(element, "The field %s in %s doesn't have generic type arguments!",
          element.getSimpleName(), element.asType().toString());
    }

    DeclaredType declaredType = (DeclaredType) element.asType();
    List<? extends TypeMirror> typeArguments = declaredType.getTypeArguments();

    if (typeArguments.isEmpty()) {
      ProcessorMessage.error(element, "The field %s in %s doesn't have generic type arguments!",
          element.getSimpleName(), element.asType().toString());
    }

    if (typeArguments.size() > 1) {
      ProcessorMessage.error(element, "The field %s in %s has more than 1 generic type argument!",
          element.getSimpleName(), element.asType().toString());
    }

    // Ok it has a generic argument, check if this extends Parcelable
    TypeMirror argument = typeArguments.get(0);

    if (typeToCheck != null) {
      if (!isOfType(argument, typeToCheck, elements, types)) {
        ProcessorMessage.error(element,
            "The fields %s  generic type argument is not of type  %s! (in %s )",
            element.getSimpleName(), typeToCheck, element.asType().toString());
      }
    }

    // everything is like expected
    return argument;
  }
}