Wednesday, 18 April 2018

How to show Admob ads in an Apache Cordova app on Android

Since the demise of AdWhirl and my previous post about how to set up ads with Cordova, I've had to revisit it and get it working with AdMob which now uses Google Play Services.  Again there are very few instructions around for this, though I did find this useful looking Cordova plugin which seems to offer a neat solution which is likely easier than the solution I'm about to propose and is cross-platform where I am only working with Android.  However, for various reasons, I don't want to use the Cordova tools, I want to do it manually.  If you're like me then here is how to do it

This How-To uses a mash of the Cordova official documentation on WebViews, my previous post on AdWhirl and the Admob Quick Start guide.  The premise here is that, since Cordova can be made to display in an Android WebView, and AdMob also uses WebViews, we can combine the two and display one in front of the other

I'll assume you have an AdMob account and know how to create an Android app in your favourite IDE (I will be using Eclipse with ADT), or have an existing Android app running Cordova that you want to add Admob to

1. Set up Cordova to use a WebView

This largely follows the official Cordova WebView documentation with some small differences.  If you haven't set up your Android app to use Cordova yet you can follow much of the above tutorial which will create a simple Android app that uses Cordova WebViews

I found that the instructions for the Activity were slightly incorrect, so I've modified it slightly to not use the Config static object, but load the page like this

cwv.loadUrl("file:///android_asset/www/index.html");

I have called my view CordovaView instead of TutorialView

    <org.apache.cordova.CordovaWebView
    android:id="@+id/cordovaView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"  />

2. Add AdMob to your app

Add Google Play Services

AdMob is no longer a downloadable SDK but instead uses Google Play Services.  Firstly you need to add this to your Android app.  There are instructions for different IDEs on this Android Developer Page.  This will allow you to use Google Play Services in your app

From here you can largely follow the Admob quick start guide but I will take you through the steps here

Modify your AndroidManifest.xml 

There are some AndroidManifest.xml changes you need to make to enable Google Play Services

You need to add some meta data to the <application>

<meta-data android:name="com.google.android.gms.version"
        android:value="@integer/google_play_services_version" />

and also add an activity

        <activity android:name="com.google.android.gms.ads.AdActivity"
            android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize"
            android:theme="@android:style/Theme.Translucent" />

Also add these permissions

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

Modify your project.properties file

You need to add Google Play Services to your project.properties file (in the root of your app) by adding the line

android.library.reference.1=../google-play-services_lib

Add an ad fragment view

Following the quick start guide, create a new layout called fragment_ad.xml in res/layout to define how the ad will be displayed as follows

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:ads="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.google.android.gms.ads.AdView
        android:id="@+id/adView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        ads:adSize="BANNER"
        ads:adUnitId="@string/banner_ad_unit_id">
    </com.google.android.gms.ads.AdView>

</RelativeLayout>

You also need to create the banner_ad_unit_id referenced here in your values/strings.xml.  This will be your admob unit ID, but you can use the test one for now which displays a dummy ad (don't forget to replace it with your real unit ID before you go live)

<string name="banner_ad_unit_id">ca-app-pub-3940256099942544/6300978111</string>

Create an Activity for the ad view

You need to create an activity for the view in order to load the advert

public class AdFragment extends Fragment {

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_ad, container, false);
    }

    
    @Override
    public void onActivityCreated(Bundle bundle) {
        super.onActivityCreated(bundle);
        
        int googlePlayServicesStatus = GooglePlayServicesUtil.isGooglePlayServicesAvailable(MyApplication.getAppContext());
        // if google play services is available
        if (googlePlayServicesStatus == ConnectionResult.SUCCESS) {
        // load the ad
       AdView mAdView = (AdView) getView().findViewById(R.id.adView);
       AdRequest adRequest = new AdRequest.Builder().build();
       mAdView.loadAd(adRequest);
        }
        // else do nothing
    }
}

There are a few things to note there:
  • the fragment_ad layout name in the onCreateView() method needs to match the view name you created in the layout.  If you called it fragment_ad as in my example, you're all set
  • We reference GooglePlayServicesUtil in order to ascertain if Google Play Services is loaded on the device.  If it isn't, or you are using an emulator, you will get an exception.  Annoyingly this gives the user control over whether they receive ads since, with a rooted phone or one running a custom ROM, they may not have it installed.  If anyone has a solution to this, I'm keen to hear it
  • We do nothing if we can't connect to Google Play Services since we can't load the adverts using it
  • I needed a way of accessing the Context object.  I found an answer for this at Stack Overflow which involved creating a class for accessing the Context in a static way.  I've replicated the answer below
import android.app.Application;
import android.content.Context;

public class MyApplication extends Application {

    private static Context context;

    public void onCreate(){
        super.onCreate();
        MyApplication.context = getApplicationContext();
    }

    public static Context getAppContext() {
        return MyApplication.context;
    }
}

and add to the AndroidManifest.xml under the <application> tag

  android:name="eu.weblore.bridgetag.severn.MyApplication"

3. Link Cordova and AdMob WebViews

We now need to link the web views we have created in order to get them both to diplay

We need to do 3 things here:

Alter the layout type

We need to alter the layout type in res/layout/main.xml from the default LinearLayout to RelativeLayout.  Don't forget the closing tag :)

Display the ad view

We need to add a fragment child view of the main view in order to retain space for the advert.  This is separate to the Google Play Services ad fragment we already created which is the fragment for the advert itself.  We need a fragment in our main view in which to display the Google fragment

    <fragment
        android:id="@+id/adFragment"
        android:name="your.package.AdFragment"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true" />

The android.name should relate to the Activity class you created for displaying the ad fragment.  I called mine AdFragment.  

The alignParentBottom tells the layout manager that we want the fragment to be displayed at the bottom of the parent layout, this can be changed if you wish to display it elsewhere.  You can alignParentTop if you wish, or anywhere else from the RelativeLayout API

Add the ad view as a child of the main view

Finally we need to add the fragment as a child of the main view by adding this line to the main view, which I called CordovaWebView

     android:layout_above="@+id/adFragment"

Your final main.xml should resemble the following

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >
    
    <org.apache.cordova.CordovaWebView
    android:id="@+id/cordovaView"
    android:layout_width="match_parent"
    android:layout_height="match_parent" 
    android:layout_above="@+id/adFragment" />
   
    <fragment
        android:id="@+id/adFragment"
        android:name="eu.weblore.bridgetag.severn.AdFragment"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true" />
    
</RelativeLayout>

And you're done!


If you now run your app you should display an ad from AdMob where you specified

Don't forget to use your real AdMob ID when you deploy your app to Google Play