54

Let's say for example that I have some Android app that does X. The free version has ads or basic features. I want to have a paid version that removes the ads and adds extra features.

How can I use the paid app as a "license key" to unlock the features in the free app?

So the user would install the free app, then install the paid app to get the extra features, but they would still run the free app (which would now be unlocked). What's the best approach to doing this?

tshepang
  • 12,111
  • 21
  • 91
  • 136
Bryan Denny
  • 27,363
  • 32
  • 109
  • 125
  • 1
    Why do this? If the free version is going to have the same features as the paid one when unlocked, then you might as well just use one app because otherwise you'd have two apps that do exactly the same thing. – JAB Jun 17 '10 at 17:53
  • 3
    @JAB The paid app isn't actually an app, but just a key! The free app checks the presence of the paid "app", and if it exists, then it would unlock the free app. This prevents code duplication (keeping a "free version" trunk and a "paid version" branch up to date [note: package names have to be different, you can't simply upload the same code twice with one line changed, you have to change the package name *everywhere*) and no user data/settings are lost when "upgrading" to the paid version. – Bryan Denny Jun 17 '10 at 18:09
  • 9
    That means a user would need to install both the free and paid app, which is pretty annoying for someone who knows they just want to buy the app. I would really recommend against doing this. – hackbod Jun 17 '10 at 19:42
  • 1
    @hackbod however, if they were trying the "free"/demo app they wouldn't have uninstall it and then re-install the paid version. They'd simple buy the paid app/license and be done with it. But good point, thanks. – Bryan Denny Jun 17 '10 at 20:23
  • You can't assume they started with the free version. Since we have the 24 hour return period, people should often feel free to just buy the full version if they want to try it out before deciding they link it. – hackbod Jun 18 '10 at 19:54
  • (Plus hopefully people will grow to like your software, giving you an increasing number of people who trust you enough to automatically buy something new from you without trying it out.) – hackbod Jun 18 '10 at 19:56

9 Answers9

37

Use PackageManager to ensure your paid package is installed. AND ensure your free package signature matches installed premium package signature. Otherwise somebody would be able to install unsigned app with package name matching your paid package name and unlock premium this way.

This post can help you to find your signature Detect if app was downloaded from Android Market

Community
  • 1
  • 1
