67

I already have a free app in the Android Market, but I want to add a paid-for version with better features. I can't upload the same up with some changed constants to unlock those features as the Market tells me I already have an app with that package name in the market.

What's the cleanest way of doing this?

DaBeeeenster
  • 1,481
  • 3
  • 16
  • 20

9 Answers9

52

The Android SDK formally addresses the issue of a shared or common codebase with something called a library project.

http://developer.android.com/tools/projects/index.html

Basically, the shared code is defined as a library project, then a paid and a free version are simply two different projects in your eclipse workbench, both referencing aforementioned library project.

At build-time, the library project gets merged with either of your releases, which is what you want.

The Android SDK example source code contains a project called TicTacToe that will help you get started with library projects usage.

Good luck.

Daniel

Daniel Szmulewicz
  • 3,971
  • 2
  • 25
  • 26
  • 4
    I've used this solution for both paid and free versions of my applications, problem is I've lost the capability to debug the source like I used to. I've linked the library to the source and I can see the source files in Debug mode, but I can't see the variables values, any workaround? – rfsbraz Oct 22 '11 at 15:02
  • if the free version saves some data, can the pro version read this data? - using this technique? (I mean without any special file permissions or sd card permissions) – ycomp Feb 23 '12 at 01:11
  • 2
    A library project allows you to share code, not data. Sharing data between two apps is another topic altogether. Implementing content providers could be a solution (among others) for your needs. – Daniel Szmulewicz Mar 03 '12 at 03:34
  • Link no longer works. – onaclov2000 Jun 02 '13 at 12:42
  • Thanks. Should be fixed. – Daniel Szmulewicz Jun 02 '13 at 13:43
35

