Marmalade SDK. Інтеграція з сервісами реклами AdMob, Chartboost, Inmobi і Leadbolt на платформах iOS і Android

Підключення розширень в проект
В рамках даної статті я розповім як підключити проміжні (Interstitial) банери декількох рекламних сервісів для мобільних додатків, розроблених в середовищі Marmalade SDK.
Отже, вважаємо що Ви вже прочитали документацію Marmalade по підключенню перерахованих сервісів, а також маєте учеткі на наступних сайтах і зареєстрували там свої додатки:
При реєстрації додатки система присвоює йому якийсь ідентифікатор (або декілька ідентифікаторів — як це зроблено в Chartboost), які будуть ідентифікувати ваші програми при взаємодії з сервісами реклами.

На сьогодні я використовую Marmalade SDK версії 7.8.0p3 [439542]. Збірка вже містить у собі наступні цікаві розширення:
  • %SDK install folder%/extensions/s3eGoogleAdMob
  • %SDK install folder%/extensions/s3eInMobiAds
  • %SDK install folder%/extensions/s3eChartboost
Розширення для Leadbolt в стандартний дистрибутив не входять. Документація Marmalade рекомендує їх з сайту help.leadbolt.com. На відміну від AdMob, Inmobi і Chartboost, Leadbolt надає окремі розширення під Android і iOS. Розширення ці написані не найзручнішим чином, а саме — деякі функції в них називаються однаково, що призводить до колізій при спробі підключити відразу два розширення в один проект. Тому я трохи переробив розширення для iOS під себе, додавши суфікс iOS до всіх методів, де це необхідно, щоб уникнути колізій. Розширення, які я використовую, доступні на github github.com/akk0rd87/Marmalade-Leabolt-SDK. Завантажуємо їх і копіюємо в /extensions/, щоб вийшло

%SDK install folder%/extensions/AppTrackerAndroid
%SDK install folder%/extensions/AppTrackerIOS

Включаємо в секцію subprojects mkb-файлу наші розширення:
Subprojects
{
...
s3eInMobiAds
s3eGoogleAdMob
s3eChartBoost
AppTrackerAndroid
AppTrackeriOS
}

У секцію deployment додаємо посилання AppTracker.jar (за замовчуванням він знаходиться в AppTrackerAndroid, я виніс його в папку common/jar) і Chartboost-ідентифікатори для Anrdoid-додатки:
deployment
{
... 
# THIS NEED FOR LEADBOLT ON ANDROID
android-external-jars='../common/jar/AppTracker.jar' 

android-extra-strings='(gps_app_id,0), (chartboost_appid, *********),(chartboost_appsig, ***************)'
}


Взаємодія програми з сервісом реклами
Загальний принцип взаємодії програми з рекламним сервісом:
  • у момент ініціалізації додатка ініціалізується сесія взаємодії з рекламним сервісом;
  • у визначений розробником момент здійснюється запит на кешування банера;
  • через деякий час після запиту на кешування банера викликається відповідна callback функція-обробник, параметри якого вказують на те, чи виконаний запит кешування або впав з помилкою;
  • у визначений розробником момент здійснюється перевірка результату останнього запиту на кешування, якщо такий був виконаний успішно, то викликається метод показу реклами;
  • в залежності від дій користувача (наприклад клік, повний перегляд або закриття відео-оголошення) також можуть викликати відповідні callback функції-обробники;
  • при закритті програми закриваємо сесію.
З вищеописаного принципу випливають такі стани сесії, описані в adengine_constants.h
#define AD_ENGINE_TEMPORARY 0 // тимчасовий стан сесії. очікування callback-a;
#define AD_ENGINE_NOT_INITED 1 // сесія не ініціалізована
#define AD_ENGINE_INITED 2 // сесія ініційована;
#define AD_ENGINE_LOAD_OK 3 // завантаження банера пройшла успішно;
#define AD_ENGINE_LOAD_ERR 4 // завантаження банера впала з помилкою;
#define AD_ENGINE_SHOW_OK 5 // запит на показ банера виконався успішно;
#define AD_ENGINE_TERMINATED 6 // сесія закрита;


