CrystalExpress Integration Guide V5.0

Version History

VersionDateAuthorComment
1.02015/05/14Danielfirst draft
2.02015/05/26Daniel
  1. add Activity onResume and onPause check
  2. modify stream preallocate method
3.02015/06/08Daniel
  1. modify content sample
4.02015/06/15Daniel
  1. demo app add crystal express app source code
  2. add interstitial splash ad integration
  3. stream add pull to refresh sample
  4. open splash use background task sample
  5. remove guard time check
  6. remove section splash ad integration
5.02015/06/19Daniel
  1. SDK add "TEST MODE" api for using test ad list
  2. SDK add AD click and AD impression callback listener
6.02015/06/25Daniel
  1. open splash change api to requesSingleOfferAD

Table of Content

1. Before Integration

1.1 Crystal ID

  • Be sure to obtain the CrystalExpress ID from Intowow.

1.2 import library

  • Adding the jar files to your project(src/libs)
    • intowowsdk.{x.y.z}.jar
    • android-support-v4.jar

2. AndroidManifest.xml

2.1 Permission

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.SET_ORIENTATION" />

2.2 Activity

<activity
    android:name="com.intowow.sdk.SplashAdActivity"
    android:configChanges="orientation|screenSize"
    android:theme="@android:style/Theme.Translucent.NoTitleBar.Fullscreen"
    android:launchMode="singleTask"
    android:screenOrientation="portrait" >
</activity>

2.3 Receiver

<receiver android:name="com.intowow.sdk.ScheduleReceiver">
    <intent-filter >
    <action android:name="com.intowow.sdk.prefetch"/>
    </intent-filter>
</receiver>

2.4 meta-data

<meta-data android:name="CRYSTAL_ID" android:value="Your CRYSTAL_ID" />

3. Activity

  • add I2WAPI.init() only once in your launch activity for init the SDK
    //  init the SDK.
    //
    //  you can call this API only once in your launch flow.
    //
    //  if you need to start the preview mode, 
    //  please passing the activity(not ApplicationContext) as the parameter
    //  and the SDK will parsing the intent on to launch the preview mode.
    //
    I2WAPI.init(this);
  • if you want to use test ad list, you can use "TEST MODE" api :
    I2WAPI.init(this, true);
  • NOTE : if you have set "TEST MODE" to SDK, then SDK will save it permanently. Only remove the App can you use common ad list.

  • in order to let the SDK get your App status, be sure to add "I2WAPI.onActivityResume" and "I2WAPI.onActivityPause" in all of your activity.

  • or you can create one parent activity which implement these code to let other activity extend too.
  • note : if you integrate the open splash ad, then you should add these API in the onStart() and onStop()
@Override
public void onResume() {
    super.onResume();

    //  let the SDK know the App status. (foreground or background)
    //
    //  you should call this API in all of your activity's onResume() status.
    //
    //  if you use splash ad, you can call this API
    //  in the onStart() instead of onResume() too.
    //
    I2WAPI.onActivityResume(this);
}

@Override
public void onPause() {
    super.onPause();

    //  let the SDK know the App status. (foreground or background)
    //
    //  you should call this API in all of your activity's onPause() status.
    //
    //  if you use splash ad, you can call this API
    //  in the onStop() instead of onPause() too.
    //
    I2WAPI.onActivityPause(this);
}

4. Integration Placement

4.1 OPEN SPLASH

  • The "OPEN SPLASH" ad means showing a single-offer or multi-offer ad before going into the main logic.
  • if your App has some initial task in the launch flow, you can refer to the demo code.

4.1.1 Implement Step

  1. Declare variable in the activity
private SplashAD mAd = null;
  1. call API I2WAPI.init
I2WAPI.init(your activity);
  1. request a splash ad
mAd = I2WAPI.requesSingleOfferAD(this, "OPEN_SPLASH");
  1. set callback, implement onLoaded, onLoadFailed and onClosed events.
if (mAd != null) {

    //  implement onLoaded, onLoadFailed and onClosed callback
    //
    mAd.setListener(new SplashAdListener() {

        @Override
        public void onLoaded() {
            //  this callback is called 
            //  when the splash ad is ready to show
            //

            //  show splash ad here
            //
            mAd.show();

            //  in order not to show the open splash ad every time,
            //  you can save the last view time by yourself
            //  
        }

        @Override
        public void onLoadFailed() {
            //  this callback is called
            //  when this splash ad load fail
            //
        }

        @Override
        public void onClosed() {
            //  this callback is called :
            //  1.user click the close button
            //  2.user press the onBackpress button
            //  3.dismiss_time setting from the server
            //
        }
    });
}
  1. add onConfigurationChanged in your activity
    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        //  you have to add this method in the activity,
        //  remember to add the android:configChanges="orientation|screenSize" property
        //  in the Androidanifest.xml
        super.onConfigurationChanged(newConfig);
    }
  1. AndroidManifest.xml

  2. add the android:configChanges="orientation|screenSize" in your activity