Fedor
  • 43,261
  • 10
  • 79
  • 89
  • 1
    The signature is the most important thing. This will make it impossible to fake the key without the keystore you are signing the app with. – Janusz Jun 18 '10 at 14:02
  • I've implemented this, but consider this, what if the user doesn't pay for the license APK? All they have to do is find a pirated copy and install it, there's no licensing going on here aftrerall.. How do you get around this? – AutoM8R Jan 05 '13 at 07:16
  • 1
    There is a method: **PackageManager.getInstallerPackageName(String packageName)** (http://developer.android.com/reference/android/content/pm/PackageManager.html#getInstallerPackageName%28java.lang.String%29). With this method you can check which application installed given packageName. Google Play package name is **com.android.vending** – franta kocourek Apr 22 '14 at 15:15
21

I'm using this:

PackageManager manager = getPackageManager();
if (manager.checkSignatures("core.package.name", "key.package.name")
    == PackageManager.SIGNATURE_MATCH) {
    //full version
}

It's pretty simple and it works.

David Vávra
  • 18,446
  • 7
  • 48
  • 56
  • What does this do if the key package is not installed? Does it return a value saying something along those lines, or does it throw an exception? – eidylon Jan 25 '12 at 21:35
  • When key package is not installed, signatures doesn't match. – David Vávra Jan 26 '12 at 15:18
  • Cool... was wondering if it needed to be wrapped in a `try...catch` block to catch that instance or not. Sounds like not. Thanks! – eidylon Jan 26 '12 at 16:28
8

Here's a simple function which checks for the presence of the Pro Key, and checks that the package signature matches the free version:

protected static boolean isProInstalled(Context context) {
    PackageManager manager = context.getPackageManager();
    if (manager.checkSignatures(context.getPackageName(), "com.your.pro.key")
        == PackageManager.SIGNATURE_MATCH) {
        //Pro key installed, and signatures match
        return true;
    }
    return false;
}

Code is from this forum post, based on the method outlined at yoki.org.

David
  • 1,698
  • 16
  • 27
7

As someone else pointed out, yes, you can use PackageManager to detect the presence of the paid 'key' app, but that's problematic if someone only installs the paid version, uninstalls the free version, etc. Users might get annoyed at having to keep around two downloads to make your one app work. FWIW, I think DoubleTwist Air Sync does it this way. I'm pretty sure the Air Sync app doesn't do anything except enable the functionality in the free DoubleTwist app.

A more practical route might be to have two separate apps, then give the ability to import prefs and data from one to the other using a ContentProvider and/or sharedUserId. You can then share most of your code using a shared library project. However this means both apps need to use a different content URI since two apps can't use the same authority, which is sort of a pain, because your shared library code can't just have a static CONTENT_URI or AUTHORITY field like you'd normally find in a ContentProvider implementation.

I'm starting to think two separate apps with two more-or-less separate codebases is the way to go, because copying code between two projects might actually be easier than trying to maintain a shared library with all sorts of switches to enable or disable features between a free and paid version.

Actually, edgman's recommendation for using a single app and using licensing is probably the best way to go about managing a free and paid version of an app. It solves all of the problems listed above, although to be fair I haven't used licensing yet myself.

EDIT licensing appears to only be allowed for paid apps (bummer) so it's a no-go if you want to offer a free version. However in-app billing might be the "right" way for you to manage a free/paid version. Maybe it's Ok for the OP but I don't feel like requiring two apps to always be installed on a user's device is ideal. If a paying user installs the app on a new device, it appears to be possible to download prior transactions so they don't have to pay twice.

Community
  • 1
  • 1
thom_nic
  • 7,809
  • 6
  • 42
  • 43
  • This answer is very old but as time has gone by, in app billing to unlock pro features in a free app is now *definitely* the right way to solve this problem. Multiple versions of your app (a paid one and a free one) just cause a split in your download metrics and headache for users who would like to convert to the paid version. **Offer a pro-version unlock in your app!** – thom_nic Jun 26 '17 at 16:20
  • That would be nice, except for the fact (please correct me if wrong!) that it is impossible to upload a free app that uses LVL. (I'd love to do this otherwise.) SO, no Google Play licensing verification...? – Photovore Aug 28 '17 at 00:18
3

Provided both apps are from the same developer and signed by the same key, they should be able to share information privately. You could probably use a file (stored with MODE_PRIVATE) , but I think the easiest route is to use SharedPreferences - set a flag in the paid app which will be read by the free one. See http://developer.android.com/guide/topics/data/data-storage.html. No idea if it would be easy to circumvent, especially on rooted devices...

Another way would be to check if the paid app is installed, for example by checking if it accept a specific Intent. See also: http://developer.android.com/resources/articles/can-i-use-this-intent.html; in that example they check if ZXing's barcode scanner is available that way.

In any case, another twist on the idea would be that you could "enable" multiple apps with only one paying, if you so wished. Your paid app would be a simple "support this developer" which would remove ads from all your apps. That's an interesting paying model IMHO.

Bryan Denny
  • 27,363
  • 32
  • 109
  • 125
Joubarc
  • 1,206
  • 10
  • 17
  • I think storing a flag in any resource file is not a good idea; people will be able to access it on rooted devices, so it would be only a matter of time before the app was decompiled, flag is changed, and app is distributed in it's modified form on the internet...there goes your money – slinden77 Dec 22 '12 at 09:06
  • Well, I **did** say I had no idea if it would be easy to circumvent, especially on rooted devices..., but yeah, probably best to assume it's actually very easy to do. – Joubarc Dec 22 '12 at 19:50
  • I missed that part :) But yes, it's very easy, even people with no programming skills would be able to do it – slinden77 Dec 22 '12 at 21:22
1

What absolut distributing only a free/demo app and implement in-app to make it pro? Therefore there will be only one app user have to install, they can test basic functions and there will be a button like "upgrade to pro $1,99" that will call in-app purchase.

almisoft
  • 2,153
  • 2
  • 25
  • 33
1

Here is one example of how it can be done:

Intent unlockerAppPresence = null;
APP_LITE_VERSION = false;
try 
{
    unlockerAppPresence = context.getPackageManager().getLaunchIntentForPackage("nameofthepackagethatunlockyoursoftware");
} 
catch (Exception e1) 
{
    APP_LITE_VERSION  = true;
}
if (unlockerAppPresence == null)
    APP_LITE_VERSION  = true;

Combine this with checking of the app's developer signature (as noted by @Fedor) and you should be good to go.

Bryan Denny
  • 27,363
  • 32
  • 109
  • 125
0

Apparently I cannot comment without 50 reputation, so I will put this in its own answer.

The PackageManager method cited by others seems like a nice and simple way to go, but as hackbod mentions, having two installed applications is annoying (and a bit confusing) for the user.

However - and I haven't tried this, because I haven't published my app yet - it seems like you could keep a variable that starts as false and then updates to true if it finds the Pro version installed. The variable would not revert to false just because the Pro version is not there. Then, you could let the user know in both versions that they need to install Pro, then open up the Trial and click Unlock. Once this is done, the Trial version would become a Full version and inform you (if it found the Pro version installed) that you can now uninstall the Pro version and you will continue to have full access.

Something a bit like this:

String msg = "";
boolean sigMatch = isProInstalled(context);
if (unlocked)
{
    // If you get here by clicking a button that goes away once the app is unlocked, then you may never see this.  Still, better safe than sorry.
    msg += "Thanks!  You already have access to the full game.";
}
else
{
    if (sigMatch)
    {
        unlocked = true;
        saveData(); // I assume you already know how to store variables.
        msg += "Unlock successful.  You now have access to the full game."
    }
    else
    {
        msg += "You are using a Trial version of this game.  (blah, blah).  To unlock the full version, please purchase XYZ Pro.  Install the application and then start this application again and go into this screen again.  You should get a message letting you know that the app has been successfully unlocked, after which you may uninstall the Pro version.  You do not have to keep it on your device after unlocking the game.";
    }
}
if (sigMatch)
{
    msg += "  If you like, you may now uninstall the Pro application.  You will continue to have full access to XYZ.";
}

Now, this cannot tell you whether the user paid for the Pro version and then returned it within 24 hours, as hackbod also mentioned is possible.** But it seems like this scenario might not happen very often. If someone paid and then returned it (especially if you are not charging very much), then they probably decided to stop using the app... or they are trying to steal it, in which case there are other ways to do it anyway. If this possibility concerns you, then In-App Billing may be your best choice. But if you are only looking for a simple measure by which to keep out the casual user, and you don't want to force them to keep two applications installed for all time, then this might be an option.

** I suppose you could keep a timestamp with another variable, and require the user to keep the Pro version installed until some number of hours after that timestamp, THEN allow them to uninstall...

-1

The route most app developers take on this is to only have one version of the app, but features are enabled (extras) or disabled (ads) based on the presence of the "license key".

edgman
  • 175
  • 3