The first few times I needed screenshots of an Android app for the Android Market description I alt-printscreen'd the emulator then sliced the app screenshot out of the resulting image. This is a pain and - as it turns out - completely unnecessary.

For capturing screenshots from physical devices there are (paid) apps in the store, but again, this is completely unnecessary if you are a developer and have set up the Android Development Tools.

Why? Because a screenshot tool comes packaged as part of the android sdk!

From Eclipse you can grab a screenshot by opening DDMS (Window -> Open Perspective -> DDMS), then in the Device pane, select the device you want to take a screenshot from (which can be the emulator or a "real" mobile device), then click the camera icon (top right in the following screenshot):

screenshot showing the take-screenshot icon in DDMS

From the command-line I'm afraid you're pretty much out of luck right now unless you feel like a bit of hacking to create your own cmdline screenshot grabber by connecting to the same service that DDMS connects to.

Android Emulator Screenshots, a lego comic strip made with Comic Strip It!

Comment on this post

Library projects with Eclipse Android and Maven, a lego comic strip made with Comic Strip It!

I'm working on extracting library projects to factor out common code shared between multiple projects. With everything compiling successfully I attempted to run my apk project in an emulator, and got hit with the following:

UNEXPECTED TOP-LEVEL EXCEPTION:
java.lang.IllegalArgumentException: 
  already added: 
    Lcom/android/vending/licensing/Manifest$permission;

Now it seems there's been a lot of problems with this recently due to changes in ADT, but the added complexity of Maven in my setup throws a few more spanners into the machinery. Robert Schmid describes a project hierarchy very similar to mine here, and actually gave me the final clue I needed to unravel the mess.

The difference between my situation and Robert's is that I'm using Maven for release builds and continuous integration - and so far its proving to be ... tricky ... to get the combination of Eclipse, Maven and ADT to play well together.

I got the dreaded UNEXPECTED TOP-LEVEL EXCEPTION because somewhere in the build cycle the Maven-Eclipse plugin is injecting its apklib dependencies into my eclipse build as well as the referenced projects in Eclipse. Having finally worked out what was causing my problem it was pretty easy to resolve:

  • right-click the project, select properties
  • go to the Maven pane
  • uncheck "Resolve dependencies from workspace projects"
  • repeat for all of the apklib projects referenced by your apk project

The down-side of this is that if I make changes in my eclipse apklib projects I have to build the jars with Maven before the changes are available to the dependent apk projects. I actually slightly prefer working this way anyway - I find that a little bit of isolation helps.

I should probably point out that I am using Maven-3.0.3, the m2eclipse and m2e-android Eclipse plugins, and the very latest SDK at time of writing (r16). YMMV.

Comment on this post

If you don't yet have your Eclipse - ADT - Maven tool-chain set up you might be interested in the previous post. If you are joining a team that has already set up as I describe here you probably want this post instead.

Google's ADT is great if you're working alone, but falls short when a team needs to work on the same Android project. It gets worse when you have multiple projects - especially if some are library projects.

It gets worse still if the development team is distributed (as we are) and/or running different development platforms - Windows, Linux, Mac OSX - (as we do). A description of how I've set things up follows this brief interlude:

Android Maven Eclipse Subversion, a lego comic strip made with Comic Strip It!

The important things I wanted to enable in our team environment are:

  1. That the whole team can "get" the latest code quickly and easily
  2. That the whole team can contribute updates to the codebase quickly and easily
  3. That any new team member coming on-board can build immediately from check-out (given a short list of pre-requisites)
  4. That any team member can easily, consistently and correctly build a signed apk for release to the market
  5. New projects can be created quickly and easily with minimum of re-work and copy-paste in configuration
  6. Componentisation (e.g. jars and apklibs) is a Good Thing, and should be encouraged by making it as straight-forward as possible
  7. Developers have their choice of OS

Here's how I've set things up to support these goals...

Pre-requisites