CPP-файл для кожного розширення
Підключаємо файли, в яких описана логіка взаємодії з конкретним рекламним сервісом. Для Leadbolt підключаємо два файлу: окремо по iOS і Android, так як для цих ОС у нас окремі розширення. Також підключаємо файл adengine.cpp, який буде керувати цією логікою. Також не забуваємо створити відповідні .h-header-и, в яких будуть описані відповідні API-фукнції. Фактично adengine.cpp — це мій движок для роботи з рекламою, і я використовую його в декількох додатках. Щоб не хардкодить в ньому ідентифікатори додатків, для цієї мети в кожен проект я додатково включаю файл local.cpp, має свою реалізацію для кожного окремого додатка.
Секція files mkb-файлу:
{
...
adengine.cpp 
googleadmob.cpp
inmobi.cpp
leadbolt_ios.cpp
leadbolt_android.cpp 
charboost.cpp
local.cpp
}
googleadmob.cpp
#include "s3eGoogleAdMob.h"
#include "adengine_constants.h"

int googlead_mob_status = AD_ENGINE_NOT_INITED;

s3eGoogleAdMobId m_Id = 0;

void DestroyAdMobAd()
{
s3eResult res = s3eGoogleAdMobDestroyAd(m_Id);
}

static int32 onAdMobLoad(void* systemData, void* userData)
{
googlead_mob_status = AD_ENGINE_LOAD_OK;

return 0;
}

static int32 onAdMobAction(void* systemData, void* userData)
{
DestroyAdMobAd();

googlead_mob_status = AD_ENGINE_INITED;

return 0;
}

static int32 onAdMobError(void* systemData, void* userData)
{
DestroyAdMobAd();

googlead_mob_status = AD_ENGINE_LOAD_ERR; 

return 0;
}

static int32 onAdMobFiledToLoad(void* systemData, void* userData)
{
DestroyAdMobAd();

googlead_mob_status = AD_ENGINE_LOAD_ERR;

return 0;
}


////////////////////////////////////////////
//////// API
////////////////////////////////////////////
void AdMob_Init(char AppCode[])
{
s3eGoogleAdMobRegister(S3E_GOOGLEADMOB_CALLBACK_AD_LOADED, onAdMobLoad , NULL);
s3eGoogleAdMobRegister(S3E_GOOGLEADMOB_CALLBACK_AD_ACTION, onAdMobAction, NULL);
s3eGoogleAdMobRegister(S3E_GOOGLEADMOB_CALLBACK_AD_ERROR , onAdMobError , NULL);

s3eResult res0 = s3eGoogleAdMobSetAdUnitId(AppCode, S3E_TRUE);

googlead_mob_status = AD_ENGINE_INITED;
}

void AdMob_Load()
{ 
googlead_mob_status = AD_ENGINE_TEMPORARY;

s3eResult res1 = s3eGoogleAdMobPrepareAd(&m_Id);

s3eGoogleAdMobAdInfo info;
s3eGoogleAdMobInspectAd(m_Id, &info);

s3eResult res2 = s3eGoogleAdMobLoadInterstitialAd(m_Id);
}

void AdMob_Show()
{
googlead_mob_status = AD_ENGINE_TEMPORARY;

s3eResult res = s3eGoogleAdMobShowAd(m_Id);
}

void AdMob_Terminate()
{ 
DestroyAdMobAd();

googlead_mob_status = AD_ENGINE_TERMINATED; 

s3eGoogleAdMobUnRegister(S3E_GOOGLEADMOB_CALLBACK_AD_LOADED, onAdMobLoad );
s3eGoogleAdMobUnRegister(S3E_GOOGLEADMOB_CALLBACK_AD_ACTION, onAdMobAction);
s3eGoogleAdMobUnRegister(S3E_GOOGLEADMOB_CALLBACK_AD_ERROR , onAdMobError ); 
}

int AdMob_Status()
{
return googlead_mob_status;
}

s3eBool AdMob_Avaliable()
{
return s3eGoogleAdMobAvailable();
}


inmobi.cpp
#include "s3eInMobiAds.h"
#include "s3e.h"
#include "adengine_constants.h"

int InMobi_ad_state = AD_ENGINE_NOT_INITED; 


static int int_request_completed(void *systemData, void *userData)
{ 
InMobi_ad_state = AD_ENGINE_LOAD_OK;
return S3E_RESULT_SUCCESS;
}

static int int_request_failed(void *systemData, void *userData)
{
InMobi_ad_state = AD_ENGINE_LOAD_ERR;
return S3E_RESULT_SUCCESS;
}

static int int_show_adscreen(void *systemData, void *userData) 
{ 
return S3E_RESULT_SUCCESS;
}
static int int_dismiss_adscreen(void *systemData, void *userData)
{
InMobi_ad_state = AD_ENGINE_INITED;
return S3E_RESULT_SUCCESS;
}
static int int_leave_application(void *systemData, void *userData) 
{ 
InMobi_ad_state = AD_ENGINE_INITED;
return S3E_RESULT_SUCCESS;
}
static int int_ad_interacted(void *systemData, void *userData)
{ 
InMobi_ad_state = AD_ENGINE_INITED;
return S3E_RESULT_SUCCESS;
}

