Wednesday, December 17, 2014

Google TagManager for Android

TagManager, what's that? Actually that's a pretty fair question.

Google hasn't done a great job of explaining what TagManager is, why you care and how to use it very well, especially for Android. This is probably because TagManager has come from the world of web marketing and has really only recently received some Android love.

But now that TagManager is part of Google Play Services (and if you aren't using Google Play Services yet you should really be asking why you aren't) integrating it into your app is a breeze.

Firstly: What is it and why should you use it?

I won't dig into the full details of TagManager, I'll let you explorer those later. Where I think you'll get most value out of TagManager initially is by using it to specify app config values for which you might want to push out new values to all your users. Eg I only show interstitial ads after 20% of games played in one of my apps. But maybe I want to be able to play with that after I have shipped. TagManager will let me do that by updating the config in its web interface and publishing a new version of the TagManager Container.

How to integrate TagManager

You'll need to sign up for TagManager (etc), create a Container and for app config variables create a MACRO of type value-collection. Start here https://developers.google.com/tag-manager/android/v4/

Don't worry too much about the weird names that TagManager uses (MACRO etc). There is a revamp due to be rolled out Jan 2015 that brings a nicer UI and names that are a lot clearer.

Once you have created and downloaded your TagManager Container to res/raw (heads up - the default name for the Container file does not follow Android resource naming conventions and you will have to change it), you can start adding code.

Start with your Application class and add the following:

// Make sure we always have a TagContainer instance
private TagContainer tagContainer = new TagContainer(null);

public TagContainer getTagContainer() {
    return tagContainer;
}

// Call this from Application#onCreate
private void configureTagManager() {

    final TagManager tagManager = TagManager.getInstance(this);
    final PendingResult<ContainerHolder> pending = tagManager.loadContainerPreferNonDefault(GTM_CONTAINER_ID, R.raw.gtm_mycontainer_v5);
    pending.setResultCallback(new ResultCallback<ContainerHolder>() {
        @Override
        public void onResult(ContainerHolder containerHolder) {
            tagContainer = new TagContainer(containerHolder);
            if (containerHolder.getStatus().isSuccess()) {
                Log.i(TAG, "GTM container loaded");
            } else {
                Log.w(TAG, "Failure loading GTM container : " + containerHolder.getStatus().getStatusMessage());
            }
        }
    });
}


We introduced the TagContainer class here to make our life simple. It looks like:

public class TagContainer {

  private static final String SHOW_FULL_SCREEN_AD_PERCENTAGE = "showFullScreenAdPercentage"; 

  private final ContainerHolder containerHolder; 

  public TagContainer(ContainerHolder containerHolder) {  
    this.containerHolder = containerHolder; 
  } 

  /**
   * Defaults to 0.20
   */
  public double showFullScreenAdPercentage() { 
    final Container container = getContainer(); 
    return container == null ? 0.20 : container.getDouble(SHOW_FULL_SCREEN_AD_PERCENTAGE); 
  } 

  private Container getContainer() { 
    return containerHolder == null ? null : containerHolder.getContainer(); 
  }
}
And finally add it into the code where you want to use the value:

    if (coinToss < getTagContainer().showFullScreenAdPercentage()) {
        // show interstitial
    }

Voila, instant app config.

The End Game

So now you can ramp the ad percentage up or down without needing to release a new version of your app. Just update the TagManager web interface, publish the new version of the Container and within 12 hours it will ave rolled out to all your users.

So for those of you who have been managed your own server and transport for app config, here's your chance to cut down on the code you need to manage.

And now that you have that ability, what else do you want to be able to tweak in your app after deployment?

Wednesday, November 26, 2014

Handling missing Android support-v4:{20-21} Jars

Some of the newer Android/Google libraries have been published with invalid dependency information. It looks like an endemic issue, but the ones that have caught me so far have been

  • com.google.android.gms:play-services:6.1.71
  • com.android.support:appcompat-v7:21.0.0

The problem is that these libraries declare a dependency on support-v4:20 or support-v4:21 but don't specify the type of that dependency. The default type for a dependency is JAR, but Google published support-v4:20 and 21 as AAR libraries.

This means that when you build, the build mechanism has no way of knowing that you really wanted to pull in support-v4:20 AAR and so will fail with missing dependencies.

This has been raised with the AOSP as Issue#72807 so please star it if it irritates you as much as me.

My suggestion for working around these are to define your own replacement artifacts that using the same AAR files but contain a POM defining valid dependencies. Don't forget to define them as an entirely new version. I have tagged mine as "-b" version

Here are those I am using:

For com.google.android.gms:play-services:6.1.71-b

<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.google.android.gms</groupId>
  <artifactId>play-services</artifactId>
  <version>6.1.71-b</version>
  <packaging>aar</packaging>
  <dependencies>
    <dependency>
      <groupId>com.android.support</groupId>
      <artifactId>support-v4</artifactId>
      <version>20.0.0</version>
      <scope>compile</scope>
      <type>aar</type>
    </dependency>
  </dependencies>
</project>

For com.android.support:appcompat-v7:21.0.0-b

<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.android.support</groupId>
  <artifactId>appcompat-v7</artifactId>
  <version>21.0.0-b</version>
  <packaging>aar</packaging>
  <dependencies>
    <dependency>
      <groupId>com.android.support</groupId>
      <artifactId>support-v4</artifactId>
      <version>21.0.0</version>
      <scope>compile</scope>
      <type>aar</type>
    </dependency>
  </dependencies>
</project>


NB if you have externally facing projects where you can't have a dependency on a play-services version that you have constructed yourself (with the correct dep), then you can manually exclude support-v4:jar from the play-services dep in your project, and then add the support-v4:aar dep yourself. Thanks to +Hugo Visser for this suggestion.

<dependency>
    <groupId>com.android.support</groupId>
    <artifactId>appcompat-v7</artifactId>


    <version>21.0.0</version>
    <type>aar</type>
    <exclusions>
        <exclusion>
            <groupId>com.android.support</groupId>
            <artifactId>support-v4</artifactId>
        <exclusion>
    <exclusions>
</dependency>
<dependency>
    <groupId>com.android.support</groupId>
    <artifactId>support-v4</artifactId>


    <version>21.0.0</version>
    <type>aar</type>
</dependency>