4.2 INTERSTITIAL SPLASH

  • The "INTERSTITIAL SPLASH" ad means showing a single-offer ad when user come back to the stream list from the content.
  • you can refer to the crystal express source(CEStreamActivity)
  • step

  • declare the variables in the activity

    private final static String mInterstitialPlacement = "SPLASH";
    private SplashAD mInterstitialSplashAd = null;

be sure to obtain the mInterstitialPlacement's value from Intowow.

  1. request the Splash ad
I2WAPI.requesSplashAD(Activity.this, mInterstitialPlacement);
  1. set listener
        if (mInterstitialSplashAd != null) {
            mInterstitialSplashAd.setListener(new SplashAdListener() {

                @Override
                public void onLoaded() {
                    mInterstitialSplashAd.show();
                }

                @Override
                public void onLoadFailed() {
                }

                @Override
                public void onClosed() {
                    //  be sure to release the splash ad here
                    //
                    mInterstitialSplashAd.release();
                }
            });
        }
  1. remember to release the ad
    @Override
    public void onDestroy() {
        super.onDestroy();

        ...
        ...
        ...

        if (mInterstitialSplashAd != null) {
            mInterstitialSplashAd.release();
            mInterstitialSplashAd = null;
        }
    }

4.3 STREAM

  • the "STREAM" ad means showing the ad in the list view(it is called "IN-STREAM" ad format too).
  • if your list view uses common android listview, you can replace it width the CrystalExpressListView.
  • if your list view uses PullToRefresh library, please to care about the "ListView.onItemClickListener" and "ListView.setOnScrollListener", since the PullToRefresh library will add one head view in the head of the listview, so you should check the right position by the offset value. you can also refer to the demo sample.
  • in order to integrate the stream ad easily, we suggest to let your adapter extend the DeferStreamAdapter. this adapter will let you implement some method which is you need to care about.
  • if your activity use multiple listview, you can refer to the crystal express source(CEStreamActivity) too

4.3.1 Common ListView

you can refer to our demo sample "StreamActivity".

  1. Declare the variables in your activity.
private CrystalExpressListView mYourOriginalListView = null;
private YourAdapter mYourOriginalAdapterExtendsDeferStreamAdapter = null;
private final static String  mPlacement = "STREAM";
be sure to obtain the mPlacement's value from Intowow.
  1. replace your original list view to the CrystalExpressListView.

  2. let your adapter( or parent adapter which you created) to extend the DeferStreamAdapter. and add the mPlacement in the constructor.

  3. in the adapter, you will need to implement "initADListener()", "initStreamADListener()" and "getDefaultMinPosition(int position)".

  4. the initADListener() hope you return a position which tell the SDK which position you want to add when the ad is loaded. before you return the position, you should first allocate one position in the data set. if you do not need any ad, then you can return "-1" directly.

    /**
     * you should implement this method to tell the SDK which position you want to add
     * */
    @Override
    public void initADListener() {
        setAdListener(new com.intowow.sdk.StreamHelper.ADListener() {

            @Override
            public int onADLoaded(int position) {
                //  when the SDK load one stream ad,
                //  it will call this callback for getting 
                //  the position you add in the DataSet.
                //
                //  if you call getStreamAd in the getView(),
                //  the SDK will return the ad refer to this position.
                //
                //  if you return "-1", it means that the ad is not added in your DataSet
                //

                position = getDefaultMinPosition(position);

                if (mList.size() >  position) {             
                    // just allocate one position for stream ad
                    //
                    mList.add(position, null);
                    notifyDataSetChanged();
                    return position;
                }
                else {              
                    return -1;
                }
            }

        });
    }
  1. the initStreamADListener() hope you tell the SDK that your dataset has been changed, if it has been changed, then SDK will need reallocate the ad position in your dataset too.
    /**
     * you should implement this method to tell the SDK that your DataSet has been changed
     * */
    @Override
    public void initStreamADListener() {
        setStreamADListener(new StreamADListener() {

            @Override
            public void onDataSetChanged() {
                for (Integer pos : getAddedStreamAdPosition()) {
                    if(pos > mList.size()) {
                        return;
                    }

                    //  check ad case
                    //
                    if(mList.get(pos) == null || mList.get(pos).equals("null")) {
                        continue;
                    }

                    mList.add(pos , null);
                }
            }});
    }
  1. the getDefaultMinPosition(int position) let you determind what minimum position you want to add
    @Override
    public int getDefaultMinPosition(int position) {
        // Don't place ad at the first place
        return Math.max(1, position);
    }
  1. in the getView(), you should check if the position is ad first, and you can set the background or width directly.
        // Get ad view if possible
        final View adView =  getStreamAd(position); 

        //  or you can resize the ad width by this way
        //  final View adView =  getStreamAd(position, 700);
        //

        if(adView != null) {
            //  you can set the background
            //  such as
            //  adView.setBackgroundColor(Color.BLACK);
            //  adView.setBackgroundResource(your resid);
            //  adView.setBackground(your background drawable);
            //  adView.setBackgroundDrawable(your background drawable);
            return adView;
        }
  1. if you have implemented the "getItemViewType(int position)" before, please modify it.
    /** 
     *  if you have implement getItemViewType, 
     *  be sure to check if the item is an ad 
     *  in this position.
     * */
    @Override
    public int getItemViewType(int position) {
        if(isAd(position)) {
            return super.getItemViewType(position);
        }else{
            //  return your view type here
            //
            //
        }
    }
  1. The Activity Lifecycle
    @Override
    public void onResume() {
        super.onResume();

        ...
        ...
        ...

        if(mAdapter != null) {
            mAdapter.onResume();
        }
    }
    @Override
    public void onPause() {
        super.onPause();

        ...
        ...
        ...

        if(mAdapter != null) {
            mAdapter.onPause();
        }
    }
    @Override
    public void onDestroy() {
        super.onDestroy();

        ...
        ...
        ...

        if(mAdapter != null) {
            mAdapter.release();
            mAdapter = null;
        }
    }