static int DeviceStateChangeCallback(void *systemData, void *userData)
{
InMobi_ad_state = AD_ENGINE_INITED;
return S3E_RESULT_SUCCESS;
}

void InMobi_Init(char appcode[50])
{

InMobiAdsRegisterIntCallback(INMOBIADS_CALLBACK_INT_REQUEST_COMPLETED, int_request_completed, NULL);
InMobiAdsRegisterIntCallback(INMOBIADS_CALLBACK_INT_REQUEST_FAILED, int_request_failed, NULL);
InMobiAdsRegisterIntCallback(INMOBIADS_CALLBACK_INT_SHOW_ADSCREEN, int_show_adscreen, NULL);
InMobiAdsRegisterIntCallback(INMOBIADS_CALLBACK_INT_DISMISS_ADSCREEN, int_dismiss_adscreen, NULL);
InMobiAdsRegisterIntCallback(INMOBIADS_CALLBACK_INT_LEAVE_APPLICATION, int_leave_application, NULL);
InMobiAdsRegisterIntCallback(INMOBIADS_CALLBACK_INT_INTERACTED, int_ad_interacted, NULL);

s3eDeviceRegister(S3E_DEVICE_UNPAUSE, DeviceStateChangeCallback, NULL);

inmobi_initialize(appcode);
inmobi_interstitial_init(appcode);

InMobi_ad_state = AD_ENGINE_INITED;

}

void InMobi_Load()
{
InMobi_ad_state = AD_ENGINE_TEMPORARY;
inmobi_interstitial_load(""); 
}

void InMobi_Show()
{
InMobi_ad_state = AD_ENGINE_TEMPORARY;
inmobi_interstitial_show();
}

void InMobi_Release()
{
InMobi_ad_state = AD_ENGINE_TERMINATED;
inmobi_interstitial_release();

InMobiAdsUnRegisterIntCallback(INMOBIADS_CALLBACK_INT_REQUEST_COMPLETED, int_request_completed);
InMobiAdsUnRegisterIntCallback(INMOBIADS_CALLBACK_INT_REQUEST_FAILED, int_request_failed); 
InMobiAdsUnRegisterIntCallback(INMOBIADS_CALLBACK_INT_SHOW_ADSCREEN, int_show_adscreen);
InMobiAdsUnRegisterIntCallback(INMOBIADS_CALLBACK_INT_DISMISS_ADSCREEN, int_dismiss_adscreen);
InMobiAdsUnRegisterIntCallback(INMOBIADS_CALLBACK_INT_LEAVE_APPLICATION, int_leave_application);
InMobiAdsUnRegisterIntCallback(INMOBIADS_CALLBACK_INT_INTERACTED, int_ad_interacted);

s3eDeviceUnRegister(S3E_DEVICE_UNPAUSE, DeviceStateChangeCallback); 
}

int InMobi_Status()
{
return InMobi_ad_state;
}

s3eBool InMobi_Avaliable()
{
return s3eInMobiAdsAvailable();
}


leadbolt_ios.cpp
#include "AppTrackeriOS.h"
#include "adengine_constants.h"

int ldb_ios_ad_state = AD_ENGINE_NOT_INITED;

void LBD_IOS_DoDestroy()
{
AppTrackeriOS_destroyModule();
}

int32 LBD_IOS_onModuleFailedEvent(void* system, void* user){
ldb_ios_ad_state = AD_ENGINE_LOAD_ERR;
return 0;
}

int32 LBD_IOS_onModuleClosedEvent(void* system, void* user){
LBD_IOS_DoDestroy();
ldb_ios_ad_state = AD_ENGINE_INITED;
return 0;
}

int32 LBD_IOS_onModuleClickedEvent(void* system, void* user){
return 0;
}

int32 LBD_IOS_onModuleLoadedEvent(void* system, void* user){
ldb_ios_ad_state = AD_ENGINE_SHOW_OK;
return 0;
}
int32 LBD_IOS_onModuleCacheEvent(void* system, void* user){
ldb_ios_ad_state = AD_ENGINE_LOAD_OK;
return 0;
}
int32 LBD_IOS_onMediaFinishedEvent(void* system, void* user){
LBD_IOS_DoDestroy();
ldb_ios_ad_state = AD_ENGINE_INITED;
return 0;
}

/////////////////////////////////////////////
///////// API
/////////////////////////////////////////////

int LDB_IOS_Status()
{
return ldb_ios_ad_state;
}