I am assuming that:

  • You use some form of source-code control (Subversion/GIT/other...). Of course you do :)
  • All developers will install Eclipse and ADT for themselves as a pre-requisite.
  • If, as a team, you use Maven and/or Continuous integration, all developers will also install m2eclipse and m2e-android eclipse plugins and Maven 3 (see previous article).
  • You have some common practices in your team like, for example, checking out all projects as siblings in a single workspace directory (otherwise you'll have problems with sharing relative paths to referenced projects between developers).

Our Setup

I've set up projects in the workspace such that all of the following are siblings in a single workspace directory:

  • A parent project that hosts most of the maven-android config as a parent pom.
  • A project that hosts the keystore, and is checked in to source-code control (I actually use the same project for both the parent pom and keystore).
  • A (Android Library) project that contains a copy of the market licensing code (Google recommend keeping a separate copy outside of the SDK install directory). Ours is checked in to SVN for convenient sharing.
  • Multiple Android library (apklib) projects for our own code that is shared between multiple apps (apk's).
  • Multiple Android (apk) projects

Since most of the maven configuration is provided by the parent pom, each new project requires only minimal configuration. The parent pom for our android projects currently looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="
  http://maven.apache.org/POM/4.0.0 
  http://maven.apache.org/xsd/maven-4.0.0.xsd" 
  xmlns="http://maven.apache.org/POM/4.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.mycompany</groupId>
  <artifactId>android</artifactId>
  <version>1.0</version>
  <packaging>pom</packaging>
  <build>
    <sourceDirectory>src/java/main</sourceDirectory>
    <pluginManagement>
      <plugins>
        <!--This plugin's configuration is used to store 
                Eclipse m2e settings only. It has no influence 
                on the Maven build itself.-->
        <plugin>
          <groupId>org.eclipse.m2e</groupId>
          <artifactId>lifecycle-mapping</artifactId>
          <version>1.0.0</version>
          <configuration>
            <lifecycleMappingMetadata>
              <pluginExecutions>
                <pluginExecution>
                  <pluginExecutionFilter>
                <groupId>
                  com.jayway.maven.plugins.android.generation2
            </groupId>
                    <artifactId>android-maven-plugin</artifactId>
                    <versionRange>[3.0.0,)</versionRange>
                <goals>
                  <goal>proguard</goal>
                </goals>
              </pluginExecutionFilter>
              <action>
                <ignore></ignore>
              </action>
                </pluginExecution>
              </pluginExecutions>
            </lifecycleMappingMetadata>
          </configuration>
        </plugin>
      </plugins>
    </pluginManagement>
    <plugins>
      <plugin>
        <groupId>com.jayway.maven.plugins.android.generation2</groupId>
        <artifactId>android-maven-plugin</artifactId>
        <version>3.0.0</version>
        <configuration>
          <androidManifestFile>
            ${project.basedir}/AndroidManifest.xml
              </androidManifestFile>
          <assetsDirectory>${project.basedir}/assets</assetsDirectory>
          <resourceDirectory>${project.basedir}/res</resourceDirectory>
          <nativeLibrariesDirectory>
            ${project.basedir}/src/main/native
          </nativeLibrariesDirectory>
          <sdk>
            <platform>14</platform>
          </sdk>
          <proguard>
            <skip>false</skip>
          </proguard>
          <sign>
            <debug>false</debug>
          </sign>
          <deleteConflictingFiles>true</deleteConflictingFiles>
          <undeployBeforeDeploy>true</undeployBeforeDeploy>
        </configuration>
        <extensions>true</extensions>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-jarsigner-plugin</artifactId>
        <version>1.2</version>
        <executions>
          <execution>
            <id>signing</id>
            <goals>
              <goal>sign</goal>
            </goals>
            <phase>package</phase>
            <inherited>true</inherited>
            <configuration>
              <archiveDirectory></archiveDirectory>
              <includes>
                <include>target/*.apk</include>
              </includes>
              <keystore>../android/keystore</keystore>
              <storepass>keystore-password-goes-here</storepass>
              <keypass>key-password-goes-here</keypass>
              <alias>key-alias-goes-here</alias>
            </configuration>
          </execution>
        </executions>
      </plugin>
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>2.3.2</version>
        <configuration>
          <source>1.6</source>
          <target>1.6</target>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>

An example of a pom from a library (apklib) project looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="
    http://maven.apache.org/POM/4.0.0 
    http://maven.apache.org/xsd/maven-4.0.0.xsd" 
  xmlns="http://maven.apache.org/POM/4.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <modelVersion>4.0.0</modelVersion>
  <parent>
    <groupId>com.mycompany</groupId>
    <artifactId>android</artifactId>
    <version>1.0-SNAPSHOT</version>
  </parent>
  <artifactId>android.util</artifactId>
  <version>1.0.1-SNAPSHOT</version>
  <name>Android Utils</name>
  <packaging>apklib</packaging>
  <description></description>
  <dependencies>
    <dependency>
      <groupId>com.google.android</groupId>
      <artifactId>android</artifactId>
      <version>2.2.1</version>
      <scope>provided</scope>
    </dependency>
        <!-- made available to android by 
             "maven android sdk deployer" -->
    <dependency>
      <groupId>android.support</groupId>
      <artifactId>compatibility-v13</artifactId>
      <version>r6</version>
    </dependency>
    <dependency>
      <groupId>oauth.signpost</groupId>
      <artifactId>signpost-core</artifactId>
      <version>1.2</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>oauth.signpost</groupId>
      <artifactId>signpost-commonshttp4</artifactId>
      <version>1.2</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.twitter4j</groupId>
      <artifactId>twitter4j-core</artifactId>
      <version>2.1.0</version>
    </dependency>
  </dependencies>
  <scm>
    <connection>scm:svn:svn://repo/project/trunk</connection>
    <developerConnection>
      scm:svn:svn://repo/project/trunk
    </developerConnection>
  </scm>
</project>

An example pom for an app (apk) project looks like this:

<project xsi:schemaLocation="
  http://maven.apache.org/POM/4.0.0 
  http://maven.apache.org/xsd/maven-4.0.0.xsd" 
  xmlns="http://maven.apache.org/POM/4.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <modelVersion>4.0.0</modelVersion>
  <parent>
    <groupId>com.mycompany</groupId>
    <artifactId>android</artifactId>
    <version>1.0-SNAPSHOT</version>
  </parent>
  <artifactId>android.ui</artifactId>
  <version>1.0.1-SNAPSHOT</version>
  <packaging>apk</packaging>
  <description></description>
  <dependencies>
    <dependency>
      <groupId>com.mycompany</groupId>
      <artifactId>domain</artifactId>
      <version>1.0.1-SNAPSHOT</version>
      <type>jar</type>
    </dependency>
    <dependency>
      <groupId>com.mycompany</groupId>
      <artifactId>android.util</artifactId>
      <version>1.0.1-SNAPSHOT</version>
      <type>apklib</type>
    </dependency>
    <!-- this project contains a copy of 
             the sdk licensing code -->
    <dependency>
      <groupId>com.mycompany</groupId>
      <artifactId>android.licensing</artifactId>
      <version>1.0.0-SNAPSHOT</version>
      <type>apklib</type>
    </dependency>
    <dependency>
      <groupId>com.google.android</groupId>
      <artifactId>android</artifactId>
      <version>2.2.1</version>
      <scope>provided</scope>
    </dependency>
        <!-- made available to android 
             by "maven android sdk deployer" -->
    <dependency>
      <groupId>android.support</groupId>
      <artifactId>compatibility-v13</artifactId>
      <version>r6</version>
    </dependency>
    <dependency>
      <groupId>oauth.signpost</groupId>
      <artifactId>signpost-core</artifactId>
      <version>1.2</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>oauth.signpost</groupId>
      <artifactId>signpost-commonshttp4</artifactId>
      <version>1.2</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.twitter4j</groupId>
      <artifactId>twitter4j-core</artifactId>
      <version>2.1.0</version>
    </dependency>
    <!-- prevent commons-logging from being included by 
         the Google HTTP client dependencies, which creates 
         a truck load of warnings and eventually kills eclipse -->
    <dependency>
        <groupId>commons-logging</groupId>
        <artifactId>commons-logging</artifactId>
        <version>1.1.1</version>
        <scope>provided</scope>
    </dependency>
  </dependencies>
  <scm>
    <connection>scm:svn:svn://repo/project/trunk</connection>
    <developerConnection>
      scm:svn:svn://repo/project/trunk
    </developerConnection>
  </scm>
</project>

Building a Release

Building a release, including running proguard to optimise and obfuscate the apk, and signing the apk from the shared keystore is now available from the maven cmdline with (as you'd expect):

mvn clean package

Its still early days for us, so I'm sure there are still wrinkles to iron out, but so far it seems to be working pretty well.

You can start the emulator and deploy the packaged apk into it using two further commands:

mvn android:emulator-start android:deploy

Enjoy!

Comment on this post

Getting Android Development Tools (ADT) for Eclipse to play nicely with Maven is quite a fiddle, involving a bunch of plugins for both Eclipse and Maven. Here's how I got it working (details after the comic-strip...). You might also be interested in two follow posts - setting up for team development with Android, Maven and Eclipse and joining a team developing with Android, Maven and Eclipse:

Converting Eclipse ADT projects to build with Maven, a lego comic strip made with Comic Strip It!

Plugins, Tools and Dependencies

  1. The Maven-Android plugin is Maven-3.0.3+ only, so you'll need to upgrade Maven if you are running an older version. The good news for Maven-2 users is the Maven guys worked hard to make 3 backwards compatible - and so far I've had no problems on some pretty complex projects.
  2. Eclipse Helios (3.6) or Indigo (3.7)
  3. The Android Developer Tools and SDK (of course).
  4. The m2eclipse Eclipse plugin (supposedly not required with Eclipse Indigo, but I had to install it)

Setup and Configuration

First, install maven 3.0.3 (or whatever newer maven is available).

Next install the m2eclipse plugin (you might want to check if you have it already - Indigo is supposed to come pre-supplied, but that probably depends on which Eclipse bundle you install. I usually go with Classic, and did not have m2eclipse. YMMV).

Now update your android sdk:

  • Using sdk manager, install all api levels you are interested in, including "google apis by google inc."
  • note: be sure to accept the license agreement for each selected jar (the ? should change to a green tick for ALL).
  • note: I find that the sdk manager either does not install all ticked packages in one go, or incorrectly reports the number of packages remaining to be installed - it "completes" but there are still pending installs (the "install N packages..." button re-enables with N > 0). I find it safest to restart SDK manager between each attempt so that it correctly shows what is installed.

If you want to work with Android 3 you need to perform an additional step. Maven Central does not have the jars available, so you'll need to use sdk deployer to push them into your repository.

  • check out with git (git clone https://github.com/mosabua/maven-android-sdk-deployer.git)
  • install android jars as required by running mvn from inside the sdk deployer project directory, example: mvn install -P 1.6, or install the whole lot with mvn install
  • if you have a shared / remote / central repository as we do, you will want to deploy the android jars there too. To do this you need to fill two fields in the android-sdk-deployer's pom.xml that the creator Manfred Moser helpfully separated out

    <repo.id>kv-repository</repo.id>
    <repo.url>scp://my-repo-host/repository</repo.url>
    

OK, we're done with installing!

Create your pom.xml

There are various ways you can create a pom for your existing Android projects. I went with the simple expedient of using mvn archetype:generate ...

  • from a directory you are happy to create projects in, execute mvn archetype:generate
  • you will be presented with an enormous list of archetypes - type android and hit return
  • the list should have been filtered down to about 3 from "de.acquinet.android..."
  • select "de.akquinet.android.archetypes:android-quickstart" - for me this was option 1
  • follow the prompts to conclusion - this will create a simple android project, including the pom.xml for an apk project.

Once you've done that you can copy the pom to your existing project(s) and modify it manually - this is what I did.

(Note: If you are starting a fresh new project you can just run mvn clean eclipse:eclipse to generate the eclipse project and classpath, then "import" the project into eclipse. After importing your project will just appear as a normal java project (neither maven nor android natures will be ascribed). To remedy that, right-click your project, go to configure->convert to Maven project, both natures are added automatically and you're ready to rock'n'roll.)

Integrate Eclipse and Maven

OK, last part ... Getting Eclipse and Maven to play nicely.

If you open the project in Eclipse now you'll probably find that it doesn't like your pom.xml. When you open the pom with m2eclipse installed it will open with the graphical xml editor. You'll notice that there's an error plugin execution not covered by lifecycle configuration....

error when pom packaging set to apk or apklib

Click the error and some details open up, including two quick fixes. Click the first quick fix ("discover new m2e connectors"). The following dialog pops up and after a short search, shows the m2e-android connector:

discover connectors dialog

Install the connector and the warnings should go away. Actually on one of my two machines they did not - I don't know why, but I had to take the 2nd quick-fix option of turning it off in Eclipse. For me that's just about ok, as I want the maven build to be the master anyway.

Congrats, you should now have a happy Eclipse project, and be able to build it using maven as expected.

What about android library projects?

Well basically its the same deal. I actually started with the library projects. The main difference is element in the pom should be set to apklib instead of apk.

Comment on this post

Android Source Code released, a lego minifigure comic strip made with Comic Strip It!

I'm quite surprised it took this long, but as of 13th December 2011 the source-code for Android is finally available as part of the SDK downloads, and can be grabbed with the SDK Manager.

There's no jar file available yet, just a folder containing the source for API level 14 and upwards only. The Android dev who announced this stated that it is "extremely unlikely" that the source for earlier API levels would ever be made available as part of the SDK.

Android Developer Tools (Eclipse plugin) doesn't automatically link to this folder either, so you need to set it up manually.

First, get the source:

  1. Fire up the SDK Manager - from Eclipse's "Window" menu, select "Android SDK Manager".
  2. Check the boxes next to "Sources for Android SDK" (at time of writing this is available for both API level 14 and 15).
  3. Click the "Install X packages..." button (bottom right corner), and wait while your new goodies download and install.

Once you're done downloading, head back to Eclipse and configure your Android project to use the source directory:

  1. In the package-explorer, locate the Android jar file (you may have to turn on "Show referenced libraries" from the little down-arrow icon in the top-right corner of the Package Explorer toolbar before it will show up in the list).
  2. Right-click the jar and choose "Properties" - a dialog pops up.
  3. Select "Java Source Attachment", then click "External Folder..."
  4. Navigate to the source directory, which will be located inside your Android SDK install directory (mine is at /home/steve/dev/sdks/android-sdk-linux/sources/android-14. Select OK to leave the file-chooser dialog, and OK again to leave the jar Properties dialog.
  5. Hover any Android class and ctrl-click (or place the caret on the class name and hit F3) to enjoy the source code for that class :)

For the full source-code saga check out the issue-tracker entry requesting the source to be made available (be prepared for a long read!)

Comment on this post