📜 ⬆️ ⬇️

OpenAdAdapter - Simple Mobile Advertising Management

A few years ago, I decided that inserting one ad network into a mobile application was not effective enough and thrust several networks, and this winter I decided to rewrite and post this development on github. This is how the OpenAdAdapter was born.

OpenAdAdapter is a library for mobile games (Android and iOS, Apache 2.0 license). I decided to make an adapter for games, not for all applications, to make the API easier. By game, I mean an application that has one GL canvas on the whole screen, and the banner is located above or below. When the banner is there, the canvas must be slightly moved. That is, the developer simply says: show the banner from the bottom, without having to delve into how to put the network X into layot. Many games are developed using SDK and engines like Marmalade or Unity. They get to the native platform and learn all the nuances of the implementation of Kolbek, this is a separate jitsu. By the way, this is why there are no callbacks in OpenAdAdapter.

It is assumed that the OpenAdAdapter API can be called from any thread. (I conceived it, but I am afraid to blame). All methods are static.
')
The following networks are currently supported:

Android

- AdColony
- Admob
- AerServ
- Chartboost
- heyzap
- InMobi

iOS

- AdColony
- Admob
- AerServ
- Chartboost
- heyzap
- InMobi
- iAd

Creating an adapter for a new network is easy. I initially wanted to support the Russian WapStart as they once paid me an incredible $ 1 per click for two days for Moscow traffic (usually 5 cents), then I fined 30% and gave money, but adding them would complicate everything on the android. The fact is that all networks give a jar file for integration, and WapStart gave a project for eclips with resource files. That's why I have not implemented them yet.

Settings are downloaded from the JSON file from the Internet.

OpenAdAdapter.initFromUrl( this, "https://raw.githubusercontent.com/sample-data/oad1/master/android-redirect.json"); 


  [OpenAdAdapter startWithUrl:@"https://raw.githubusercontent.com/sample-data/oad1/master/ios-redir.json"]; 


(Hosting static github files may violate github rules)

It is very important. You can change the strategy or stop showing advertisements of a network, or a network may ban you.

OpenAdAdapter can show the banner from above or below, hide the banner, show the ad on full screen (fullscreen / interstitial), video (video) and video for reward (rewarded). What and how to show is described in the json file.

iOS


Controller for iphone
 #import "ViewController.h" #import "OpenAdAdapter.h" @interface ViewController () @property (weak, nonatomic) IBOutlet UIButton *btnInit; @property (weak, nonatomic) IBOutlet UILabel *label1; @end @implementation ViewController { bool btick; NSString * rewardText; } - (IBAction)clickChkReward:(id)sender { OADReward * reward = [OpenAdAdapter reward]; if(reward != nil){ self->rewardText = [NSString stringWithFormat:@"%@ %f %@", [reward network], [reward amount], [reward currency]]; }else{ self->rewardText = @""; } } - (IBAction)clickInit:(id)sender { if(!self->btick){ self->btick = true; [self performSelector:@selector(tick) withObject:nil afterDelay:1.0]; } self.label1.text = @"Initializing 1"; [OpenAdAdapter startWithUrl:@"https://raw.githubusercontent.com/sample-data/oad1/master/ios-redir.json"]; // [OpenAdAdapter startWithUrl:@"https://raw.githubusercontent.com/sample-data/oad1/master/ios-no-heyzap.json"]; //[OpenAdAdapter startWithUrl:@"https://raw.githubusercontent.com/sample-data/oad1/master/ios-heyzap.json"]; self.label1.text = @"Initializing 2"; } -(void)tick{ [self performSelector:@selector(tick) withObject:nil afterDelay:1.0]; NSString * s1 = [NSString stringWithFormat:@"bh %g %g %@", [OpenAdAdapter bannerHeightPts], [OpenAdAdapter bannerHeightPixels], self->rewardText]; self.label1.text = s1; } - (IBAction)clickBanner:(id)sender { [OpenAdAdapter showTopBanner:self]; } - (IBAction)clickBottomBanner:(id)sender { [OpenAdAdapter showBottomBanner:self]; } - (IBAction)clickHideBanner:(id)sender { [OpenAdAdapter hideBanner]; } - (IBAction)clickFullscreen:(id)sender { [OpenAdAdapter showFullscreen:self]; } - (IBAction)clickVideo:(id)sender { [OpenAdAdapter showVideo:self]; } - (IBAction)clickRewarded:(id)sender { [OpenAdAdapter showRewarded:self]; } - (IBAction)clickTest:(id)sender { // [TestX1 test1]; [OpenAdAdapter test1]; } - (IBAction)clickTest2:(id)sender { //[OpenAdAdapter test2:self]; [OpenAdAdapter showTopBanner:self]; } - (IBAction)clickTest3:(id)sender { //[OpenAdAdapter test3:self]; [OpenAdAdapter showBottomBanner:self]; } - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } @end 


The difference in linking applications for iOS (from Android) made it not just one lib, but one + adapter for each network.
That is, if you need Chartboost, then add to the project

- libOADAdapterChartboost.a - adapter from OpenAdAdapter
- Chartboost.framework - the original framework from Chartboost