void LDB_IOS_Init(char AppCode[])
{

AppTrackeriOSRegister(APPTRACKERIOS_MODULEFAILED, &LBD_IOS_onModuleFailedEvent, NULL);
AppTrackeriOSRegister(APPTRACKERIOS_MODULELOADED, &LBD_IOS_onModuleLoadedEvent, NULL);
AppTrackeriOSRegister(APPTRACKERIOS_MODULECLOSED, &LBD_IOS_onModuleClosedEvent, NULL);
AppTrackeriOSRegister(APPTRACKERIOS_MODULECLICKED, &LBD_IOS_onModuleClickedEvent, NULL);
AppTrackeriOSRegister(APPTRACKERIOS_MODULECACHED, &LBD_IOS_onModuleCacheEvent, NULL);
AppTrackeriOSRegister(APPTRACKERIOS_MEDIAFINISHED, &LBD_IOS_onMediaFinishedEvent, NULL);

AppTrackeriOS_startSession(AppCode);

ldb_ios_ad_state = AD_ENGINE_INITED;
}

void LDB_IOS_Load()
{
ldb_ios_ad_state = AD_ENGINE_TEMPORARY;
AppTrackeriOS_loadModuleToCache("inapp");
}

void LDB_IOS_Show()
{
ldb_ios_ad_state = AD_ENGINE_TEMPORARY;
AppTrackeriOS_loadModule("inapp");
}

void LDB_IOS_Terminate()
{
ldb_ios_ad_state = AD_ENGINE_TERMINATED;

AppTrackeriOS_closeSession();

AppTrackeriOSUnRegister(APPTRACKERIOS_MODULEFAILED, &LBD_IOS_onModuleFailedEvent);
AppTrackeriOSUnRegister(APPTRACKERIOS_MODULELOADED, &LBD_IOS_onModuleLoadedEvent);
AppTrackeriOSUnRegister(APPTRACKERIOS_MODULECLOSED, &LBD_IOS_onModuleClosedEvent);
AppTrackeriOSUnRegister(APPTRACKERIOS_MODULECLICKED, &LBD_IOS_onModuleClickedEvent);
AppTrackeriOSUnRegister(APPTRACKERIOS_MODULECACHED, &LBD_IOS_onModuleCacheEvent);
AppTrackeriOSUnRegister(APPTRACKERIOS_MEDIAFINISHED, &LBD_IOS_onMediaFinishedEvent);
}

s3eBool LDB_IOS_Avaliable()
{
return AppTrackeriOSAvailable();
}


leadbolt_android.cpp
#include "AppTrackerAndroid.h"
#include "adengine_constants.h"

int ldb_ad_state = AD_ENGINE_NOT_INITED;


void DoDestroy()
{
destroyModule();
}

int32 onModuleFailedEvent(void* system, void* user){
ldb_ad_state = AD_ENGINE_LOAD_ERR;
return 0;
}

int32 onModuleClosedEvent(void* system, void* user){
DoDestroy();
ldb_ad_state = AD_ENGINE_INITED;
return 0;
}

int32 onModuleClickedEvent(void* system, void* user){
return 0;
}

int32 onModuleLoadedEvent(void* system, void* user){
ldb_ad_state = AD_ENGINE_SHOW_OK;
return 0;
}
int32 onModuleCacheEvent(void* system, void* user){
ldb_ad_state = AD_ENGINE_LOAD_OK;
return 0;
}
int32 onMediaFinishedEvent(void* system, void* user){
DoDestroy();
ldb_ad_state = AD_ENGINE_INITED;
return 0;
}

/////////////////////////////////////////////
///////// API
/////////////////////////////////////////////

int LDB_Android_Status()
{
return ldb_ad_state;
}

void LDB_Android_Init(char AppCode[])
{

AppTrackerAndroidRegister(APPTRACKERANDROID_MODULEFAILED , &onModuleFailedEvent , NULL);
AppTrackerAndroidRegister(APPTRACKERANDROID_MODULELOADED , &onModuleLoadedEvent , NULL);
AppTrackerAndroidRegister(APPTRACKERANDROID_MODULECLOSED , &onModuleClosedEvent , NULL);
AppTrackerAndroidRegister(APPTRACKERANDROID_MODULECLICKED, &onModuleClickedEvent, NULL);
AppTrackerAndroidRegister(APPTRACKERANDROID_MODULECACHED , &onModuleCacheEvent , NULL);
AppTrackerAndroidRegister(APPTRACKERANDROID_MEDIAFINISHED, &onMediaFinishedEvent, NULL); 

startSession(AppCode);

ldb_ad_state = AD_ENGINE_INITED;
}

