I'm looking to optimize generating of slightly different APKs of the same Android app, the only difference being the http API server it's using (dev/staging/prod).
Ideally, I'd just want my Eclipse to build 2 APKs, one with the prod server and one with the dev one.
I'm even OK with having 2 Run configurations, but I haven't been able to figure out how to pass parameters to the app and read them from the code.
I want to target 1.5, BTW, and I'd like to use Eclipse auto-build tools, so I'm looking for the most generic solution.
Thank you.
I think using ant build script would be the easiest solution. Eclipse supports ant build, so you can run ant command in eclipse.
You can solve your problem with ant like this.
prepare two xml android resource file.
build a package with resource #1
overwrite resource #1 with content of resource #2
build another package
xml would be like this:
resource #1:
<resources>
<string name="target">dev</string>
</resources>
resource #2:
<resources>
<string name="target">staging</string>
</resources>
and ant script would be like this:
<project>
<target name="build_all">
<copy file="res1.xml" to="res/values/target.xml"/>
<ant antfile="build.xml" target="debug"/>
<copy file="res2.xml" to="res/values/target.xml"/>
<ant antfile="build.xml" target="debug"/>
</target>
</project>
Move all you code to a library project see
http://developer.android.com/guide/developing/projects/projects-eclipse.html#SettingUpLibraryProject
Then create separate projects in eclipse for test and production each with a unique package name. You can then use the package name to distinguish between versions.
Something like:
public static boolean isProductionVersion(){
return context.getPackageName().toLowerCase().contains("production");
}
This may seem like overkill for managing different http end points but it will make the code more manageable. You can also do useful things like:
flag the test version with a different application icon
run test and production versions side by side on one device
This can all be done in eclipse without using and third party tools.
Its not really what you want:
private static Boolean isSignedWithDebugKey = null;
protected boolean signedWithDebug() {
if(isSignedWithDebugKey == null) {
PackageManager pm = getPackageManager();
try {
PackageInfo pi = pm.getPackageInfo(getPackageName(), 0);
isSignedWithDebugKey = (pi.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
}
catch(NameNotFoundException nnfe) {
nnfe.printStackTrace();
isSignedWithDebugKey = false;
}
}
return isSignedWithDebugKey;
}
You could then hit a dev/staging server if the app is signed with a debug key, and production with a release certificate.
For passing parameters, you could always create a file in android's directory system and have your code read it from it.
In my case I just wanted to change a few values in strings.xml between different versions.
First I have to load the ant-contrib library, to define the for loop task:
<taskdef resource="net/sf/antcontrib/antcontrib.properties">
<classpath>
<pathelement location="lib/ant-contrib-1.0b5-SNAPSHOT.jar" />
</classpath>
</taskdef>
I put my list of configurations, config.names, in a properties file:
config.url.root=http://projectserver.aptivate.org/
config.names=student-production, teacher-production, student-testing, teacher-testing
And define a build-all target, that loops over the config.names:
<target name="build-all">
<for param="config.name" trim="true" list="${config.names}">
<sequential>
Defining a custom resources directory for each one, saving the directory name in the config.resources property:
<var name="config.resources" unset="true" />
<property name="config.resources" value="bin/res-generated/#{config.name}" />
Delete it, and copy the global resources from res into it:
<delete dir="${config.resources}" />
<copy todir="${config.resources}">
<fileset dir="res"/>
</copy>
Change - to / in the config name, to make it a path in the URL parameter:
<var name="config.path" unset="true" />
<propertyregex property="config.path"
input="#{config.name}" regexp="-"
replace="/" casesensitive="true" />
Run an XSLT transform to modify the strings.xml file:
<xslt in="res/values/strings.xml"
out="${config.resources}/values/strings.xml"
style="ant/create_xml_configs.xslt"
force="true">
<param name="config.url.root" expression="${config.url.root}" />
<param name="config.name" expression="#{config.name}" />
<param name="config.path" expression="${config.path}" />
</xslt>
This is the XSLT stylesheet that I use:
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:param name="config.url.root" />
<xsl:param name="config.name" />
<xsl:param name="config.path" />
<!-- http://my.safaribooksonline.com/book/xml/9780596527211/creating-output/xslt-id-4.6 -->
<xsl:template match="/">
<!--
This file is automatically generated from res/values/strings.xml
by ant/custom_rules.xml using ant/create_xml_configs.xslt.
Do not modify it by hand; your changes will be overwritten.
-->
<xsl:apply-templates select="*"/>
</xsl:template>
<xsl:template match="*">
<xsl:copy>
<xsl:for-each select="#*">
<xsl:copy/>
</xsl:for-each>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<!-- the value of update_server_url must end with a slash! -->
<xsl:template match="string[#name='update_server_url']/text()">
<xsl:value-of select="$config.url.root" /><xsl:value-of select="$config.path" />/
</xsl:template>
<xsl:template match="string[#name='app_version']/text()">
<xsl:value-of select="." />-<xsl:value-of select="$config.name" />
</xsl:template>
</xsl:stylesheet>
And back to custom_rules.xml where I then extract the app_version from the original (unmodified) res/values/strings.xml:
<xpath input="res/values/strings.xml"
expression="/resources/string[#name='app_version']"
output="resources.strings.app_version" />
And use the antcall task to call the debug build:
<antcall target="debug">
<param name="resource.absolute.dir" value="${config.resources}" />
<param name="out.final.file" value="${out.absolute.dir}/${ant.project.name}-${resources.strings.app_version}-#{config.name}.apk" />
</antcall>
with two changed property values:
resource.absolute.dir tells the debug target to use my modified res directory, defined in the config.resources property above;
out.final.file tells it to produce an APK with a different name, including the configuration name (e.g. student-testing) and the version number extracted from strings.xml.
And then, finally, I can run ant build-all from the command line and build all four targets. A little bit more script, just before the end of the build-all target, lists the compiled APK files together for reference:
<echo message="Output packages:" />
<for param="config.name" trim="true" list="${config.names}">
<sequential>
<echo message="${out.absolute.dir}/${ant.project.name}-${resources.strings.app_version}-#{config.name}.apk" />
</sequential>
</for>
Related
Some Android libraries such as Google Analytics use resources for configuration purposes (e.g. ga_trackingId).
In these cases, I have different values for debug and production. What I currently do is manually comment the production values when I'm debugging, and viceversa. It looks like this:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- DEBUG -->
<string name="ga_trackingId">UA-12345678-1</string>
<integer name="ga_dispatchPeriod">1</integer>
<bool name="ga_debug">true</bool>
<!-- PRODUCTION -->
<!--string name="ga_trackingId">UA-87654321-1</string>
<integer name="ga_dispatchPeriod">120</integer>
<bool name="ga_debug">false</bool-->
</resources>
This way of switching configuration is tedious and error-prone, and generates unnecessary repository changes if I'm not careful. Is there a better way?
(e.g.: on iOS I use conditional compilation with the IF DEBUG macro)
Under the src folder you probably have a main folder where you store all shared stuff. But you can have specific resources for flavours or build types.
Put a folder named debug under the src folder where you will place a copy of your xml file but with with proper content. You have to maintain the folder structure under debug so the global_tracker.xml needs to be placed in ../src/debug/res/xml
Your folder structure should look like this:
Android Studio will notice that this xml file have multiple versions.
This is what you should see in AS:
You can use this for all kind of resources, i.e., have multiple versions of the same file and it will be "magically" chosen properly.
I had a similar issue with Google Maps keys where they depend on the signature. What I did was to use the ant script which generates/copies resources to the project conditionally. You can include the ant script in Eclipse under the Project>Properties>Builders
If you need to use the DEBUG value in the code, you can create a java file with static values that will be included conditionally too.
Please comment if ant environment variables worked properly (you can see the "Build type: " message in console after execution of the script).
<project name="build-res">
<property name="conditional.resources.dir" value="myresources" />
<property name="keys_file" value="res/values/keys.xml" />
<target name="copy-release" if="${build.mode.release}" >
<property name="build.type" value="Release" />
<echo message="Build type: ${build.type}" />
<property name="google.maps.key" value="nanana-value-for-release" />
<copy file="${conditional.resources.dir}/Release.java" tofile="gen/com/example/project/BuildInfo.java" />
</target>
<target name="copy-debug" if="${build.mode.debug}">
<property name="build.type" value="Debug" />
<echo message="Build type: ${build.type}" />
<property name="google.maps.key" value="lalala-value-for-debug" />
<copy file="${conditional.resources.dir}/Debug.java" tofile="gen/com/example/project/BuildInfo.java" />
</target>
<target name="build-res" depends="copy-debug,copy-release">
<echo file="${keys_file}" message="<?xml version='1.0' encoding='utf-8'?><resources><string name='google_maps_key'>${google.maps.key}</string></resources>" />
</target>
</project>
I'm building my Android application with Ant, and would like to set one of the values in my strings.xml at build time. For example, I could use
<string name="app_name">MyApp-DEBUG</string>
with a debug build, or I could use
<string name="app_name">MyApp<string>
for a release build. Is this possible?
There are two tasks in Ant that can help:
First is the <replace>. You give it a file name that contains parameters that can be replaced, and you give the <replace> task the values for those parameters. It replaces them in the file. I don't like this task because it's used to replace stuff that is under version control, and if you're not careful, you can end up changing the file without meaning to.
settings.xml
<settings>
<properties>
<property name="server" value="#SERVER#"/>
</properties>'
</settings>
Replace Task
<replace file="settings.xml">
<replacetoken token="#SERVER#" value="google.com"/>
</replace>
I've seen plenty of version control repositories where revision #3 of the replaced file was an accidental checkin of the the file with the replaced parameters (and not realizing it until the next release when the parameters didn't get changed). Then version #4 is a duplicate of version #2 which had the replacement parameters. Followed by a bad version #5, followed by a version #6 which restores the file, and on and on.
My preferred method is to copy the file over to another directory, and use <filterset>/<filter> tokens to change the file while being copied:
<copy todir="${target.dir}"
file="settings.xml">
<filterset>
<filter token="SERVER" value="google"/>
</filterset>
</copy>
Both can use a property file instead of specifying individual tokens. The <copy>/<filterset> pair can take a fileset of files and replace a bunch of tokens at once. (Be careful not to pass it a binary file!).
try this code, it works for me
<target name="app-name-debug">
<replaceregexp file="res/values/strings.xml" match='name="app_name"(.*)'
replace='name="app_name">MyApp-DEBUG<\/string>'/>
</target>
<target name="app-name-release">
<replaceregexp file="res/values/strings.xml" match='name="app_name"(.*)'
replace='name="app_name">MyApp<\/string>'/>
</target>
Getting an IDE to automatically increment build numbers is a long discussed issue - I'm always surprised that something that seems so basic (to me) is so much work.
The highest scored question and answer (by quite a bit) for this topic are here:
https://stackoverflow.com/a/8156809/150016
My problem is that the solution depends on .net and I'd prefer to avoid a .net dependency for Eclipse solution. I also want to avoid any dependency on VCS.
So, I'm looking for a solution that is portable and 'Eclipse-ish'. I think the best candidates are an Ant solution (which I will post) or a Java app similar to the dotNet app in the linked answer above.
(I didn't add my answer to that question because it has an accepted answer and is already a very long post. If the moderators prefer I will delete this question and add my answer to the question above.)
I was hoping for something comparatively simple but I don't think any solution to this problem really is simple.
In the project's properties dialog, under Builders, you can configure a script to run prior to building your apk.
In the properties dialog, select Builders from the list on the left. Click New to add a new script. On the dialog that pops up, select Program. On the ensuing dialog, call your Builder Program what you like in the Name field. Put the path to the script in the Location field. Give it a working directory in the Working Directory field. Note that you can specify variables in these fields (e.g., we use ${build_project:/libs/update_version.sh} to reference a script in the project's lib directory).
You'll want this to run before any of the other builders, so move it to the top of the list using the Up button.
On the Build Options tab of the Dialog (it's right under the Name field towards the top), you can select options for when you want this program to run.
I'm assuming something like Linux, Unix, or MacOS here. In those systems you can use grep or its variants to get the current versionCode, increment it, and then awk or similar to modify the file accordingly. Or, you could write a Ruby, Python, Perl, etc. script to replace the versionCode in the AndroidManifest.xml file. I suppose there are equivalent tools on Windows, but I'm not very familiar with that environment.
Have you tried to 'Maven-ise' your android project build ?
If you use the "maven-android-plugin", you can keep track of your project build with pom.xml's "version" tag.
In pom.xml you can increment the build version by using "versions-maven-plugin"
To reflect the incremented build number to to your AndroidManifest.xml you can use the Synchronizing Version technique within your pom file.
This way you can auto-increment the android project build number by running the "versions-maven-plugin" every time the app builds. The solution is also portable in the same way the Ant solution is.
I hope this can also solve the problem you have mentioned (and what I have understand). Please correct me if something is missing.
Here is a solution that uses Ant. I must admit that getting Ant setup was a bit of a hassle, but it seems to be a widely used and powerful adjunct to Eclipse, so I believe it will turn out to be valuable for me in other ways.
Ant is the preferred solution for building Android SDK apps without Eclipse (as described here), but is not part of the ADT so you probably won't have it if you have just been using Eclipse.
So, the first step is to download and install Ant from here.
Next add Ant to your path and make sure that your path also points to the JDK (rather then the JRE). On Windows I found that I also had to create JAVA_HOME and point it to the root of the JDK.
We only need Ant for the auto-increment step - the intention is not to replace Eclipse and use it for the whole build - but it is useful to do a few steps in that direction to ensure that Ant is working. So, go to the root of your project and type
> android update project --path .
This creates a build.xml for your project. Once I had done that I was able to do
> ant release
and Ant built my project. If you want to keep going in that direction I found this article useful.
Next, I edited my project's new build.xml and added the following task (it can go anywhere, just not in the middle of another task/target)
<target name="update.buildnum">
<buildnumber/>
<!-- this task creates (and increments) Ant property 'build.number' which I use below -->
<echo>Current build number:${build.number}</echo>
<echoxml file="res/values/buildnum.xml">
<resources>
<string name="buildnum">${build.number}</string>
</resources>
</echoxml>
</target>
The task used above auto-increments a build number in a file (which it will create) called 'build.number' in your project's root. The rest of the task above puts the current build number into a resource string so that your application can retrieve it with this code:
String build = context.getString( com.your.app.R.string. buildnum );
int appBuild = ( int ) Integer.parseInt( build );
Some people will prefer a solution that writes the build number to the application code in the AndroidManifest.xml. Here is an answer that includes a task to do that, but it requires a 3rd party Ant task (xmltask), and I'm a bit hesitant to have a 3rd party task modifying my manifest (which is now pretty huge) on every build. If you do decide to use that task then the corresponding Android code to get the build number would be:
String packageName = ctx.getPackageName();
PackageInfo pInfo = ctx.getPackageManager().getPackageInfo( packageName, 0);
int appVer = pInfo.versionCode;
Finally, you need to get Eclipse to run this Ant task every time you do a build.
Go to your project's properties, find the Builders, and click the button to add a new Builder. There are tons of options here but you only need to specify a few.
Give the builder some name by setting the Name: field.
Select the Buildfile:. I used the 'browse workspace' option to select it, and then it filled the field with "${workspace_loc:/appname/build.xml}"
Select targets from a list on the targets tab. In my case I had to remove the 'help' task (the default task in the build.mxl generated above), and then add the 'build.number' task. I suggest you only set a target for Manual Build:.
I moved the new Builder to be the first build step.
One issue with this solution is that every time you get Eclipse to deploy (run) your application it is considered to be a new manual build, even if the project hasn't changed. So, if you deploy your application to several devices, each device will have a different build with its own build number. One thing that will ameliorate this problem a bit is that Google says that a future ADT will allow for mutiple deployments in one operation.
This (above) was tested with Ant 1.8.4, Eclipse Indigo SR2 (3.7.2), ADT 20 preview 2 and SDK Tools 20, on Win7 32bit.
My solution is based on using Ant (without XMLTask)
<property name="version.file" value="version.properties"/>
<property name="manifest.file" value="AndroidManifest.xml"/>
<!--build.slot: 0.1.2.3.4.5-->
<property name="build.slot" value="2"/>
<target name="all">
<antcall target="increment.build"/>
<antcall target="update.manifest"/>
</target>
<target name="increment.build">
<propertyfile file="${version.file}">
<entry key="build.no" type="int" default="0" operation="+" value="1" />
</propertyfile>
</target>
<scriptdef name="setBuildNo" language="javascript">
<attribute name="verName" />
<attribute name="buildNo" />
<attribute name="buildSlot" />
<attribute name="property" />
<![CDATA[
var verNums = attributes.get("vername").split(".");
var buildNo = attributes.get("buildno");
var buildSlot = attributes.get("buildslot");
if (!(buildSlot > 1 && buildSlot < 10))
buildSlot = 2;
var newVer = "";
for (var i = 0; i < Math.min(verNums.length, buildSlot); i++)
newVer += (verNums[i].trim() || "0") + ".";
for (var i = verNums.length; i < buildSlot; i++)
newVer += "0" + ".";
project.setProperty(attributes.get("property"), newVer + buildNo);
]]>
</scriptdef>
<target name="debug">
<setBuildNo verName="1" buildNo="999" property="a"/>
<setBuildNo verName="2." buildNo="999" property="b"/>
<setBuildNo verName="3.3" buildNo="999" property="c"/>
<setBuildNo verName="4.4." buildNo="999" property="d"/>
<setBuildNo verName="5.5.5" buildNo="999" property="e"/>
<setBuildNo verName="6.6.6." buildNo="999" property="f"/>
<setBuildNo verName="7.7.7.7" buildNo="999" property="g"/>
<echo>1 => ${a}</echo>
<echo>2. => ${b}</echo>
<echo>3.3 => ${c}</echo>
<echo>4.4. => ${d}</echo>
<echo>5.5.5 => ${e}</echo>
<echo>6.6.6. => ${f}</echo>
<echo>7.7.7.7 => ${g}</echo>
</target>
<target name="update.manifest">
<fail message="File not found: "${manifest.file}"" status="1">
<condition>
<not>
<available file="${manifest.file}" />
</not>
</condition>
</fail>
<!-- Reads build version -->
<loadproperties srcfile="${version.file}">
<filterchain>
<linecontains>
<contains value="build.no="/>
</linecontains>
</filterchain>
</loadproperties>
<!-- Reads versionName from AndroidManifest -->
<xmlproperty file="${manifest.file}" collapseAttributes="true"/>
<fail unless="manifest.android:versionName" message="Attribute "android:versionName" undefined into "${manifest.file}"" status="1"/>
<property name="version.name" value="${manifest.android:versionName}"/>
<!-- Create a new version -->
<setBuildNo verName="${version.name}" buildNo="${build.no}" buildSlot="${build.slot}" property="new.version.name"/>
<!-- Replaces using regexp -->
<replaceregexp
file="${manifest.file}"
match="android:versionName.*=.*".*${version.name}.*""
replace="android:versionName="${new.version.name}""/>
<!-- Replaces for check and generates a exception (if not found version) -->
<replace
file="${manifest.file}"
token="android:versionName="${new.version.name}""
value="android:versionName="${new.version.name}""
failOnNoReplacements="true"/>
<!--<echo>${version.file}</echo>
<echo>${manifest.file}</echo>-->
<echo>Auto Increment Build: ${version.name} => ${new.version.name}</echo>
</target>
Using
ant [-Dmanifest.file=<file>] [-Dversion.file=<file>] [-Dbuild.slot=<number>]
-Dmanifest.file - path and filename AndroidManifest.xml. Default: AndroidManifest.xml in a folder with build.xml
-Dversion.file - path and file name in which to store the number Build version. Default: version.properties folder with build.xml
-Dbuild.slot - position of build number in version array. Default: 2, ie X.X.build.
Auto Increment Build in Eclipse
Project | Properties | Builders | New | Ant Build
Main
Build file
<path>/build.xml
Arguments
-Dmanifest.file=${workspace_loc://AndroidManifest.xml} -Dversion.file=${workspace_loc://version.properties}
Refresh
[x] Refresh resources upon completion
(*) Specify resources
[Specify resources]
<Project>
[x] AndroidManifest.xml
Build Options
[x] Specify working set of relevant resources
[Specify resources]
<Project>
[x] res
[x] src
[x] AndroidManifest.xml
My solution used some of the code found in other answers, XMLTask, and Groovy to automatically increment the build number. I also included a rule to do a git commit and tag to link the build number to a specific point in the version control - useful for bug tracking.
<taskdef name="xmltask" classname="com.oopsconsultancy.xmltask.ant.XmlTask"/>
<path id="groovy.classpath">
<fileset dir="/Users/deepwinter1/.gvm/groovy/current/lib/">
<include name="*.jar"/>
</fileset>
</path>
<taskdef name="groovy" classname="org.codehaus.groovy.ant.Groovy">
<classpath refid="groovy.classpath"/>
</taskdef>
<target name="update.build.number">
<xmltask source="AndroidManifest.xml">
<copy path="manifest/#android:versionCode" property="buildNum"/>
</xmltask>
<groovy>
buildNum = Integer.valueOf(properties["buildNum"])
properties["buildNum"] = buildNum + 1
</groovy>
<xmltask source="AndroidManifest.xml" dest="AndroidManifest.xml">
<replace path="manifest/#android:versionCode"
withText="${buildNum}"/>
</xmltask>
<antcall target="commit.and.tag.build">
<param name="buildNum" value="${buildNum}"/>
</antcall>
</target>
<target name="commit.and.tag.build">
<exec executable="git">
<arg value="commit"/>
<arg value="-a"/>
<arg value="-m"/>
<arg value="Build Number ${buildNum}"/>
</exec>
<exec executable="git">
<arg value="tag"/>
<arg value="b${buildNum}"/>
</exec>
</target>
a "bit" better workaround answer you can find here...
https://stackoverflow.com/a/8156809/304270
Tiny c# tool which will execute before everynew new build and increase appropriate value in manifest file..
I'm working on an Open-source project. As it is intended that anyone can download the source and build it themselves, I do not want to hard-code the package name anywhere - including the directory structure.
I use ant for building. Apparently I can modify build.xml, but I believe this is overwritten by android update. Whatever is used will be committed to the Git repo, and it should not be too complicated.
Currently the process to build the code straight from the Git repo is fairly simple. Here's an excerpt from the README file:
$ cd ~/src/isokeys/IsoKeys
$ android list targets # I build against API level 10.
$ android update project --name IsoKeys --target 1 --path ./ # Only needed first time.
$ ant debug && adb -d install -r bin/IsoKeys-debug.apk
To me, it makes sense to put the package name in local.properties, because this is .gitignore'd. As the package name won't be anywhere else, the build will fail without doing this. So there needs to be at least 1 extra step in the README, but I want to keep it to a minimum.
Edit: Of course, another requirement is that diffs make sense - which they don't if you manually rename the package name.
I did something similar (but not for this reason) which required updating the manifest at build time. The way I accomplished this was by making a second AndroidManifest and putting it under a directory named config.
So in config/AndroidManifest you could have something like this:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="#CONFIG.APP_PACKAGE_NAME#"
android:versionCode="#CONFIG.APP_VERSION_CODE#"
android:versionName="#CONFIG.APP_VERSION#">
<!-- EVERYTHING ELSE GOES HERE -->
</manifest>
Then you can use the regular bare bones build.xml ant script with just a few modifications (no need to copy the whole script from the android build system as they added some hooks for you to use without reinventing the wheel).
The build script should be reading local.properties by default, but if not add (or uncomment) a line like this:
<property file="local.properties" />
In your build script you should see a task called "-pre-build", change it like this:
<target name="-pre-build">
<copy file="config/AndroidManifest.xml" todir="." overwrite="true" encoding="utf-8">
<filterset>
<filter token="CONFIG.APP_PACKAGE_NAME" value="${app.packagename}" />
<filter token="CONFIG.APP_VERSION" value="${app.version}" />
<filter token="CONFIG.APP_VERSION_CODE" value="${app.versioncode}" />
</filterset>
</copy>
</target>
Then your local.properties file you would put the package name, version name/code like so:
app.version=1.0
app.versioncode=1
app.packagename=com.mypackage.name
Now you just need to make sure in your manifest that you fully qualify all of your activities/services/broadcast listeners etc.. That means you always specify the full package of your source code. If you want the package for your own source code to be dynamic you could replace out each of the prefixes to each class.. But that seems kind of silly.. It is easy enough to package your code up under your own package name and they can use it from any project by simply including the source or a jar in their project.
-- UPDATE --
Oh and one other thing you can do to notify the user that they must define a package name is use the fail tag in your build xml like this:
<fail message="app.packagename is missing. This must be defined in your local.properties file" unless="app.packagename" />
Put this after the line which reads the local.properties file
With thanks to Matt Wolfe for his help, I'm posting a partial answer with my efforts so far.
I noticed that the default barebones build.xml would also import custom_rules.xml:
<import file="custom_rules.xml" optional="true" />
So I created this file and started tinkering. This is what I have come up with so far:
<?xml version="1.0" encoding="UTF-8"?>
<project name="custom_rules" default="debug">
<target name="-pre-build">
<fail message="Please define app.packagename in your local.properties file." unless="app.packagename" />
<taskdef resource="net/sf/antcontrib/antcontrib.properties">
<classpath>
<pathelement location="/usr/share/java/ant-contrib.jar"/>
</classpath>
</taskdef>
<!-- How do I check for propertyregex?
<fail message="Depends on ant-contrib's propertyregex for app.packagename.path." unless="propertyregex" />
-->
<propertyregex property="app.packagename.path"
input="${app.packagename}/"
regexp="\."
replace="/"
global="true"
/>
<copy todir="build/" overwrite="true" encoding="utf-8">
<fileset dir="./">
<include name="AndroidManifest.xml" />
<include name="res/**" />
<include name="lib/**" />
</fileset>
<filterset>
<filter token="CONFIG.APP_PACKAGE_NAME" value="${app.packagename}" />
</filterset>
</copy>
<copy todir="build/src/${app.packagename.path}" overwrite="true" encoding="utf-8">
<fileset dir="./src/isokeys/">
<include name="**" />
</fileset>
<filterset>
<filter token="CONFIG.APP_PACKAGE_NAME" value="${app.packagename}" />
</filterset>
</copy>
</target>
<target name="-pre-clean" description="Removes output files created by -pre-build.">
<delete file="build/AndroidManifest.xml" verbose="${verbose}" />
<delete dir="build/res/" verbose="${verbose}" />
<delete dir="build/lib/" verbose="${verbose}" />
<delete dir="build/src/" verbose="${verbose}" />
</target>
<!-- NOW CHANGE DIRECTORY TO build/ BEFORE HANDING BACK OVER TO build.xml!!! -->
</project>
This sets everything up in build/ (which has the added bonus of keeping things neat and tidy), now the intention is for the sdk tools build.xml to run from this build/ directory. However, I can't find any way of cd'ing.
Easiest way might be replace the package name as late as possible. This way, you don't even have to touch your code. There is a nice article named Renaming the Android Manifest package(http://www.piwai.info/renaming-android-manifest-package/). Summary:
You can use aapt --rename-manifest-package to modify the package name
Alternatively, if you want package name replacement to be a part of the ant build process, you can override the -package-resources target:
copy the -package-resources target from SDK's build.xml
add manifestpackage parameter
We have an Android app built with Mono for Android, and now we have desire to make a deployable test version for use in acceptance testing. It is important that the production version remains on the device and keeps working. What is the recommended way of creating a test build without causing interference like package name collisions?
This solution applies to Mono for Android and allows you to change the package name of an application based on build configuration in Visual Studio:
Create a new Build Configuration, Test, for your solution.
Define a new Conditional compilation symbol, TEST, in your project.
Rename your existing AndroidManifest.xml to AndroidManifest-Template.xml
Create two .xslt files in the Properties folder:
manifest-transform.xslt:
<?xml version="1.0" ?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output indent="yes" />
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="/manifest/#package">
<xsl:attribute name="package">
<xsl:value-of select="'<your.test.package.name.here>'" />
</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
manifest-copy.xslt:
<?xml version="1.0" ?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output indent="yes" />
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Add two XslTransformation tasks to the BeforeBuild target of your project file:
<Target Name="BeforeBuild">
<XslTransformation
Condition="'$(Configuration)|$(Platform)' != 'Test|AnyCPU'"
XslInputPath="Properties\manifest-copy.xslt"
XmlInputPaths="Properties\AndroidManifest-Template.xml"
OutputPaths="Properties\AndroidManifest.xml" />
<XslTransformation
Condition="'$(Configuration)|$(Platform)' == 'Test|AnyCPU'"
XslInputPath="Properties\manifest-transform.xslt"
XmlInputPaths="Properties\AndroidManifest-Template.xml"
OutputPaths="Properties\AndroidManifest.xml" />
</Target>
Use the TEST symbol for conditional code:
#if TEST
[Application(
Label = "App Test",
Theme = "#style/Theme.App.Test",
Icon = "#drawable/ic_launcher_test")]
#else
[Application(
Label = "App",
Theme = "#style/Theme.App",
Icon = "#drawable/ic_launcher")]
#endif
You can now switch between test and regular app by changing build config :)
Changing the package name should probably be enough to keep things from conflicting, unless you are writing data to a hardcoded location, which would also need to be changed.