Saturday, January 10, 2015

Be wary of Android SDK updates

Android SDK contains all of the build tools and libraries necessary to create Android apps.

The Android SDK Manager is used to pull updates to those tools and libraries and Google supplies the libraries via 2 artifact repositories. This is a good thing as you get the benefits of artifact versioning etc. (NB I won't rant here about why these artifact repositories should be global instead of machine local - suffice to say it is a barrier to dev, particularly open source dev).

The problem is that the Android SDK Manager doesn't behave like a well mannered repository manager when it is updating it's 2 repos.

It should just download the new library versions and place them within the repo.

Instead it removes the repo and recreates it from a combination of

  • new libraries
  • existing libraries that it thinks should be there
If you have added artifacts to your Google repos, to get around Issue#72807 for instance, then you will have lost those artifacts and will need to recreate them.

So lessons from this:
  1. Don't rely on Android SDK Manager to be a good citizen. Store those modified Google artifacts in a separate repository of your own (mea culpa).
  2. Google - fix Issue#72807 so we don't have to individually create modified Google artifacts. With the advent of PlayServices-6.5.87 there are 17+ more artifacts that have invalid dependency information.

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>

Saturday, March 2, 2013

Connecting any interstitial provider with Admob mediation

Admob mediation provides a way to code your Android app to include banner or interstitial ads just the once but to change the networks supplying those ads on the fly.

I'm going to show you just how easy it is easy to write an adapter that can receive interstitial requests from Admob mediation and serve up interstitial impressions from an ad network.

We need to implement CustomEventInterstitial which performs all the work.

  • requestInterstitialAd() retrieves an ad
  • showInterstitial() displays that ad
  • destroy() performs any cleanup

One thing to remember when retrieving your ad is that you need to notify the mediation layer about whether retrieval was successful or not. So you attach a listener to the ad and when the listener fires, relay that event back to the mediation layer via the mediation listener. Also, if for any reason your network has no ability to retrieve an ad (eg wrong SDK version) then you should report the failure back to the mediation layer immediately so that it can ask the next ad network in the queue for an impression.

Here's the source for requestIntersitialAd()


    
Pretty simple! And to display the ad is even easier.



There's no really much to cleanup here, so that's all the coding.

Make sure to:

  1. Include the libraries for your target network when building your app 
  2. Add Proguard config so leave you mediation adapter intact as the Admob mediation layer will be looking up it via reflection. 
  3. Create a CustomEvent in you Admob mediation config that points to your adapter and passes in the network id for your app.

So now you can serve up ads from any ad network all via Admob mediation. Full source code for the above along with several other mediation adapter and an example app can be found at https://github.com/william-ferguson-au/Admob-CustomEvents

Enjoy

Thursday, January 24, 2013

Simple Google-OAuth2 from a Java client

OAuth is a powerful way to ensure that clients have access to appropriate resources, and the Google OAuth libs do a lot of the heavy lifting for you. But while the doco is pretty good I found it lacking when looking for the most appropriate way to authorize for a simple Java (non-web) application and that's probably to do with the rate at which this area is moving.

It turns out that there are excellent classes in the Google libraries to make OAuth absolutely trivial, in fact it's really only 3 lines:
  1. Construct your AuthorizationCodeFlow
  2. Construct your AuthorizationCodeInstalledApp
  3. Ask the InstalledApp to authorize the client
Here's the full code listing:


Now to make this happen you're going to need to include the relevant Google libs. You can find lots of good info about the libs here google-api-java-client, google-oauth-java-client and google-http-java-client.

The libs necessary for the above are:

Wednesday, July 27, 2011

Tracking user behaviour in an Android app

If your app asks the the INTERNET permission then I recommend that you embed an analytics library into your app right from the start. This will let you get near real time information about who is using your app and how they are using your app.

Adding an analytics library is easy. I use Flurry and at it's simplest you just add the following to each of your Activities:

    @Override
    public void onStart() {
        super.onStart();
        FlurryAgent.onStartSession(this, FLURRY_KEY_FOR_THIS_APP);
    }

    @Override
    protected void onStop() {
        super.onStop();
        FlurryAgent.onEndSession(this);
    }
This will give you all kinds of information about your users. Such as how many times is your app used per day and how much time do user's spend on it. And also in what order are different activities invoked and how much time ares user's spending on each Activity.

Subsequently you can start getting finer grained information by reporting on specified events, such as how times was a new game started and hence on average how many games are played in a single session by a user. This is a simple as adding the following as required.

FlurryAgent.onEvent("gameStarted");

You can even provide a Map of arbitrary parameters to be associated with the event.

final Map<String, String> params = new HashMap<String, String>();
params.put("score", getScore(jumble));
params.put("percentFound", getPercentFound(jumble));
params.put("wordsPerMinute", getWordsPerMinute(jumble));
FlurryAgent.onEvent("gameOver", params);

The event data has let me get a good understanding of how people are playing the game. And helped me tailor my development efforts so that I'm spending time improving areas that are of interest and relevance to my users. Don't get me wrong, direct feedback from user's is gold, but it's rare and it's the voice of a highly motivated individual, it may not reflect the vast majority, that's where the statistics provided by an analytic engine comes to the fore.

Overall the data from Flurry (and I expect any analytic engine) is more than 10 times as much information as is available via the Android Market. I just wish I had it embedded right from the beginning, because it's not entirely clear how many user's are still running old versions for which I have no info.

Tuesday, June 7, 2011

Best 2 design decisions I made for my Android app

I published my first app for the Android ecosystem a bit over a month ago. It's a word puzzle game called Jumblee. There are 2 design decision that I made early on that have paid tremendous dividends and I believe are worthy of consideration for all Android apps.

The first was to include ACRA to capture any app failures no matter what Android version, and to post details of the failure including the stacktrace to a GoogleDoc hosted spreadsheet. You can configure ACRA to report silently or to present a dialog to the user and to capture a variety of information including a user comment. I chose a simple Toast notification and posting of the standard set of fields. I also configured the target spreadsheet so that I receive an email the moment it is modified.

Having ACRA embedded meant that I was aware the instant one of my users found the first bug (and believe me, no matter what testing regime you put in place, Android's heterogeneous hardware and OS environment will cause you to miss something). Before 99% of my users had come across the issue I already had a solution and a new version of my app ready for distribution.

Which leads me to the second decision that has paid back its effort ten fold. Its no use having a new version of your app that fixes a killer bug if no one knows the new version exists. So I built in a component that on startup hits my server to find out the latest version of Jumblee (you could probably have it ping the Market instead using the unofficial market-api). If a more recent version is available it displays a dialog letting the user know and asking if they'd like to download the new version now.

This has meant that my user's have kept rolling forward with new versions quite quickly and I'm not swamped with reports of bugs that have long since been fixed.

I know that the Android Market periodically reminds users about new versions of apps, but the timing of those notifications isn't clear to me, and when I'm confronted with a plethora of updates at once (especially when out of wireless coverage) I sometimes clear and ignore the lot. I'm more likely to accept a single update that is relevant to me right now and I think that is the same for my users.

Well, without these 2, I would have been in a world of pain trying to support my app, and wouldn't have as good a market rating as I do. Its not much of an investment for a heap of gain, so I'd heartily recommend you consider using both techniques.

Bon apetit.