void LDB_Android_Load()
{
ldb_ad_state = AD_ENGINE_TEMPORARY;
loadModuleToCache("inapp", "");
}

void LDB_Android_Show()
{
ldb_ad_state = AD_ENGINE_TEMPORARY;
loadModule("inapp", "");
}

void LDB_Android_Terminate()
{
ldb_ad_state = AD_ENGINE_TERMINATED;

closeSession();

AppTrackerAndroidUnRegister(APPTRACKERANDROID_MODULEFAILED , &onModuleFailedEvent);
AppTrackerAndroidUnRegister(APPTRACKERANDROID_MODULELOADED , &onModuleLoadedEvent);
AppTrackerAndroidUnRegister(APPTRACKERANDROID_MODULECLOSED , &onModuleClosedEvent);
AppTrackerAndroidUnRegister(APPTRACKERANDROID_MODULECLICKED, &onModuleClickedEvent);
AppTrackerAndroidUnRegister(APPTRACKERANDROID_MODULECACHED , &onModuleCacheEvent);
AppTrackerAndroidUnRegister(APPTRACKERANDROID_MEDIAFINISHED, &onMediaFinishedEvent); 
}

s3eBool LDB_Android_Avaliable()
{
return AppTrackerAndroidAvailable();
}


charboost.cpp
#include "s3e.h"
#include "s3eChartBoost.h"
#include "adengine_constants.h"

int charboost_ad_state = AD_ENGINE_NOT_INITED;

void RequestCB(void* systemData, void* userData)
{
charboost_ad_state = AD_ENGINE_LOAD_OK;
}
void AdvertisementClosed(void* System, void* User)
{
charboost_ad_state = AD_ENGINE_INITED; 
}
void AdvertisementDismissed(void* System, void* User)
{
charboost_ad_state = AD_ENGINE_INITED;
}

void AdvertisementClicked(void* System, void* User)
{
charboost_ad_state = AD_ENGINE_INITED;
}

void ErrorCallback(void* System, void* User)
{
charboost_ad_state = AD_ENGINE_LOAD_ERR;
}


//////////////////////////////
//////////////// API
//////////////////////////////

s3eBool CharBoost_Avaliable()
{ 
return s3eChartBoostAvailable();
}

void CharBoost_Init(char AppCode[], char Signature[])
{ 
s3eChartBoostRegister(S3E_CHARTBOOST_CALLBACK_REQUEST_RESPONSE, (s3eCallback) RequestCB , NULL);
s3eChartBoostRegister(S3E_CHARTBOOST_CALLBACK_AD_CLOSED , (s3eCallback) AdvertisementClosed , NULL);
s3eChartBoostRegister(S3E_CHARTBOOST_CALLBACK_AD_DISMISSED , (s3eCallback) AdvertisementDismissed, NULL);
s3eChartBoostRegister(S3E_CHARTBOOST_CALLBACK_AD_CLICKED , (s3eCallback) AdvertisementClicked , NULL);
s3eChartBoostRegister(S3E_CHARTBOOST_CALLBACK_ERROR , (s3eCallback) ErrorCallback , NULL);

s3eChartBoostSetAppID(AppCode);
s3eChartBoostSetAppSignature(Signature);

s3eChartBoostStartSession(); 

charboost_ad_state = AD_ENGINE_INITED;
}

void CharBoost_Load()
{
charboost_ad_state = AD_ENGINE_LOAD_OK;
}

void CharBoost_Show()
{
charboost_ad_state = AD_ENGINE_TEMPORARY;

s3eChartBoostShowInterstitial(S3E_CHARTBOOST_LOCATION(HOME_SCREEN));
}

void CharBoost_Terminate()
{
charboost_ad_state = AD_ENGINE_TERMINATED;

s3eChartBoostUnRegister( S3E_CHARTBOOST_CALLBACK_REQUEST_RESPONSE, (s3eCallback)RequestCB);
s3eChartBoostUnRegister( S3E_CHARTBOOST_CALLBACK_AD_CLOSED, (s3eCallback)AdvertisementClosed);
s3eChartBoostUnRegister( S3E_CHARTBOOST_CALLBACK_AD_DISMISSED, (s3eCallback)AdvertisementDismissed);
s3eChartBoostUnRegister( S3E_CHARTBOOST_CALLBACK_AD_CLICKED,(s3eCallback) AdvertisementClicked);
s3eChartBoostUnRegister(S3E_CHARTBOOST_CALLBACK_ERROR, (s3eCallback)ErrorCallback);
}

int CharBoost_Status()
{
return charboost_ad_state;
}