4.3.2 PullToRefresh ListView

the integrate step is similar to 4.3.1.
you can refer to the demo sample "CEStreamActivity" or "PullToRefreshStreamActivity".
in order to implement the pull effect, the library add one head view in front of your data set.
therefore, you will need to care about "onItemClickListener" and "onScrollListener" method additionally.

  1. declare the offset varable
private final static int FIRST_VISIBLE_ITEM_OFFSET = -1;
  1. let your adapter extend the DeferStreamAdapter and implement relative methods too, you can refer to the 4.3.1

  2. check onItemClickListener

    mPullToRefreshListView.setOnItemClickListener(new OnItemClickListener() {

        @Override
        public void onItemClick(AdapterView<?> parent, View view,
                int position, long id) {

            position = position + FIRST_VISIBLE_ITEM_OFFSET;

            if(mAdapter != null && mAdapter.isAd(position)) {
                return;
            }

            //  ...
            //  if you have already implemented this listener,
            //  add your original code here 
            //  ...

        }});
  1. check onScrollListener
    mPullToRefreshListView.setOnScrollListener(new OnScrollListener() {

        @Override
        public void onScrollStateChanged(AbsListView view, int scrollState) {
            //  ...
            //  if you have already implemented this listener,
            //  add your original code here 
            //  ...

            if(mAdapter != null) {
                mAdapter.onScrollStateChanged(view, scrollState);
            }
        }

        @Override
        public void onScroll(AbsListView view, int firstVisibleItem,
                int visibleItemCount, int totalItemCount) {
            //  ...
            //  if you have already implemented this listener,
            //  add your original code here 
            //  ...

            if(mAdapter != null) {
                //  pass the right position on to the SDK
                //
                mAdapter.onScroll(view, 
                        firstVisibleItem + FIRST_VISIBLE_ITEM_OFFSET, 
                        visibleItemCount + FIRST_VISIBLE_ITEM_OFFSET, 
                        totalItemCount);
            }
        }});
  1. you can refer to the 4.3.1 to check activity lifecycle

4.4 CONTENT

  • In most cases, the "CONTENT" ad means showing the ad in the end of the article(it is called IN-READ ad too).
  • In the demo app, we use the ScrollView to implement this format. if your App is not support it, please contact with Intowow.
  • You can refer to the activity_content.xml to set up the UI.

4.4.1 Implement Step

  1. Add CrystalExpressScrollView.java and ContentHelper.java in your project.

  2. Declare the variables in the activity.

    private final static String mPlacement = "CONTENT";
    private ContentHelper mContentHelper = null;
