wtorek, 29 marca 2011

Taking a screenshot in Android

There are plenty quite few decent methods for taking a screenshot in Android.

  • Monkey runner that comes with sdk 2.3 (works in 2.2 (checked) and earlier (assumed))
    http://developer.android.com/guide/developing/tools/monkeyrunner_concepts.html#SampleProgram
    This is pretty cool, python thing. I just wonder if that can be used within android with Android Scripting Engine. Definitely good choice for automated things like tests.
  • Reading framebuffer with an adb command
    adb pull /dev/graphics/fb0 screenshot.raw

    and processing this with some geeky commands. Might be impressive for your geekfriends but is not very simple to use.
    More about it you can read here here and there. The problem is that you have to count the buffer size, know the format of framebuffer and is not quite obvious.
  • Using android screenshot library maintained by polidea
  • Using ddms tool from sdk

    Pretty simple but you can't use it in an environment in automated, operatorless (no human involved) way.
  • Using a hierarchy viewer tool. With that you can take screenshots not only of the whole screen but also individual controls (views in android jargon).
  • Writing something around ddmlib. In fact all the tools above except ASL use ddmlib.
  • Last, but definetely not least is something by google itself. No surprise done on top of ddmlib.
    This tool is called screenshot and is in java. You can find that in sdk
    sdk/screenshot/src/com/android/screenshot/Screenshot.java.
    I am pretty sure you can compile it standalone with ddmlib as only dependency, but I had already built my sdk and it was compiled in

    android-source/out/host/linux-x86/bin/screenshot2
    and that simply works!
    ./screenshot2
    Usage: screenshot2 [-d | -e | -s SERIAL] [-l] OUT_FILE
    -d Uses the first device found.
    -e Uses the first emulator found.
    -s Targets the device by serial number.
    -l Rotate images for landscape mode.


Enjoy!

czwartek, 10 marca 2011

Mavenize existing android eclipse project

I have been working some time on android projects that try to use maven. We grew with the maven android plugin and maven/android eclipse plugin.
In this article I wanted to create guide how to mavenize existing android/eclipse project. During writing this post I discovered that few things have been different with new plugins (android or eclipse) and changed for better.