local.cpp — ідентифікатори додатків маскированы
#include < string.h>
#include "s3e.h"

void GetInMobiAppAdIdentifier(char code[])
{
int os = s3eDeviceGetInt(S3E_DEVICE_OS);

switch (os)
{
case S3E_OS_ID_ANDROID:
strcpy(code, "********************************");
break;
case S3E_OS_ID_IPHONE:
strcpy(code, "********************************");
break;
// case S3E_OS_ID_WINDOWS:
// break;
} 
}

void GetLDBAppAdIdentifier(char code[])
{
int os = s3eDeviceGetInt(S3E_DEVICE_OS);

switch (os)
{
case S3E_OS_ID_ANDROID:
strcpy(code, "********************************");
break;
case S3E_OS_ID_IPHONE:
strcpy(code, "********************************");
break;
// case S3E_OS_ID_WINDOWS:
// break;
} 
}

void GetCharBoostIdentifiers (char app[], char signature[])
{
int os = s3eDeviceGetInt(S3E_DEVICE_OS);

switch (os)
{
case S3E_OS_ID_ANDROID:
strcpy(app , "************************");
strcpy(signature, "****************************************");
break;
case S3E_OS_ID_IPHONE:
strcpy(app , "************************");
strcpy(signature, "****************************************"); 
break;
// case S3E_OS_ID_WINDOWS:
// break;
} 
}

void GetAdMobAdIdentifier(char code[])
{
int os = s3eDeviceGetInt(S3E_DEVICE_OS);

switch (os)
{
case S3E_OS_ID_ANDROID:
strcpy(code, "ca-app-pub-***************************");
break;
case S3E_OS_ID_IPHONE:
strcpy(code, "ca-app-pub-***************************");
break;
// case S3E_OS_ID_WINDOWS:
// break;
} 
}


AdEngine — API для програми
Шляхом тестування виявлено (і знайдено підтвердження на answers.madewithmarmalade.com), що ChartBoost-API фактично не викликає Callbackoв — це баг. У зв'язку з цим:
  • у нас немає можливості перевірити, чи вдало виконався запит кешування банера;
  • ставимо ChartBoost останнім пріоритету.
Логіка роботи движка реклами така:
через деякий час до передбачуваного моменту показу реклами запрашиванием кешування банера до тих пір, поки не сталося кешування банера хоча б по одному з сервісів;
— в момент коли потрібно показати рекламу, з тих сервісів, за яким щось закешировалось, вибираємо найбільш пріоритетний і показуємо його банер.
— якщо ні по одному з сервісів нічого не закешировано, то викликаємо метод показу банера ChartBoost. Буде показ реклами в цьому випадку — тут вже як пощастить.
adengine.cpp
#include "local.h"
#include "s3e.h"
#include "inmobi.h"
#include "leadbolt_ios.h"
#include "leadbolt_android.h"
#include "googleadmob.h"
#include "charboost.h"

#define DELAY_MS 1000 // мінімальний час між запитами на кешування реклами (в мілісекундах)
#define DELAY_4_SHOW_MS 60000 // 1 minute. мініальну час між показами реклами 

// час останнього запиту на кешування реклами (окремо по кожному сервісу)
int64 InMobiPrevTm, LDB_AndroidPrevTm, LDB_IOSPrevTm, CharBoostPrevTm, AdMobPrevTm;
int64 LastShowTm; // час останнього показу реклами

struct Struct_AdAvaliable
{
s3eBool InMobi;
s3eBool LeadBolt_Android;
s3eBool LeadBolt_IOS;
s3eBool CharBoost;
s3eBool AdMob;
};

Struct_AdAvaliable AdAvaliable;

