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.
15 Comments
this post is very usefull thx!
nice one, very usefull!
ObA,
Israel.
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
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?
> 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.
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?
> 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.
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
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?
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
Over the summer he headed to Rio with his national team and picked up a silver medal after losing the final to hosts Brazil.
His deal length means he could end up playing at Liverpool for as long as he was at City for.