How to develop two versions of the same application, one being free and the other paying?

16

I'm starting to develop an Android app using Android Studio, and I want to make it available in two versions, one paid, with more features, and one free, more basic.

I thought about starting to develop and after finishing the base version, common between the paid application and the free one, create a copy of the project and start adding new features to the paid application, but I did not find this legal practice because there would be redundant information (classes, images, layouts, etc.) and whenever I find an error I would have to fix both projects if it is in the base version.

I would like to know what to do in these cases and if there is any standard / tool to deal with it.

  

Note: Base version are the commonalities between paid and free version, this includes classes, images, layouts, and everything.

    
asked by anonymous 09.01.2015 / 22:32

1 answer

26

There is a default (not a Design Pattern itself) of Android / Gradle to handle this. The prerequisite would be to use Android Studio and compile your apk using Gradle. For it is Gradle that provides this functionality.

To handle the generation of two different apk's, Gradle lets you define Build / Product Flavors .

Like Build Types (release or debug), Build / Product Flavors are, as the name says, "flavors" ( of features / code, features, settings, and so on) that your application may have. The most common ones are Free , Paid , Phone and Tablet (the latter two being unnecessary depending on the type of customization you will be doing). You can give the name you want for a flavor , it has no limitation.

Correction : Combining a Build Type with a Build Flavor generates a Build Variant . That would be used in fact compilation (this I explain better in the end).

And using Gradle you can generate an apk for each combination of flavor and build type available. Below I'll explain how to do this:

Build Configuration

A simple configuration of your build.gradle file would be:

apply plugin: 'com.android.application'

android {
    compileSdkVersion 21
    buildToolsVersion "21.1.2"

    defaultConfig {
        applicationId "br.com.testegradleflavors"
        minSdkVersion 10
        targetSdkVersion 21
        versionCode 1
        versionName "1.0"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }

    productFlavors {
        free {
            applicationId "be.tamere.testegradleflavors.free"
        }

        paid {
            applicationId "br.com.testegradleflavors"
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:21.0.3'
}

In this example, I define two flavors : free and paid . As Google Play requires, each apk must have a unique ID. Soon each flavor must have a applicationId different because in the end it will generate a different apk.

It is neither necessary nor recommended to change the package structure to equal the applicationId of each flavor . This is because Gradle can handle the merge of the current Build Variant files.

Of course, you can make this configuration more elaborate, like Build variables:

productFlavors {
        free {
            applicationId "be.tamere.testegradleflavors.free"
            buildConfigField "String", "VARIAVEL_ESTATICA", "\"free\""
        }

        paid {
            applicationId "br.com.testegradleflavors"
            buildConfigField "String", "VARIAVEL_ESTATICA", "\"pago\""
        }
    }

There are other settings, such as up to Multi-flavor Variants that would be Flavors in multiple levels, but that goes far beyond the scope of the question.

To access these Build variables in Java code:

String variavel = BuildConfig.VARIAVEL_ESTATICA;

Code / resources configuration

Regarding code, just follow this design structure (following the gradle pattern):

Inthisimage,itillustratesmyprojectthathasthreefoldersatthesamehierarchicallevel:mainwiththe/resourcescodecommontoallpossibleBuildVariantsoftheapplication(defaultinaGradleprojectcurrentlycreated),paidwiththeclasses/resources/filesthatarespecifictopaidapkandfreewithclasses/resources/filesspecifictoapkfree.

WhenyoumodifyBuildVariants(bychoosingadifferentflavor),itvisuallydisassociatesfoldersthatarenotbeingused.IntheimageforexampletheMainActivityappearswitharederrorsymbolinthefreefolder,butthatisbecausethefolderwasdisabledwhenIchoseflavorpaid.Whenyouchangeflavortofree,theinversewilloccur.

Itisgoodnottousethesameresourcename(classes,xmls,filesingeneral)betweenmainandfree/paid.Becausethefeaturesofthespecificpackages(freeandpaid)willoverridethefeaturesofthelessspecific(main).

Whenaflavorischosenforthebuild,itwillconsiderallclassesandresourcesandwillputeverythingtogetherinthesameapk,overwritingwhatithasrepeated(givingflavor).Thefilesthatexistinmainbutdonotexistinflavors,willbeincludedwithoutprobleminapk.

Atthetimeofthebuild,justchooseflavorandtype.Thiscaneitherbebycommandline:

gradlew assembleFreeRelease -- Build para o apk Free em modo Release gradlew assemblePaidRelease -- Build para o apk Pago em modo Release gradlew assembleFreeDebug -- Build para o apk Free em modo Debug -- Demais configurações de build flavor e type

As for Graphical Interface: within Android Studio, just change the Build Variants combobox option as in the image of the project organization.

References for further study:

09.01.2015 / 23:32