Sharing code between “full” and “lite” versions of an Android application

When designing an application for Android or any other device, we often come up with the desire to release a commercial version built on top of the free version. This is our take on how one could setup his projects to share common code between a full and a lite version of the same application.

For example, in our own Cover Art Live Wallpaper application, the free application only show 5 albums at a time whereas the full version will show all your music collection at once.

As you can imagine, we have not programmed two separate applications from scratch: bug corrections, new features, database design, … all this had to be factorized in order to minimise the amount of work needed when releasing a new version. There are several options to consider, of which we will detail the one we use at MarvinLabs.

The options you have and their implications

  1. Two completely separate projects: different code, different resources, etc. This is the simplest to setup as your projects are disconnected. You simply need to remember to give the applications a unique package name and that’s it. However, maintainance will be a total nightmare; Bugs might or might not occur on both sides, if there is a database, when the user gets the full version he will find himself with an empty database again (unless you code specifically the database duplication routines which is not trivial and thus might lead to additional bugs), …
  2. One “library” project and two lightweight launchers: the idea here is to factorize all the developments in a common library: classes, activities, layouts, … On top of that, you will make two separate projects which will only contain the minimal code to launch the application either in full or in restricted version. These projects will basically contain an AndroidManifest file, a custom application file or a custom activity file that will be used to set the flag telling whether we are in full version or not. While we have addressed most of the maintainance problems (code is now factorized in a unique library), we user will still loose all his database records when he installs the full version. Indeed, both of the applications have a unique identifier (their package name) and thus unique databases. You will have to manually copy the lite database to the full database, and then uninstall the lite version (or let the user do it). This is still far from perfect.
  3. A single application and an unlocking application: in order to address the database problem, the idea will be to have a single application containing all the code and checking when it is launched if a special application (the unlocking application) is installed. This is what we use at MarvinLabs and what will be detailled below.

The unlocking application solution

First of all, we’ll develop our application containing both the full and the lite versions. In order to restrict functionnalities, we will override the Application class with our own class and add it a method to indicate if we are currently in full or lite mode.

public class MyApplication extends Application {  
  private boolean isFullVersion;  
  // ...  
  public boolean isFullVersion() {  
    return isFullVersion;  
  }  
  // ...  
}

In the rest of the code, the simplest is to always keep at hand the application instance in order to be able to test which version we are running:

public class MyActivity extends Activity {  
  private MyApplication app;  
  // ...  
  @override  
  public void onCreate(Bundle savedInstanceState) {  
    // ...  
    app = (MyApplication) getApplication();  
    // ...  
  }  
  // ...  
  public void onTouchEvent(MotionEvent e) {  
    //...  
    if (app.isFullVersion()) {  
      //...  
    }  
    //...  
  }  
  // ...  
}

Debugging is simple as you will simply need to initialise the isFullVersion member to true or false when you want to test restricted functionnalities. Now, when we are ready, we will try to detect whether the unlocking application is present or not. We will assume that our main application is in the package com.example.application and the unlocking application is in the package com.example.application.unlocker. We will place a few lines of code in the onCreate method of our MyApplication class:

public class MyApplication extends Application {  
  public static final String LITE_PACKAGE = "com.example.application";  
  public static final String FULL_PACKAGE = "com.example.application.unlocker";  
  private boolean isFullVersion;

  @Override  
  public void onCreate() {  
    super.onCreate();  
    // ...  
    // Compute the full version flag and date of expiration  
    final int signatureMatch = getPackageManager().checkSignatures(LITE_PACKAGE, FULL_PACKAGE);  
    setFullVersion(signatureMatch == PackageManager.SIGNATURE_MATCH);  
  }  
  //..  
}

That’s it! This is as simple as that. Each time our application is called, the onCreate method of our MyApplication class is called and we try to see if the unlocker application is installed. Note that both of your applications will need to be signed with the same key when you package them. The unlocker application will be a separate project, containing nothing but a minimal Android manifest file (a single application tag in the main manifest tag).