Several approaches exist, but you usually don't see the drawbacks until you try them for yourself. Here are my experiences:

  1. Unlocker app. This is very easy to implement, create a new app that acts as a licence: your original app should check if the unlocker app's signature matches that of your app (if yes the unlocker is available on the device, otherwise no); your unlocker should prompt for downloading your free app if not installed, otherwise start it and remove its launcher icon.

    Pros: easy to implement and there is only one codebase to maintain.
    Cons: it is said that users are sometimes confused by this model, they simply don't understand why they have two launcher icons, or what have they just downloaded. Removing a launcher icon is cumbersome, changes are only visible if the device is rebooted. Also, it seems you will not be able to use Google's licensing API (LVL), because your free app cannot make licensing requests on behalf of your paid unlocker app. Any workaround for this latter leads to bad user experience.

  2. In-app purchase. This is easy to implement if you have IAP in your code anyway, otherwise it will take quite some time to get things right.

    Pros: there is only one codebase to maintain and purchasing flow for the user is quite convenient.
    Cons: users are said to be concerned about whether their purchase is 'persisent', meaning they are confused whether they could still use the pro features if they later installed the app to another device or reinstalled it.

  3. Free and paid versions with shared library project. This should not be a hard thing to code and seems like a logical decision to have a single codebase containing most of your app logic and maintain only the differences.

    Pros: users are less confused with this model, but they are probably concerned about whether their preferences will be lost if they start using the paid version.
    Cons: Java/Eclipse in my experience is not really friendly when it comes to libraries. It will take some time to set up the projects correctly, but it will still be confusing how the whole thing works, what to put in which manifest, what happens with resources, etc. You will face build issues with misconfigured projects, build issues with resources not being found (library project will not 'see' already referenced resources, so you'll have to edit the references one-by-one). Moreover asset files are not supported in this model meaning you will have to do some tricks with symlinking, copying or other magic that will just add to the terrible mess your "single codebase" project has already become. And when your project finally builds you just have to keep fingers crossed for the whole thing to work as expected, be prepared for missing images and hidden errors to look for. And of course you will need to provide your users a convenient way to migrate their preferences into the paid version upon first launch.

  4. Free and paid versions with separate codebases and source control. At first glance this also seems a good idea since a decent source control system could lift weight off your shoulders but...

    Pros: same as 3.
    Cons: you will be maintaning two different codebases and nothing else but a merging/branching hell that is to be expected here. App package names should be different, so you'll probably need to differentiate every single file you have, to keep things clean. And of course you will need to provide your users a convenient way to migrate their preferences into the paid version upon first launch.

  5. Free and paid versions with a script that derives one from the other. It sounds like a dirty hack to accomplish, but you know for sure that it works.

    Pros: same as 3 plus you only maintain a single codebase.
    Cons: creating the scripts takes a little time (make sure you rename folders, replace package, application and project names) and you'll have to be careful to update your scripts from time-to-time if necessary (this won't happen if there aren't too many differences between the free and the paid versions). And of course you will need to provide your users a convenient way to migrate their preferences into the paid version upon first launch.

No solution is perfect, but the above can hopefully point you in the right direction if you are just about to start implementing one of the possibilities.

Levente Dobson
  • 773
  • 8
  • 10
  • Can you point me to some info on method 1? Do I just check for package name of the unlocker app or is there a better way? Thanks – Shmuel May 19 '14 at 19:16
  • No it's not enough to check for package name, because an attacker could easily create and install a fake unlocker app for your app. You need to sign both your app and your unlocker app with the same key and then in your app check if the signatures match. Something like this: String mainAppPkg = context.getPackageName(); String keyPkg = "com.example.your.unlocker.key.package.name"; int sigMatch = context.getPackageManager().checkSignatures(mainAppPkg, keyPkg); boolean isInPaidMode = sigMatch == PackageManager.SIGNATURE_MATCH; – Levente Dobson May 20 '14 at 07:43
  • ah, cool. so implemented that and im about to release an unlock-key version of my app to the play store. the sole perpose of the key is so the free version can find it and remove ads. is this a violation of googles terms? does a paid app have to do someting, or can it just be a donate/remove ads for the free app. thanks – Shmuel May 20 '14 at 22:46
  • Lot of apps use this model so I very much doubt that this poses any sort of problem against the terms. In my opinion it should not be an issue. The only problems that arise with this model that I know of are what I have described in my post: users are confused, no trivial LVL support, hiding icons is dirty and painful. – Levente Dobson May 21 '14 at 05:53
  • For options 3, 4 and 5, how do you get the user to buy the paid version? Do you have a "Buy" button that links directly to the paid version in the app store? I saw a SO answer where this was suggested, and it got 2 downvotes, but no explanation. – user1725145 Mar 13 '16 at 20:27
  • I'm not an expert in this, but there are several options. 1: include this info in the app store description of your free app, 2: show a popup after the user has started the app 15 times, 3: display a 'buy' button, but probably not on the main screen of the app, only in a 'help' or 'about' section, 4: display this hint at features that are only accessible for premium users, 5: if the free app shows ads simply make your app great and your users will search for the paid version on the app store. – Levente Dobson Mar 18 '16 at 21:14
34

With Gradle build system, you now can have different product flavors, each having its own package name. Following is an example gradle script having free and pro flavors of the same app.

apply plugin: 'com.android.application'

android {
    compileSdkVersion 19
    buildToolsVersion "19.1"

    defaultConfig {
        applicationId "com.example.my.app"
    }

    productFlavors {
        free {
            applicationId "com.example.my.app"
            minSdkVersion 15
            targetSdkVersion 23
            versionCode 12
            versionName '12'
        }
        pro {
            applicationId "com.example.my.app.pro"
            minSdkVersion 15
            targetSdkVersion 23
            versionCode 4
            versionName '4'
        }
    }

R class will still be generated in the package name specified in the AndroidManifest.xml so you don't need to change a single line of code when switching flavors.

You can switch the flavor from Build Variants pane which is accessible from left bottom corner of Android Studio. Also, when you want to generate a signed APK, android studio will ask you the flavor you want to build the APK.

Also, you can have different resources for each flavor. For an example, you can create a directory pro in your src directory. The directory structure should be similar to the main directory. (Eg: if you want to have a different launcher icon for pro version, you can place it in src\pro\res\drawable. This will replace the free icon located in src\main\res\drawable, when you have switched to pro flavor).

If you create a strings.xml in pro resource directory described above, the main strings.xml and pro strings.xml will get merged to get a final strings.xml when building in pro flavor. If a certain string key exists in both free and pro xml, the string value will be taken from pro xml.

If you need to check whether current version is pro or free in code, you can use a method like following.

public boolean isPro() {
    return context.getPackageName().equals("com.example.my.app.pro");
}

For more information, Refer this

Lahiru Chandima
  • 22,324
  • 22
  • 103
  • 179
  • 3
    This is by far the best solution, provided that one uses gradle build. – hgoebl Nov 12 '15 at 08:17
  • I ll go with this for start! Looks more elegant and no need for shared stuff – Jimmy Kane May 24 '16 at 13:17
  • Is this needed also if we have a seperate wear module or only the mobile module needs to have this setup ? – Jimmy Kane May 25 '16 at 14:36
  • @JimmyKane I haven't developed any wear modules but it seems that you can have flavors in both handheld and wear modules. So, if you have free and paid versions of the wear module too, you should use flavors in wear module too. Check out this answer. http://stackoverflow.com/a/27405731/1015678 – Lahiru Chandima May 25 '16 at 15:55
  • @LahiruChandima I figured it out. Actually you need both in order for the package name to change. The rest you can omit if codebase is the same (versions etc) – Jimmy Kane May 25 '16 at 17:43
  • In the end it needed extra work. Damn. At least I solved it. Check my answer and thanks again for the info! Cheers – Jimmy Kane May 26 '16 at 13:40
  • This works fine, thx. But If you get an error like "Error:All flavors must now belong to a named flavor dimension. The flavor 'flavor_name' is not assigned to a flavor dimension." You need to add flavorDimensions "version" just before "productFlavors {" check this https://developer.android.com/studio/build/build-variants – mili Jun 04 '21 at 00:54
15

One source code two apps (Eclipse)

There is a recurring problem of managing an app with two forms of presentation in the market, maybe with only one bit difference between the two ( paidFor = true; ) Maybe the icon is different too.

This approach uses the description of Package names explained by Mihai at http://blog.javia.org/android-package-name/ which underlines the distinction between the package name used to manage the source code and the package name used to publish the apk on the Android Market. In this example the two published package names are com.acme.superprogram and com.acme.superprogrampaid, the Java source code package name is com.acme.superprogram.

In the manifest the Activities are listed and named as .ActivityName. Mike Wallace pointed out in his recent presentation that the preceding dot is important and it can be replaced by a fully qualified package. For example “com.acme.superprogram.DialogManager” can replace “.DialogManager” in the manifest.xml text.

Step 1 is to replace all the activity android:name entries with these fully qualified package names, using the java source code management package name (com.acme.superprogram).

Then the Manifest Package name can be changed...

In Eclipse this forces a recompile and a new R.java is created in the gen folder. Here is where it gets a bit tricky; there are two folders com.acme.superprogram and com.acme.superprogrampaid, only one has a R.java. Simply copy the R.java into the other folder so that the program can resolve the R.layout.xyz items.

When you change the package in the

I have tried it on a couple of apps. I have both running together on the emulator, my 2.1 phone and they are both on the Android Market.

user462990
  • 5,472
  • 3
  • 33
  • 35
  • 2
    Isn't having only one bit of difference between the two packages ( a paidFor flag) dangerous? You are basically giving away the full code in the free version, with a single flag at the beginning of the code which would be very easy to identify and change even if the code was obfuscated? – CraPo Apr 10 '12 at 08:45
  • Great answer, but sadly the gen directory gets wiped when I try and export - suggested way round this as an alternative answer. – Neil Townsend Oct 28 '12 at 18:11
2

Just in case you have also a wear module there needs extra work to be done.

Here are some example Gradle files for packaging a wear module with flavours and buildtypes.

Module mobile build.gradle

apply plugin: 'com.android.application'

android {
    compileSdkVersion 23
    buildToolsVersion "23.0.3"

    defaultConfig {
        applicationId "com.example.app"
        minSdkVersion 15
        targetSdkVersion 23
        versionCode 85
        versionName "2.5.2"
    }
    buildTypes {
        debug {
            applicationIdSuffix ".debug"
            embedMicroApp = true
            minifyEnabled false
        }
        release {
            embedMicroApp = true
            shrinkResources true
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            zipAlignEnabled true
        }
    }
    productFlavors {
        free{
            applicationId "com.example.app"
        }
        pro{
            applicationId "com.example.app.pro"
        }
    }
}

configurations {
    freeDebugWearApp
    proDebugWearApp
    freeReleaseWearApp
    proReleaseWearApp
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:23.4.0'

    freeDebugWearApp project(path: ':wear', configuration: 'freeDebug')
    proDebugWearApp project(path: ':wear', configuration: 'proDebug')

    freeReleaseWearApp project(path: ':wear', configuration: 'freeRelease')
    proReleaseWearApp project(path: ':wear', configuration: 'proRelease')
}

Module Wear build.gradle

apply plugin: 'com.android.application'

android {
    compileSdkVersion 23
    buildToolsVersion "23.0.3"
    publishNonDefault true

    defaultConfig {
        applicationId "com.example.app"
        minSdkVersion 20
        targetSdkVersion 23
        versionCode 85
        versionName "2.5.2"
    }
    buildTypes {
        debug {
            applicationIdSuffix ".debug"
            minifyEnabled false
        }
        release {
            shrinkResources true
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            zipAlignEnabled true
        }
    }
    productFlavors {
        free {
            applicationId "com.example.app"
        }
        pro {
            applicationId "com.example.app.pro"
        }
    }
}

dependencies {

    ...

}
Jimmy Kane
  • 16,223
  • 11
  • 86
  • 117
1

Changing the package name will help.
Just change the package name of your paid app using eclipse and put it in market.

That will solve your problem.

Metalhead1247
  • 1,978
  • 1
  • 17
  • 28
1

I had the same problem, what I settled on is making two android apps, one called my_epic_app_free, and the other my_epic_app_paid. What I would do is only make changes to the paid version, then I have a separate java console program, all it does is access all the .java files directly from disk, copy them into memory, fiddle with the package names and manifest lines, then paste them directly into the free app. I have this on a button push on the desktop so when I'm done developing on the paid version, I press the button, then compile the free version. I have both the paid app and free app communicate to eachother, the user can even install both of them, and the free version sees the paid, and unlocks to the paid version.

Another idea is to make the paid app just a bare bone program which contains a keyfile. The paid app, if run, will automatically run the free version, and the free version will behave as the paid version.

The free app checks the presence of the paid app's key file, and if it exists, then it would unlock the free app. This prevents code duplication. The package names still have to be different but ideally the paid app would not need to be changed often.

Benefit: No user data/settings are lost when "upgrading" to the paid version.

Drawback: If the user installs the paid version first, they will have to be directed to install the free version which is a hassle, why do they have to install two apps for the paid version?

Eric Leschinski
  • 146,994
  • 96
  • 417
  • 335
0

If you don't want to use library projects, and are happy with the risks that a couple of people have mentionned in comments, then @user426990 has provided an excellent answer, in theory. However, eclipse seems to wipe the entire contents of the gen directory when doing a fresh build for export (which I find it hard to argue with as a general principle).

An alternative solution, based on the same principle, is as follows, assuming that you have written com.acme.superprogram and you wish to create com.acme.superprogrampaid

  1. Ensure that your manifest points to the activities, services and so on by full name. As per @user426990's answer ".CoolActivity" must be listed as com.acme.superprogram.CoolActivity

  2. Create a new class MyR in your code (com.activity.superprogram, with the rest of the code) as follows:

    package com.acme.superprogram;
    import com.acme.superprogram.R;
    
    public final class MyR {
        public final static R.attr     attr     = new R.attr();
        public final static R.color    color    = new R.color();
        public final static R.dimen    dimen    = new R.dimen();
        public final static R.layout   layout   = new R.layout();
        public final static R.id       id       = new R.id();
        public final static R.string   string   = new R.string();
        public final static R.drawable drawable = new R.drawable();
        public final static R.raw      raw      = new R.raw();
        public final static R.style    style    = new R.style();
        public final static R.xml      xml      = new R.xml();
    }
    

    You will need to vary the exact content to reflect the resources you use! For example, you may not need the xml line and may need another one. Look at the real R.java file in gen/com/acme/superprogram and you will need one line per class. You may need to subclass.

  3. Now (a) remove all "import com.acme.superprogram.R" lines from your code and (b) replace all "R." references to "MyR.". This way all your references to R are indirected via one place. the downside is that they all get warnings about not being static enough. You have three options with these warnings: you can suppress them, you can ignore them, or you can make a more complete version of MyR, with a line for each entry in R which you use:

    package com.acme.superprogram;
    import com.acme.superprogram.R;
    
    public final class MyR {
        final static class attr {
            final static int XXXX = R.attr.XXXX
    // And so on ...
    
  4. As per @user426990, you can now change the package in the manifest from "com.acme.superprogram" to "com.acme.superprogrampaid"; you probably also want to change the name, launch icon and the key variables at this point.

  5. Change the import line in MyR to import com.acme.superprogrampaid.R

And away you go.

ps. Remember to change the file name in the final export dialog box ...

Neil Townsend
  • 6,024
  • 5
  • 35
  • 52
-6

Why not make two different Eclipse projects? It is not so difficult to fix the few things referring to the package name by hand. Take care to change the import statements and the manifest file. You need to code twice anyway. Of course, it is a nuisance to fix code in both places.

The proper solution would be in the market. It should allow two applications with the same package names, and ask to replace the one when the other is to be installed. Upon upload, it should check if the package name is really from the same software vendor.

Rene

Rene
  • 3,746
  • 9
  • 29
  • 33
  • 12
    This is a poor solution. Maintaining two code bases is far from ideal and there are more elegant solutions. – Jason Robinson Jan 27 '12 at 20:11
  • +1 It is not ideal, but perfectly reasonnable for anyone who works with an appropriate source control management – rds Aug 27 '12 at 09:15
  • 4
    no one "who works with an appropriate source control management" would pick this solution. – Rodrigo Dias Jan 10 '13 at 16:51
  • I don't see why this would require two codebases. One codebase could have both main files, and the pro main file could just extend the main file. So there would have to be two different ways to build, but both would use the same code base. – MiguelMunoz Aug 13 '16 at 09:44