/////////////////////////////////////////////////
/////////////////// API
/////////////////////////////////////////////////
void AdEngine_Init()
{
char AppCode [50];
char Signature[50];

AdAvaliable.InMobi = S3E_FALSE;
AdAvaliable.LeadBolt_Android = S3E_FALSE;
AdAvaliable.LeadBolt_IOS = S3E_FALSE;
AdAvaliable.CharBoost = S3E_FALSE;
AdAvaliable.AdMob = S3E_FALSE;

InMobiPrevTm = 0;
LDB_AndroidPrevTm = 0;
CharBoostPrevTm = 0;
AdMobPrevTm = 0; 
LDB_IOSPrevTm = 0;

LastShowTm = 0; 

AdAvaliable.LeadBolt_Android = LDB_Android_Avaliable();
AdAvaliable.LeadBolt_IOS = LDB_IOS_Avaliable();
AdAvaliable.InMobi = InMobi_Avaliable();
AdAvaliable.CharBoost = CharBoost_Avaliable();
AdAvaliable.AdMob = AdMob_Avaliable();

if (AdAvaliable.AdMob)
{ 
GetAdMobAdIdentifier(AppCode); // підтягуємо ідентифікатор файлу local.cpp
AdMob_Init(AppCode); 
} 

if (AdAvaliable.InMobi)
{
GetInMobiAppAdIdentifier(AppCode); // підтягуємо ідентифікатор файлу local.cpp
InMobi_Init(AppCode);
}

if (AdAvaliable.LeadBolt_Android)
{
GetLDBAppAdIdentifier(AppCode); // підтягуємо ідентифікатор файлу local.cpp 
LDB_Android_Init(AppCode);
} 

if (AdAvaliable.LeadBolt_IOS)
{
GetLDBAppAdIdentifier(AppCode); // підтягуємо ідентифікатор файлу local.cpp
LDB_IOS_Init(AppCode);
} 

if (AdAvaliable.CharBoost)
{ 
GetCharBoostIdentifiers(AppCode, Signature); // підтягуємо ідентифікатори з файлу local.cpp
CharBoost_Init(AppCode, Signature); 
} 
}


void AdEngine_Load() // Робимо запити реклами 
{ 
int status; 
int64 NewTm;

NewTm = s3eTimerGetMs(); 

if (AdAvaliable.AdMob == S3E_TRUE)
{
status = AdMob_Status();
switch (status)
{
case AD_ENGINE_INITED: AdMob_Load(); break;
case AD_ENGINE_LOAD_ERR: 
if (NewTm - AdMobPrevTm > DELAY_MS)
{
AdMobPrevTm = NewTm;
AdMob_Load();
}
break;

case AD_ENGINE_LOAD_OK: return; // зараз за пріоритетом AdMob - перший
}
}

if (AdAvaliable.InMobi == S3E_TRUE)
{
status = InMobi_Status();
switch (status)
{
case AD_ENGINE_INITED: InMobi_Load(); break;
case AD_ENGINE_LOAD_ERR:
if (NewTm - InMobiPrevTm > DELAY_MS)
{
InMobiPrevTm = NewTm;
InMobi_Load();
}
break;
case AD_ENGINE_LOAD_OK: return;
}
}

if (AdAvaliable.LeadBolt_IOS == S3E_TRUE)
{
status = LDB_IOS_Status();
switch (status)
{
case AD_ENGINE_INITED: LDB_IOS_Load(); break;
case AD_ENGINE_LOAD_ERR:
if (NewTm - LDB_IOSPrevTm > DELAY_MS)
{
LDB_IOSPrevTm = NewTm;
LDB_IOS_Load();
}
break;
case AD_ENGINE_LOAD_OK: return; 
}
}

if (AdAvaliable.LeadBolt_Android == S3E_TRUE)
{ 
status = LDB_Android_Status();
switch (status)
{
case AD_ENGINE_INITED: LDB_Android_Load(); break;
case AD_ENGINE_LOAD_ERR: 
if (NewTm - LDB_AndroidPrevTm > DELAY_MS)
{
LDB_AndroidPrevTm = NewTm;
LDB_Android_Load();
}
break;
case AD_ENGINE_LOAD_OK: return; 
} 
} 

// коментуємо AdAvaliable.CharBoost, так як його Callback-і не викликаються
/*
if (AdAvaliable.CharBoost)
{
status = CharBoost_Status();
switch (status)
{
case AD_ENGINE_INITED: CharBoost_Load(); break;
case AD_ENGINE_LOAD_ERR:
if (NewTm - CharBoostPrevTm > DELAY_MS)
{
CharBoostPrevTm = NewTm;
CharBoost_Load();
}
break;
}
}
*/ 
}


bool AdEngine_Show() // показуємо рекламу в порядку пріоритету
{ 
int status; 

int64 NewTm = s3eTimerGetMs();

// не показуємо рекламу частіше ніж через якийсь проміжок часу
if ((NewTm - LastShowTm) < DELAY_4_SHOW_MS && LastShowTm != 0) return true; 

if (AdAvaliable.AdMob)
{
status = AdMob_Status();
if (status == AD_ENGINE_LOAD_OK)
{
AdMob_Show();
LastShowTm = s3eTimerGetMs();
return true;
}
} 

if (AdAvaliable.LeadBolt_IOS)
{
status = LDB_IOS_Status();
if (status == AD_ENGINE_LOAD_OK)
{
LDB_IOS_Show();
LastShowTm = s3eTimerGetMs();
return true;
}
}

if (AdAvaliable.LeadBolt_Android)
{ 
status = LDB_Android_Status();
if (status == AD_ENGINE_LOAD_OK)
{
LDB_Android_Show();
LastShowTm = s3eTimerGetMs();
return true;
} 
} 

if (AdAvaliable.InMobi)
{
status = InMobi_Status();
if (status == AD_ENGINE_LOAD_OK)
{
InMobi_Show();
LastShowTm = s3eTimerGetMs();
return true;
}
}

if (AdAvaliable.CharBoost)
{
CharBoost_Show();
LastShowTm = s3eTimerGetMs();
return true;
} 

return false;
}