be sure to obtain the mPlacement's value from Intowow.
  1. Init mContentHelper
    final CrystalExpressScrollView sv = (CrystalExpressScrollView)findViewById(R.id.scrollview);
    final RelativeLayout contentAdLayout = (RelativeLayout)findViewById(R.id.contentad);

    mContentHelper = new ContentHelper(this, mPlacement, sv, contentAdLayout);
    //  let the SDK know that this placement is active now
    //
    mContentHelper.setActive();
    mContentHelper.onPageSelected(0);
  1. Set the ScrollViewListener into your scroll view.
    //  callback
    //
    sv.setScrollViewListener(new ScrollViewListener(){
        public void onScrollChanged(CrystalExpressScrollView scrollView,int x, int y, int oldX, int oldY){
        }
      public void onScrollViewIdle(){
        if(mContentHelper != null) {
                mContentHelper.checkContentAD();
        }
      }
    });
  1. load content ad. if you want to load the content ad at the beginning, insert this code into your initial flow. or you can add it when you want to show this ad after your data load finish. for example: webview's onPageFinished
    if(mContentHelper != null) {
        mContentHelper.loadContentAd();
    }
  1. if you want to set background or resize the width, then modify the ContentHelper.java
    public View requestAD(int position) {
        if (mHelper != null) {

            //  you can resize the ad width by
            //
            //  mHelper.requestAD(position, intWidthValue);

            return mHelper.requestAD(position);
        }

        return null;
    }
    private void addAd(View contentAd) {
        if(contentAd!=null){

            //  you can set your background here
            //
            contentAd.setBackgroundColor(Color.WHITE);

            mContentAdLayout.getLayoutParams().height = RelativeLayout.LayoutParams.WRAP_CONTENT;
            mContentAdLayout.removeAllViews();
            mContentAdLayout.addView(contentAd);
        }
    }
  1. The Activity Lifecycle
    @Override
    public void onResume() {
        super.onResume();

        ...
        ...
        ...

        if(mContentHelper != null){
          mContentHelper.start();
        }
    }
    @Override
    public void onPause() {
        super.onPause();

        ...
        ...
        ...

        if(mContentHelper != null){
          mContentHelper.stop();
        }
    }
    @Override
    public void onDestroy() {
        super.onDestroy();

        ...
        ...
        ...

        if(mContentHelper != null) {
          mContentHelper.destroy();
            mContentHelper = null;
        }
    }

4.5 FLIP

  • In most case, the "FLIP" ad means showing the ad when user flip some page in the same activity.
  • In the demo app, we use the ViewPager to implement this format.

4.5.1 Implement Step

  1. Add FlipHelper.java in your project.

  2. Delcare variables in the activity.

    private final static String mPlacement = "FLIP";
    private FlipHelper mFlipHelper = null;
