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>
Related
I am lately having problems with a build process that has been working for months now - I may have updated the Android build tools since the last working build, but I am not sure.
In any case
ant -Dsdk.dir=$ANDROID_HOME -Djava.source=7 -Djava.target=7 clean release
is failing with the following message:
/home/dmta/development/android-sdk-linux/tools/ant/build.xml:694: Execute failed: java.io.IOException:
Cannot run program "/home/dmta/EclipseProjects/MyProject/Hawk/${aapt}"
(in directory "/home/dmta/EclipseProjects/MyProject/Hawk"): error=2,
No such file or directory
Where Hawk is a library project I am using.
What could be wrong here?
I can build the program from Eclipse but I need to use the ant build for proguard.
the AAPt location is changed in the latest update. Copy the lib/dx and aapt : these two files back to platform-tools.
Also try adding the tools path if not there already, example:
<!-- tools location -->
<property name="android.tools.dir" location="${sdk.dir}/tools" />
<property name="android.platform.tools.dir" location="${sdk.dir}/platform-tools" />
<property name="android.buildtools.dir" location="${sdk.dir}/build-tools/22.0.1" />
<condition property="exe" value=".exe" else=""> <os family="windows" /> </condition>
<condition property="bat" value=".bat" else=""> <os family="windows" /> </condition>
<property name="adb" location="${android.platform.tools.dir}/adb${exe}" />
<property name="lint" location="${android.tools.dir}/lint${bat}" />
<property name="zipalign" location="${android.buildtools.dir}/zipalign${exe}" />
<property name="aidl" location="${android.platform.tools.dir}/aidl${exe}" />
<property name="aapt" location="${android.buildtools.dir}/aapt${exe}" />
<property name="dx" location="${android.buildtools.dir}/dx${bat}" />
<property name="renderscript" location="${android.buildtools.dir}/llvm-rs-cc${exe}"/>
<property name="lint" location="${android.tools.dir}/lint${bat}" />
With the help of S P's initial answer I was able to get this to work.
It is worth noting that my question, or at least the question title is misleading: At first I thought that ant was actually looking for the executable aapt inside the project folder.
However, on closer inspection it actually is looking for ${aapt}. This indicates that ant is trying to use the value of a property called "aapt", which is however not set. So instead of building any sensible executable path it just appends the name to it's current working directory - or something like that.
In any case, all I needed to to was insert the following lines into my ~/development/android-sdk-linux/tools/ant/build.xml file:
<property name="android.buildtools.dir" location="${sdk.dir}/build-tools/22.0.1" />
<property name="aapt" location="${android.buildtools.dir}/aapt" />
<property name="aidl" location="${android.buildtools.dir}/aidl" />
<property name="dx" location="${android.buildtools.dir}/dx" />
<property name="zipalign" location="${android.buildtools.dir}/zipalign" />
That's because as soon as I told ant where to find aapt it complained about aidl, dx and zipalign.
I am trying to change an application name on compilation through an ant script(which is in the build.xml). Application name is stored in strings.xml. Following is the piece of code,
<target name="changeName">
<property
name="applicationName"
value="30004" />
<replaceregexp
file="C:\Users\<user-name>\git\appname\res\values\strings.xml"
match='<string name="app_name"<test</string<'
replace="\1${applicationName}\2"
byline="true"/>
</target>
But it doesnt change the name in strings.xml. Can someone please correct me on what I am doing wrong? Also please let me know how I can achieve similar operation through relative paths.
Thanks
Edit:
Following is the xml that needs to be changed through the above mentioned ant script
<string name="app_name">Test Application</string>
The following replacement will change the "app-name" attribute for any <string> element that contains this attribute:
<replaceregexp
file="C:\Users\<user-name>\git\appname\res\values\strings.xml"
match="<string name=".+""
replace="<string name="${applicationName}""
/>
To save yourself from escape code hell, you might want to set some properties using CDATA before running the replacement. This way you can clearly see the regex patterns:
<property name="regex.match"><![CDATA[<string name=".+"]]></property>
<property name="regex.replace"><![CDATA[<string name="${applicationName}"]]></property>
<replaceregexp
file="C:\Users\<user-name>\git\appname\res\values\strings.xml"
match="${regex.match}"
replace="${regex.replace}"
/>
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>
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
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>