void AdEngine_Terminate()
{ 
if (AdAvaliable.InMobi)
{
InMobi_Release();
}

if (AdAvaliable.LeadBolt_Android)
{
LDB_Android_Terminate();
}

if (AdAvaliable.LeadBolt_IOS)
{
LDB_IOS_Terminate();
} 

if (AdAvaliable.AdMob)
{
AdMob_Terminate();
} 

if (AdAvaliable.CharBoost)
{
CharBoost_Terminate();
}
}




Правки AndroidManifest.xml
Доповнюємо список activity
<!-- inmobi -->
<activity
android:name="com.inmobi.androidsdk.IMBrowserActivity"
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|smallestScreenSize|screenSize"
android:theme="@android:style/Theme.Translucent.NoTitleBar">
</activity> 
<activity android:name="com.inmobi.android.sample.app.AdBannerActivity"
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|smallestScreenSize|screenSize" >
</activity>
<activity android:name="com.inmobi.android.sample.app.AdInterstitialActivity"
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|smallestScreenSize|screenSize">
</activity>
<service
android:name="com.inmobi.commons.internal.ActivityRecognitionManager"
android:enabled="true">
</service> 

<!-- Admob -->
<activity android:name="com.google.android.gms.ads.AdActivity"
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize"
android:theme="@android:style/Theme.Translucent" /> 
<meta-data android:name="com.google.android.gms.games.APP_ID" android:value="@string/gps_app_id" />
<meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version" />

<!-- CharBoost --> 
<activity android:name="com.chartboost.sdk.CBImpressionActivity"
android:excludeFromRecents="true" 
android:theme="@android:style/Theme.Translucent.NoTitleBar" />

<!-- Leadbolt --> 
<activity
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
android:name="com.apptracker.android.module.AppModuleActivity"
android:label="ModuleActivity"
android:theme="@android:style/Theme.Translucent"
> 
</activity>

<!-- Leadbolt. Required for Google Referrer --> 
<receiver android:name="com.apptracker.android.track.AppTrackerReceiver" android:exported="true">
<intent-filter> 
<action android:name="com.android.vending.INSTALL_REFERRER" /> 
</intent-filter> 
</receiver> 


Поповнюємо список дозволів
<!-- Доступ до інтернету та параметрами мережі -->
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>

<!-- Використання дозволів локації може підвищити дохід -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> 
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> 

<!-- Наступні два потрібні для Chartboost, щоб кешувати банер на storage. Без цього дозволу частота успішних показів істотно падає -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> 
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>


Сценарій використання
  • При ініціалізації додатка викликаємо AdEngine_Init();
  • У момент, коли треба закешувати банер, викликається AdEngine_Load(). Технічно ніщо не заважає вам викликати його кілька разів, перед тим, як викликати метод AdEngine_Show(). У методі AdEngine_Load() використовується константа DELAY_MS, яка регулирет фактичну частоту викликів. Також виклик кешування фактично не станеться, якщо по даному сервісу банер вже закеширован. Не рекомендується викликати сервіси занадто часто, оскільки вони можуть заблокувати ваш додаток;
  • У момент, коли треба показати банер — виклик AdEngine_Show();
  • Закриття сесії при закритті програми AdEngine_Terminate().


Досвід використання
Основна територія розповсюдження моїх додатків — це Росія, Україна, Казахстан і Білорусь. Є і в інших країнах, але цей відсоток незначний. В результаті року використання цих сервісів я відмовився від Inmobi і Leadbolt з наступних причин:
  • Leadbolt показуємо дуже низький середній показник вартості кліка (у порівнянні, наприклад, з AdMob);
  • Inmobi має низький FillRate і також низький показник вартості кліка.
Так як ChartBoost має непоганий FillRate, поки зупинився на парі AdMob (як основний сервіс) і ChartBoost (для випадку, коли за AdMob запит на кешування повернув помилку).
В планах є інтеграція з Appodeal — якщо все пройде вдало, то обов'язково доповню статтю.
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.