I used git to track what changes are being done by each steps described here to show the diff(s) and explain better what has happened.


  1. Create eclipse project for transition

    The project to show the mavenization process has been created with following parameters.

    Application name: MavenizeMe
    Package: net.retsat1.starlab.mavenize
    Activity: Me
    Sdk: 8

  2. Create android maven project from archetype
    https://github.com/akquinet/android-archetypes/wiki/android-quickstart-archetype

    mvn archetype:generate \
    -DarchetypeArtifactId=android-quickstart \
    -DarchetypeGroupId=de.akquinet.android.archetypes \
    -DarchetypeVersion=1.0.5 \
    -DgroupId=net.retsat1.starlab.mavenize \
    -DartifactId=mavenize-me

    To change sdk to 8 you need to answer 'N' for the first question and correct the default 7.

  3. Copy pom.xml to the project with android

  4. Import the project as maven project.

    When the project gets imported you will notice changes in .project and .classpath.

    .project shows that the project has new nature of maven nature.
    One of the builders is removed and instead we have maven builder.

                     </arguments>  
    </buildCommand>
    <buildCommand>
    - <name>com.android.ide.eclipse.adt.ApkBuilder</name>
    + <name>org.maven.ide.eclipse.maven2Builder</name>
    <arguments>
    </arguments>
    </buildCommand>
    </buildSpec>
    <natures>
    + <nature>org.maven.ide.eclipse.maven2Nature</nature>
    <nature>com.android.ide.eclipse.adt.AndroidNature</nature>
    <nature>org.eclipse.jdt.core.javanature</nature>
    </natures>

    I have been having lot of problems when apk builder was enabled. It did not go well with maven builder and I was getting some random errors during the build. I think the earlier versions of the plugin had not been removing apk builder and it clashed with maven very often.

    .classpath has a bunch of new entries
     -     <classpathentry kind="src" path="src"/>  
    - <classpathentry kind="src" path="gen"/>
    + <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
    + <classpathentry kind="con" path="org.maven.ide.eclipse.MAVEN2_CLASSPATH_CONTAINER"/>
    <classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
    - <classpathentry kind="output" path="bin"/>
    + <classpathentry kind="src" output="target/android-classes" path="gen">
    + <attributes>
    + <attribute name="optional" value="true"/>
    + </attributes>
    + </classpathentry>
    + <classpathentry kind="output" path="target/android-classes"/>


    Not sure why src has been removed. I reverted it back by rightclick on src and add to build path.

    There is a wide belief that eclipse files should not be committed to version control but should be generated for every developer on his own machine. In general I believe that is true but for android maven project this might be exception to the rule.
    In this case these files are already there :) What is more, mvn eclipse:eclipse generates something that does not work well as in case of regular java projects.

  5. Enabling ADT to run and debug.

    After cleaning the project I noticed that the project cannot be run with eclipse adt plugin. Could not find mavenize-me.apk!

    Maven does produce apk, but it has version number in the name./target/mavenize-me-1.0-SNAPSHOT.apk.

    At the same time ADT plugin expects the apk with the name as the project at the output folder.

    The solution is to either force maven to generate a project without a version information or copy generated apk to the location that adt expects. I used the second solution with the help of maven-ant-plugin.
     <plugin>  
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-antrun-plugin</artifactId>
    <version>1.6</version>
    <executions>
    <execution>
    <phase>package</phase>
    <configuration>
    <target>
    <property name="source"
    value="${project.build.directory}/${project.artifactId}-${project.version}.${project.packaging}" />
    <property name="destination"
    value="${project.build.directory}/classes/${project.name}.apk" />
    <delete file="${destination}" />
    <copy file="${source}" tofile="${destination}" />
    </target>
    </configuration>
    <goals>
    <goal>run</goal>
    </goals>
    </execution>
    </executions>
    </plugin>


  6. Fixing the source location

    The apk generated by maven does not contain anything except the resource so the process crashes on emulator. It does not see the activity class Me. The default maven for java code is src/main/java but adt plugin prefers the src directory.

    We can either setup maven to work with
     <sourceDirectory>src</sourceDirectory>  
    or move the src folder to the location that maven prefers to work with. I chose the latter.

    mkdir -p src/main/java
    git mv src/net/ src/main/java/

    And set the eclipse classpath to new location. First you need to remove src from the path and add it src/main/java
    .classpath will show the diff as follows
     -    <classpathentry kind="src" path="src"/>  
    + <classpathentry kind="src" path="src/main/java"/>


  7. Remove the builders

    I did not understand why we need other builders than maven and java so I removed ResourceBuilder and Precompiler from the .project.

         </projects>  
    <buildSpec>
    <buildCommand>
    - <name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
    - <arguments>
    - </arguments>
    - </buildCommand>
    - <buildCommand>
    - <name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
    - <arguments>
    - </arguments>
    - </buildCommand>
    - <buildCommand>
    <name>org.eclipse.jdt.core.javabuilder</name>
    <arguments>
    </arguments>


    It turned out when I added new id to the main.xml that it is not visible in net.retsat1.starlab.mavenize.R. The eclipse project does not compile and as a result run button does not work. Also the project reports errors. Maven on the other hand successfully compiles the project.

    The id is generated in
    target/generated-sources/r/net/retsat1/starlab/mavenize but not in gen folder by adt. No wonder, because I removed the builder responsible for that. But if it is generated in the r directory we do not need gen folder. It must be on the build path. Deleting gen will cause adt to complain about missing problem so I needed a nifty way to deceive eclipse. This way is to create a linked folder name gen pointing to the r folder. We use eclipse variable for that


    PROJECT_LOC/target/generated-sources/r


     +        <link>  
    + <name>gen</name>
    + <type>2</type>
    + <locationURI>PROJECT_LOC/target/generated-sources/r</locationURI>
    + </link>



  8. The maven way

    At this moment I had source in the standard maven but still assets and res folders at root that does not look like a standard maven. I am not sure where would be the correct place in maven for them but chose src/main/android-assets and src/main/android-res. To make adt happy I did the same trick as with link to generated sources.

     +        <link>  
    + <name>assets</name>
    + <type>2</type>
    + <locationURI>PROJECT_LOC/src/main/android-assets</locationURI>
    + </link>
    + <link>
    + <name>res</name>
    + <type>2</type>
    + <locationURI>PROJECT_LOC/src/main/android-res</locationURI>
    + </link>


    The defaults in maven-android-plugin are the same as for adt plugin so is necessary to adjust those paths.

     -                    <assetsDirectory>${project.basedir}/assets</assetsDirectory>  
    - <resourceDirectory>${project.basedir}/res</resourceDirectory>
    + <assetsDirectory>${project.basedir}/src/main/android-assets</assetsDirectory>
    + <resourceDirectory>${project.basedir}/src/main/android-r</resourceDirectory>


  9. The maven project

    In those steps I achieved the project which is compiled solely by maven. The project is also using adt plugin for running and debugging so creates similar experience for the developers accustomed to adt.

    I am sure this is rather lengthy operation to convert to maven project and I had not discoverd many problems that bigger android maven projects may have on such trivial example. I have not considered a project with android tests.

    Another drawback of this setup is that Maven as a project builder is not very convenient. Is much slower than adt and also does not support incremental builds perfectly. In the background maven compiler is invoked constantly eventough there are no changes ins the source code. That might be little annoying.

    The pros for such setup is for sure the dependency management that maven may give you.
    On top of that also your continuous integration system will be more than happy to receive maven project and should generate the same artifact(s) as on developers workstations. I do not like when developers use different method of building the projects(on their workstation) than the official build does. Is often the case of works for me only or send my build to the customer scenario.

