What I've come up with:

* I have *three* versions -- a dev version, and two release versions
(free, pro). This helps keep things straight, and helps ensure that
the release ones are built via a common transformation.

* The dev version is what is checked in, and is what I build and test
via Eclipse.

* The free/pro versions are produced via a master (project-
independent) ant script. This script:

** Determines the build number from the highest Subversion version
number in {project, common tools (including this build script), third-
party (including the SDK))

** Tags the release-to-be via an Subversion copy operation

** Checks out/cleans+updates, so I have a clean working copy of my new
tag. (Doing a clean+copy is faster than a new checkout, but it's just
an optimization).

** I export TWO copies of the working copy, one for FREE, one for PRO

** I perform the necessary transformations on each copy (see below)

** I then invoke the project's build script in each of these two
copies

** I then copy the results to my build repository (both debug and
release versions).

The transformations I've found I need are:

* Via an XSLT script, I change the package name in the manifest,
replacing .dev with .free or .pro
* I set android:debuggable="false"
* I set the android:versionCode and android:versionName attributes

* I set up my project so that I have three packages, each containing
classes with the name named in the manifest:
** com.sfsmart.xxx.dev
** com.sfsmart.xxx.free
** com.sfsmart.xxx.pro

These are mostsly trivial subclasses of a common abstract base class
that provides the base functionality, but I put any code that's
specific to another variant in these classes.

Thus, when I switch the package in the manifest from, say,
com.smfsmart.xxx.dev to com.sfsmart.xxx.pro, it switches classes, and
I don't have to do anything complicated to implement the change.

* I also make some changes to the metadata in the manifest -- changing
somme IDs, etc. that I store there. I could also remove things that
aren't used in one version.

* I run another XSLT script over my resource .xml files. This one
simply substititues the package name in the namespaces of certain
elements. I don't know of any case that needs this except references
to attributes defined in a declare-stylable. I don't know any way to
pin those to a specific package.  I thought that aapt's new --custom-
package command-line argument would do it, but it didn't seem to. I
may try that again, though; I may have had a bug in my XSTL script at
the time. But it's only available in the 2.1 version of aapt.

Finally, I have to make one change to my project build script.  (I'm
thinking of injecting this via the master!)  The one major fly in the
ointment, is that R.java changes packages. My ant script changes its
package and moves it back to com.sfsmart.xxx.dev, where it is in the
development builds. This could be avoided if --custom-package can be
made to work; I plan to look into this further.

The result is that the code doesn't have to change at all. The
resources change very slightly, though I'm looking for a solution
there. Each product runs the exact same .java (but not the same code
paths). The differences are all driven by the manifest changes.

I haven't done it yet, but I plan to make the .dev version launch
either the .free or the .pro version, based on a dev-only setting.
This way, I can easily develop both versions of the application.

A final optimization would be to use Proguard to eliminate classes
which aren't used by one version or the other. In my case, the
differences are small.  Similarly, the configure step could remove
assets or resources which aren't used in one or the other version.
Unlike using Proguard on classes, this wouldn't be based on direct
dependency analysis (though you COULD do it!), so should be approached
with a bit of caution. If you have a lot of graphic assets, though, it
may be worth doing.

Here's a sanitized version of the script I use to adjust the manifest:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
version="1.0"
    xmlns:android='http://schemas.android.com/apk/res/android'
    >
    <xd:doc xmlns:xd="http://www.oxygenxml.com/ns/doc/xsl";
scope="stylesheet">
        <xd:desc>
            <xd:p><xd:b>Created on:</xd:b> Feb 24, 2010</xd:p>
            <xd:p><xd:b>Author:</xd:b> Bob Kerns</xd:p>
            <xd:p></xd:p>
        </xd:desc>
    </xd:doc>
    <xsl:param name="VERSION"/>
    <xsl:param name="VERSION_EXTRA" select="''"/>
    <xsl:param name="BUILDTYPE" select="'PRO'"/>

    <xsl:output indent="no"/>
    <xsl:template match="/">
        <xsl:text>