be sure to obtain the mPlacement's value form Intowow.
  1. Init mFlipHelper
    mFlipHelper = new FlipHelper(this, mPlacement);
    //  let the SDK know that this placement is active now
    //
    mFlipHelper.setActive();
  1. Set mFlipHelper into your pager adapter(we recommand to use your pager adapter's contructor)
    YourPagerAdapter adapter = new YourPagerAdapter(XXX, ..., mFlipHelper);
  1. OnPageChangeListener
    @Override
    public void onPageScrollStateChanged(int state) {
        ...
        ...
        ...

        if (mFlipHelper != null) {
            mFlipHelper.onPageScrollStateChanged(state);
        }
    }

    @Override
    public void onPageSelected(int position) {

        ...
        ...
        ...

        if (mFlipHelper != null) {
            mFlipHelper.onPageSelected(position);
        }
    }
  1. in your pager adapter, Declare variable
    private FlipHelper mFlipHelper;
  1. Edit contructor
    public YourPagerAdapter(XXX, ..., FlipHelper helper)
    {
        ...
        ...
        ...

        mFlipHelper = helper;

        mFlipHelper.setAppADListener(new AppADListener() {

            @Override
            public int onADLoaded(int position) {
                // Don't place ad at the first place!!
                position = Math.max(1, position);

                if (yourDataList.size() >=  position) {
                    YourClass data = new YourClass();   // your data class
                    yourDataList.add(position, data);
                    notifyDataSetChanged();
                    return position;
                }
                else {
                    return -1;
                }
            }

        });
    }
  1. Edit instantiateItem()
    public Object instantiateItem(View collection, final int position) {
        LinearLayout flipAd = null;
        if (mFlipHelper != null) {
            flipAd = mFlipHelper.getFlipAdView(position);
        }

        if(flipAd != null) {
            ((ViewPager) collection).addView(flipAd);
            return flipAd;
        }

        ...
        ...
        ...
    }
  1. The Activity Lifecycle
    @Override
    public void onResume() {
        super.onResume();

        ...
        ...
        ...

        if(mFlipHelper != null) {
          mFlipHelper.start();
        }
    }
    @Override
    public void onPause() {
        super.onPause();

        ...
        ...
        ... 

        if(mFlipHelper != null) {
          mFlipHelper.stop();
        }
    }
    @Override
    public void onDestroy() {
        super.onDestroy();

        ...
        ...
        ...
        if(mFlipHelper != null) {
          mFlipHelper.destroy();
            mFlipHelper = null;
        }
    }

5. Proguard

if your App has using proguard, please add these code in your proguard setting file.

##################################
#   intowow sdk
#
-keep class com.intowow.sdk.* { *; }
##################################
#   android-support-v4
#
-keep interface android.support.v4.app.** { *; }
-keep class android.support.v4.** { *; }
##################################

6. Preview Mode

  • The CrystalExpress SDK supports you to load the ad by preview mode. you can check the ad directly by using QRCode.

6.1 Preview Setting

  1. Edit the AndroidManifest.xml's launch activity(FirstActivity.java)
<intent-filter>
    <actionandroid:name="android.intent.action.VIEW"/>
    <categoryandroid:name="android.intent.category.DEFAULT"/>
    <categoryandroid:name="android.intent.category.BROWSABLE"/>
    <dataandroid:scheme="crystalexpress"android:host="adpreview"/>
    <dataandroid:scheme="crystalexpress"android:host="activate"android:pathPattern=".*"/>
</intent-filter>
you should provide Intowow with your App's scheme.
  1. Create the QRCode, the format is
{your app's scheme}://adpreview?adid={Your Ad ID}

7. AD click and impression callback

  • the SDK supports the App to set one Ad Event Listener to get ad's 'click' and 'impression' event.

  • if you set Ad Event Listener, the SDK will callback these event in the background thread. so you should take care the 'main-thread' only issue after you handle the callback.

  • in order not to block the App's resource (such as activity is destroy), the SDK will use WeakReference to save this listener. so you should maintain this listener's lifecycle by yourself and set it in the SDK frequently.

  • the api is

I2WAPI.setADEventListener(final Context context, ADEventListener listener);
  • sample code

// you should maintain this listener's lifecycle by yourself
//
private ADEventListener mADEventListener = null;

if(mADEventListener == null) {
    mADEventListener = new ADEventListener() {
        @Override
        public void onAdClick(final String adId) {
            //  get click event here
            //
        }

        @Override
        public void onAdImpression(final String adId) {
            //  get impression event here
            //
        }
    };

    I2WAPI.setADEventListener(Activity.this, ADEventListener listener);
}

8. API Reference

/**
*   init the SDK.
*
*   you can call this API only once in your launch flow.
*
*   if you need to start the preview mode, 
*   please passing the activity(not ApplicationContext) as the parameter
*   and the SDK will parsing the intent to launch preview mode.
*   
**/
static public synchronized void init(final Context context)
/**
*
* use the test ad list
*
**/
static public synchronized void init(final Context context, final boolean isTestMode)
/**
*
* to get the ad click and impression event
*
**/
static public void setADEventListener(final Context context, ADEventListener listener)
/**checking if the ad is serving now*/
static public synchronized boolean isAdServing(final Context context)
/**request a single-offer or multi-offer splash ad*/
static public SplashAD requestHybridSplashAD(final Context context, final boolean isTest)
/**request a single-offer splash ad*/
static public SplashAD requesSplashAD(final Context context, final String placement)
/**
*   to notify the SDK that the APP has onResume.
*   you should call this API in all of your activity's onResume() status.
*   
*   if one of your activity use splash ad, then you can call this API
*   in the onStart() instead of onResume() too.
*   
**/
static public void onActivityResume(final Context context)
/**
*   to notify the SDK that the APP has onPause.
*   you should call this API in all of your activity's onPause() status.
*   
*   if one of your activity use splash ad, then you can call this API
*   in the onStop() instead of onPause() too.
*   
**/
static public void onActivityPause(final Context context)
/**
*   to trigger the background fetch flow.
*   
*   you can call this API in the GCM onMessage or any background flow.
*   
**/
static public void triggerBackgroundFetch(final Context context)
/**to track the ad event*/
static public void trackEvent(final Context context, final String type, final JSONObject props)