Creating a free/paid app pair for the Android Market

If you have an app in the Android Market of which you want to have both a free version and a paid version, there are basically two ways of doing so. I will describe these two ways in this post. (Disclaimer: This is something that I haven’t tested yet myself, and I’m actually writing this to make sure I won’t forget it myself. On top of that, I don’t know whether there are any other/better ways of doing what I will describe below.)

Two completely separate apps

You can put two completely separate applications in the Market, with the free version covering a subset of the functionality of the paid version. While this is done quite often, this introduces a couple of minor problems:

  • If people are satisfied with the free version and want to purchase the paid version, they have to uninstall the free version first and then purchase and install the paid version. If they don’t, they might run into errors (depending on your application and to what extent you messed up your code). You have two separate applications with split download counts in your Developer Console, even though they’re in fact the same application.
  • You have to take measures for your development environment to be able to build two different versions of your application with little effort. For quite a lot of people, that can be a bit cumbersome.

A single application with all functionality and a ‘key’ app

To tackle these two problems mentioned above, you can also put all your functionality in a single app, and create a paid app that will serve as a ‘key’ to your app and sign them with the same certificate. Then, you can easily check if the user has installed that paid ‘key’ app if he wants to use a feature that needs to be enabled for paying users only.

Let’s take a look at the following code snippet.

protected boolean isProInstalled(Context context) {
  // the packagename of the 'key' app
  String proPackage = "org.yoki.android.pkgname";

  // get the package manager
  final PackageManager pm = context.getPackageManager();

  // get a list of installed packages
  List<PackageInfo> list = pm.getInstalledPackages(PackageManager.GET_DISABLED_COMPONENTS);

  // let's iterate through the list
  Iterator<PackageInfo> i = list.iterator();
  while(i.hasNext()) {
    PackageInfo p = i.next();
    // check if proPackage is in the list AND whether that package is signed
    //  with the same signature as THIS package
    if((p.packageName.equals(proPackage)) &&
       (pm.checkSignatures(context.getPackageName(), p.packageName) == PackageManager.SIGNATURE_MATCH))
      return true;
  }
  return false;
}

Basically, what this function does is checking whether the paid ‘key’ app is installed. If so, it returns true. If not, it returns false. It’s crucial that you do the check in line 18. If you don’t, then anyone can just create a randomly signed package with the same name as your ‘key’ app after which your app would incorrectly think that the user paid for the ‘key’ app.

If you don’t do anything extra, your users will end up with two icons in their app drawer. Obviously, this is undesirable. However, it’s solved easily by removing a section from your AndroidManifest.xml. Whenever the Android system wants to show the app launcher, it broadcasts a specific intent to which activities can respond. Activities that respond will end up in the launcher with an icon. (This is also why some apps can have multiple entry points in a launcher.) Just remove the intent-filter from your manifest with name ‘android.intent.action.MAIN’ and category ‘android.intent.category.LAUNCHER’ to make sure that your activity won’t respond to that intent.

Be sure to read the post at the Android Developers Blog about the new licensing service. For now, this new licensing service will only apply to your paid app(s) and not your free app(s).

That’s all! If I’m wrong about anything (remember my disclaimer at the top? :)), please leave a comment.

13 Comments

  • August 2, 2010 - 00:09 | Permalink

    this post is very usefull thx!

  • November 27, 2010 - 22:06 | Permalink

    nice one, very usefull!

    ObA,
    Israel.

  • Emile
    March 10, 2011 - 17:39 | Permalink

    Nice post.
    However can you still use the (new) Google Licensing scheme which relies on occasionally contacting Google to check the License? Surly only the paid app ‘key’ can do this?
    Cheers

  • Pingback: 무료/유료버전의 앱을 동시에 만들기 – 1 « Dev.Zemna.Net

  • trungt
    February 6, 2012 - 21:20 | Permalink

    This is exactly what I need, thank you so much. But I still have 1 doubt.
    What happen if user backup the ‘key’ app using some backup tools (i.e Titanium), then copy the backed up APK and install on another device?

  • Patty
    April 2, 2012 - 16:44 | Permalink

    > your users will end up with two icons in their app drawer.
    > Obviously, this is undesirable.

    I don’t see that as a problem.
    He installed the free app… so it’s there.
    He installed the paid-app… so it’s there.
    Normal.

    I think it would be MORE confusing if “installed apps started disappearing on there own”…. as well as “I’m seeing apps in my install-list… that I don’t see on my laucher”.

    If a user doesn’t want an app… he can simply delete it himself.

  • Donna
    April 2, 2012 - 16:47 | Permalink

    Does your method require the user to install the “free app first”… then later the “paid app”?

    What happens if he only directly installs the “paid app” alone?

  • Bonnie
    April 2, 2012 - 16:55 | Permalink

    > You have two separate applications with split download counts in your
    > Developer Console, even though they’re in fact the same application.

    You would *NOT* want to see the different counts between your “paid downloads” separately from your “free downloads”?

    Why not? VERY handy info.

  • May 7, 2012 - 07:23 | Permalink

    Thanks for posting this! It’s a really effective way to do the free/paid app split. I’ll definitely look at using this method in some of my future apps.

    I actually tried this code, and modified it slightly to avoid iterating through the list of installed packages. If you’re interested, the updated code is posted at http://makingmoneywithandroid.com/forum/showthread.php?tid=31&pid=1821#pid1821

  • Kristoffer
    May 18, 2012 - 07:49 | Permalink

    Hello.

    Thanks, this was a good way to handle this, but i have a problem when i try to add this to my app.
    When i try to make a if statement with isProInstalled
    if (isProInstalled)
    then eclipse asks me if i would like to change to
    isProInstalled(Context) so if i change it will look like this
    if (isProInstalled(null)) and that will throw me a NullPointerException.

    I guess this is something simple, but maybe you could help me with this?

    • May 23, 2012 - 14:51 | Permalink

      When you write the if statement, you have to pass an actual instance of a Context to the function. So if you’re calling it from your main Activity, this is what you’d write:

      if(isProInstalled(this)) {

      }

      Because Activity is a sub-class of Context.

  • Pingback: 무료/유료버전의 앱을 동시에 만들기 – 1 | ZEMNA BLOG

  • Pingback: Make one IAP valid for different applications | news

  • Leave a Reply

    Your email address will not be published. Required fields are marked *