github.com/OpenAdAdapter/OAD-iOS-bin/tree/master/chartboost

- libOpenAdAdapter.a - and the OpenAdAdapter itself

The simplest is to throw all the networks at once.

github.com/OpenAdAdapter/OAD-iOS-bin

Add the necessary frameworks:

  AdSupport.framework StoreKit.framework MessageUI.framework libxml2.2.dylib libz.dylib libsqlite3.0.dylib CoreTelephony.framework EventKit.framework EventKitUI.framework Security.framework Social.framework WebKit.framework 


and Other Linker Flags: -ObjC

Android


Activation from the example
 package com.example.testoad01; import com.openadadapter.OpenAdAdapter; import com.openadadapter.Reward; import android.support.v7.app.ActionBarActivity; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.TextView; import android.widget.Toast; public class MainActivity extends ActionBarActivity { Runnable tick = new Runnable(){ @Override public void run() { try{ label1.setText("bh " + OpenAdAdapter.getBannerHeightInPoints() + " " +OpenAdAdapter.getBannerHeightInPixels()); } finally{ handler.postDelayed(tick, 1000); } }}; Handler handler = new Handler(Looper.getMainLooper()); boolean ticking; private TextView label1; @Override protected void onCreate(Bundle savedInstanceState) { // TestX.test(); super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); OpenAdAdapter.onCreate(this); label1 = (TextView)findViewById(R.id.textView1); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { // Handle action bar item clicks here. The action bar will // automatically handle clicks on the Home/Up button, so long // as you specify a parent activity in AndroidManifest.xml. int id = item.getItemId(); if (id == R.id.action_settings) { return true; } return super.onOptionsItemSelected(item); } public void butBS(View v) { Toast.makeText(getApplicationContext(), "Bottom Banner Show", Toast.LENGTH_LONG).show(); OpenAdAdapter.showBottomBanner(null); } public void butBSTop(View v) { Toast.makeText(getApplicationContext(), "Top Banner Show", Toast.LENGTH_LONG).show(); OpenAdAdapter.showTopBanner(null); } public void butBH(View v) { Toast.makeText(getApplicationContext(), "Banner Hide", Toast.LENGTH_LONG).show(); OpenAdAdapter.hideBanner(); } public void butF(View v) { Toast.makeText(getApplicationContext(), "Banner Fullscreen", Toast.LENGTH_LONG).show(); OpenAdAdapter.showFullscreen(null); } public void butVideo(View v) { Toast.makeText(getApplicationContext(), "Banner Video", Toast.LENGTH_LONG).show(); OpenAdAdapter.showVideo(null); } public void butR(View v) { Toast.makeText(getApplicationContext(), "Rewarded Video", Toast.LENGTH_LONG).show(); OpenAdAdapter.showRewarded(null); } public void butIU(View v) { Toast.makeText(getApplicationContext(), "Init from URL", Toast.LENGTH_LONG).show(); // OpenAdAdapter.initFromUrl(this, // "https://raw.githubusercontent.com/sample-data/oad1/master/data1.json"); OpenAdAdapter .initFromUrl( this, "https://raw.githubusercontent.com/sample-data/oad1/master/android-redirect.json"); if(!ticking){ handler.postDelayed(tick, 1000); ticking = true; } } public void butIF(View v) { Toast.makeText(getApplicationContext(), "Init from File", Toast.LENGTH_LONG).show(); // OpenAdAdapter.preinit(); } public void butV(View v) { Toast.makeText(getApplicationContext(), "verify", Toast.LENGTH_LONG) .show(); OpenAdAdapter.verify(); } public void butSF(View v) { Toast.makeText(getApplicationContext(), "Show Fullscreen", Toast.LENGTH_LONG).show(); OpenAdAdapter.showMyFullscreen(this); } public void clickFetchReward(View v) { Reward reward = OpenAdAdapter.fetchReward(); if (reward == null) { Toast.makeText(getApplicationContext(), "No reward", Toast.LENGTH_LONG).show(); } else { Toast.makeText( getApplicationContext(), "Reward " + reward.getNetwork() + " " + reward.getAmount() + " " + reward.getCurrency(), Toast.LENGTH_LONG) .show(); } } @Override public void onStart() { super.onStart(); OpenAdAdapter.onStart(this); } @Override public void onResume() { super.onResume(); OpenAdAdapter.onResume(this); } @Override public void onPause() { super.onPause(); OpenAdAdapter.onPause(this); } @Override public void onStop() { super.onStop(); OpenAdAdapter.onStop(this); } @Override public void onDestroy() { super.onDestroy(); OpenAdAdapter.onDestroy(this); } @Override public void onBackPressed() { if (OpenAdAdapter.onBackPressed(this)) return; super.onBackPressed(); } } 


Since some networks want to receive onStart, onPause, onResume, etc. notifications, then the OpenAdAdapter on android needs to be called on these events.

You also need to not forget about the activation package, which you need to specify in AndroidManifest.xml

And throw all jar files into the lib folder (unnecessary ones can be thrown out): github.com/OpenAdAdapter/OAD-Android-bin/tree/master/lib

Unobvious - probable


Important point: it is not desirable that the banner overlaps the program interface. Just this problem and pushed me to create a project. My application has determined whether or not the banner is based on what the delegate / callback / lisner reports.

- AdShown - the screen shrank
- AdFailed - screen went flat

That is, I did not take a screenshot of the screen and did not recognize the image of the banner on it, but it turned out that the most excellent advertising network does not work as I expected. She can tell AdFailed, and when the application expands the canvas to full screen - ban the application.

The following functions are the solution to the problem of overlapping content

 int pt = OpenAdAdapter.getBannerHeightInPoints(); int px = OpenAdAdapter.getBannerHeightInPixels(); 

 int pt = [OpenAdAdapter bannerHeightPts]; int px = [OpenAdAdapter bannerHeightPixels]; 

If the value 0 - there is no banner, the value is greater than 0 - the banner is on top, the value is less than 0 - the banner is below. You can call each frame or once a second and change the dimensions of the GL canvas accordingly.

In the same way without callbacks, I decided to implement a check for receiving rewards for viewing the rewarded video.

JSON configuration file


raw.githubusercontent.com/sample-data/oad1/master/data1.json :

 { "debug":{"verify":true}, "urls":["https://ohohoho.appspot.com/track", {"url":"https://xman545476.appspot.com/track", "priority": 10}], "commands":["save", {"cmd":"settings", "settings":{"reportLocation":true, "advertisingId":true, "userId": true}}], "strategy":{ "banner": {"list":["admob", "inmobi", "wapstart", "aerserv"], "strategy":"random"}, "fullscreen2": { "list":[ "aerserv", "inmobi"], "strategy":"random"}, "fullscreen": { "list":[ "aerserv", "heyzap", "adcolony", {"name":"heyzap","type":"rewarded", "preload": "always"}, "admob", {"name":"chartboost","type":"video", "preload": "low"}, "inmobi"], "strategy":"random"}, "video": {"list":["chartboost", "heyzap"],"strategy":"round-robin"}, "rewarded": {"list":["adcolony", "chartboost", "heyzap"], "strategy":"random"} }, "networks":[ { "name":"admob", "bannerId":"ca-app-pub-8607147313123654/8458359243", "fullscreenId":"ca-app-pub-8607147313123654/9935092440" }, { "name":"inmobi", "propId":"d4783f2efd4147499e40cc3540f2d221", "bannerId":"d4783f2efd4147499e40cc3540f2d221", "fullscreenId":"d4783f2efd4147499e40cc3540f2d221", "bannerId1":"1428178968194889", "fullscreenId1":"1428178928625995" }, { "name":"chartboost", "id":"5519c460c909a67c4e1e58a4", "sig":"e34adc93d56dfcff2a3a34d5f0dcd74204cbaf05", "video": "true", "rewarded": "true" }, { "name":"heyzap", "id":"e0c44b21d39c921a55f31ea836a70b65", "video": "true", "rewarded": "true" }, { "name":"adcolony", "id":"app398cb71e4cae463f94", "videoId":"vzc71a58270b924c95a0", "rewardedId":"vz6669f90f9dbe4e4a89" }, { "name":"aerserv", "fullscreenId":"1000741", "banner320":"1000834", "banner728":"1000835" }, { "name":"home", "bannerId":"", "halfId":"", "fullscreenId":"" }, { "name":"direct", "bannerId":"", "halfId":"", "fullscreenId":"" } ] } 

The first three keys are debug, urls, commands - not yet used. In the key networks are listed networks. The last two (home, direct) are also not used yet. I am developing a server that will be more intelligent than a JSON file.

The key strategy is the most difficult in this config. He has 4 subkeys for 4 strategies - banner, fullscreen, video, rewarded.
(fullscreen2 - not used)

 "banner": {"list":["admob", "inmobi", "wapstart", "aerserv"], "strategy":"random"} 

Each strategy has in turn two keys list and strategy.

- list — lists networks;
- strategy - determines the strategy: random, round-robin, failsafe;

random - shows ads randomly;
round-robin - shows ads in turn;
failsafe - always shows the first advertisement from the list, and if it does not work, then the second one.

I have written an HTML application to create this file. I'll post it soon.

How the project will develop


HTML - config editing application (already written);
Website - I plan to make a website so that the developer selects networks and receives a ZIP file (which will contain everything and can be simply copied to the project with one hand movement) and instructions on what frameworks to add and what to write in AndroidManifest.xml;
Unity3D is written, all types of advertising are shown on both platforms, but there everything is not so simple done and the implementation description requires a separate article, as long as there is no support for UnityAds;
Server - there are a lot of ideas, you can endlessly develop;
Tuning - the size of banners relative to the screen, smart and economical preload of networks, etc .;
Self Test - in the android project you can notice the verify method. In theory, he should check whether all necessary activations, permishes, receivers and meta tags are defined with the google play services version. And he partially does this, but until the end this functionality is not implemented;
An article on Megamind - with the economic rationale for using the OpenAdAdapter against Mopub, AdTapsy, Appodeal and others promising an incredible CTR.

Source: https://habr.com/ru/post/262715/


All Articles