środa, 9 marca 2011

Getting started with Android & Maven

Is hard to imagine a project that is expected to deliver something without having a build script that automates the process. Batch/shell script is a primitive example but ant and maven are well established industry standards for build solutions. Maven scope is slightly bigger than that but main part of maven is for building.

Android folks have selected ant as their choice. In the sdk you can find ant examples (platforms/android-8/ant) and by using android create project command you can get a skeleton of android project that is built by ant.

Maven is widely considered as next step in evolution after ant so no wonder that many people start asking question if there is a support for android in maven.

Android maven plugin is the answer they look for. To get started there are few ways to do it

When analyzing samples, at the first glance you will notice that standard maven directory layout is not preserved. The reason for that is that maven android plugin does not want to go into the way of android development tools (adt) layout. People accustomed to maven will find it slightly odd.
To work with eclipse you obviously need m2eclipse plugin and maven-android-integration plugin. You can find that with eclipse marketplace directly from eclipse.
After plugins are installed the next thing you do is to import the project in eclipse (Import -> Existing maven project), it does not work on start but after clean or two you have a compilable maven + android project.

When project is imported in eclipse it will be treated as maven project but also will have android nature. This allows the developer to use a Run As Android Application (and Debug).


When you go to a builders configuration page (Projects -> Properties -> Builders) you should see 4 builders, 2 from adt (android resource builder and precompiler) and java and maven builder. I don't think that the 2 former are really necessary for the project. At least disabling them does not cause anything noticeably bad.

To learn what is available from the android plugin use help:describe goal.
mvn -Dplugin=android help:describe -Dfull=true This would display a lengthy list with the targets you may use along with the configuration xmls that can be placed in the pom.xml in plugin configuration.

Most important goals are
  • android:apk - to create android pakcage
  • android:deploy - to deploy on emulator/device
  • android:emulator-start

My own opinion



I must admit I was never a fan of maven. Mainly because mobile projects I was involved in where rather small, having no dependencies and mostly ant based. When I tried learn on my own I was put off by the conventions imposed by maven. My prejudice towards maven would last probably longer hadn't been I forced to use maven. Then I gradually discovered the power of auto dependency management and the benefits of conventions.


As android is concerned I still find some clashes but the maven plugin is strongly developed. Mailing list is active, development is active and new features arrive at good pace (recently apklib).
Still have not dwelt very deeply in android maven development so I cannot say is industry ready yet or not but the future looks promising.