</xsl:text>
        <xsl:apply-templates select="@*|node()"/>
    </xsl:template>


    <!-- Set the version code to be the supplied version number
(derived from the Subversion revision number). -->
    <xsl:template match="/manifest/@android:versionCode">
        <xsl:attribute name="android:versionCode"><xsl:value-of
select="$VERSION"/></xsl:attribute>
    </xsl:template>

    <!-- Generate the android:versionName attribute, using the
existing value as a prototype but filling
         in the build number -->
    <xsl:template match="/manifest/@android:versionName">
        <xsl:variable name="major"><xsl:value-of select="substring-
before(., '.')"/></xsl:variable>
        <xsl:variable name="minorRest"><xsl:value-of select="substring-
after(., concat($major, '.'))"/></xsl:variable>
        <xsl:variable name="minor"><xsl:value-of select="substring-
before($minorRest, '.')"/></xsl:variable>
        <!-- $extra picks up the value of $VERSION_EXTRA, with a prepended
space if that's not empty. -->
        <xsl:variable name="extra">
            <xsl:if test="$VERSION_EXTRA">
               <xsl:text> </xsl:text>
            </xsl:if>
            <xsl:value-of select="$VERSION_EXTRA"/>
        </xsl:variable>
        <!-- The final value for android:versionName -->
        <xsl:variable name="versionName">
           <xsl:value-of select="concat($major, '.', $minor, '.',
$VERSION, $extra)"/>
        </xsl:variable>
        <xsl:attribute name="android:versionName"><xsl:value-of
select="$versionName"/></xsl:attribute>
    </xsl:template>

    <!-- Switch the package name -->
    <xsl:template match="/manifest/@package">
        <xsl:attribute name="package">
            <xsl:value-of select="substring(., 1, string-length(.)-3)"/
>
            <xsl:choose>
                <xsl:when test="$BUILDTYPE='PRO'">pro</xsl:when>
                <xsl:when test="$BUILDTYPE='FREE'">free</xsl:when>
                <xsl:otherwise><xsl:message>Incorrect build type:
<xsl:value-of select="$BUILDTYPE"/></xsl:message><xsl:value-of
select="$BUILDTYPE"/></xsl:otherwise>
            </xsl:choose>

        </xsl:attribute>
    </xsl:template>

   <!-- Always turn off android:debuggable in the product builds -->
    <xsl:template match="/manifest/application/@android:debuggable">
        <xsl:attribute name="android:debuggable">false</xsl:attribute>
    </xsl:template>

    <!-- Get rid of the ADMOB_PUBLISH_ID if we're not the FREE version
-->
    <xsl:template match="/manifest/application/meta-
da...@android:name='ADMOB_PUBLISHER_ID']">
        <xsl:if test="$BUILDTYPE = 'FREE'">
            <xsl:copy>
                <xsl:apply-templates select="@*|node()"/>
            </xsl:copy>
        </xsl:if>
    </xsl:template>

    <!-- Add an additional meta-data element if we're the PRO version
-->
    <xsl:template match="/manifest/application">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
            <xsl:if test="$BUILDTYPE = 'PRO'">
                <meta-data android:value="true" android:name="paid"/>
            </xsl:if>
        </xsl:copy>
    </xsl:template>

    <!-- Identity transform - copy everything else verbatim! -->
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

(BTW, I recommend the <Oxygen/> XML for developing and testing stuff
like this; it makes it very easy to do).

On Feb 26, 9:39 am, Moto <medicalsou...@gmail.com> wrote:
> I would like to make my life easier by having one common code base for
> my service for both paid and free apps.  I want to eliminate the
> complex merging I find my self doing every time I need to release!
>
> As of now I had to change the service file name along with the Remote
> service aidl calls.
>
> Please any advice would be helpful!
>
> -Moto!

-- 
You received this message because you are subscribed to the Google
Groups "Android Developers" group.
To post to this group, send email to android-developers@googlegroups.com
To unsubscribe from this group, send email to
android-developers+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/android-developers?hl=en

Reply via email to