Eclipse, Android and Maven: Part 7 - Global properties and settings for Maven

For those who work on projects with multiple developers such as open source projects, you'd realise that the way I've currently set up Maven is not very good for multi-developer projects.

It's also not a good idea to have passwords or computer specific SDK locations in your pom.xml file because each developer will need to change the pom.xml in order to compile the code.

If you're fine with using script files to pass arguments to Maven then that's fine. Otherwise, you can set up the Maven global settings.xml file.

Contents

Machine specific Maven settings

It can be located in either:

  • %M2_HOME%/conf/settings.xml
  • %USERPROFILE%\.m2\settings.xml (aka ~/.m2/settings.xml)

Either way, the guts of it looks the same.

Open up your settings.xml file (you may need to create it for the user specific one).

I use this to store the location of my Android SDK home folder, Android SDK Maven repository and information such as keystore file and passwords.

The profile named "variables" sets up some properties for us which is automatically filled into your pom.xml file when needed.

The Android Maven repository is set up so you don't have to do it in every pom.xml file you work on.

And lastly, active profiles tells Maven to always use these the variables profile.

Once you're done, you'll need to reload the settings into Eclipse (if you don't want to restart).

  • Preferences
  • Maven
  • User settings
  • Click "Reindex" to update the settings

And that's it! We've finally reached the end of the Maven journey!

ostrich-skiing

Time to go full pro at Maven now! I mean if an ostrich can ski that good, what's YOUR excuse?

Source

Eclipse, Android and Maven: Part 6 - Sign your Android app APK for release

And now its time to sign your APK for release. This took a while to figure out due to all the conflicting information about ways of setting this up.

I've determined that the easiest way of doing this is to simply create a "toggle variable" that skips the signing process unless specified by the calling script / command line argument.

Contents

Setting it up

Open up your pom.xml file for editing, go to project > properties and add in:

<sign.skip>true</sign.skip>

This, by default, will skip the signing process.

Now scroll down to find the project > build > plugins and add in this:

<!-- Sign the APK with release signature -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jarsigner-plugin</artifactId>
<version>1.3.2</version>
<configuration>
<!-- Required because it's initially signed with a debug cert -->
<removeExistingSignatures>true</removeExistingSignatures>
<skip>${sign.skip}</skip>

<keystore>${sign.keystore}</keystore>
<storepass>${sign.storepass}</storepass>
<alias>${sign.alias}</alias>
<keypass>${sign.keypass}</keypass>
</configuration>

<executions>
<execution>
<id>sign</id>
<goals><goal>sign</goal></goals>
</execution>
<execution>
<id>verify</id>
<goals><goal>verify</goal></goals>
</execution>
</executions>
</plugin>

You may have noticed in the configuration that <skip> is controlled by ${sign.skip}, as defined above in the properties. You can override this by passing in command line arguments to mvn.

Similarly with the keystore, storepass, alias and keypass, they're all configured with properties or command line arguments. This is a good way of keeping your passwords out of the pom.xml file and source control.

Another point to make clear is that you NEED removeExistingSignatures. When the jar/apk is initially created, it's signed with a debug certificate. You have to REMOVE that before signing with your own, otherwise the verification goal will fail with this error:

[INFO] jarsigner: java.lang.SecurityException: invalid SHA1 signature file digest for res/drawable-xhdpi/abc_ic_go_search_api_holo_light.png

How to sign your APK

Normally, you'd just run this to create a debug APK:

mvn package

To create the release APK, type the following to begin the build process:

mvn package -Dsign.skip=false -Dsign.keystore=X:\your\cert.keystore
-Dsign.storepass=STOREPASSWORD -Dsign.alias=KEYALIAS
-Dsign.keypass=KEYPASS

The "-Dvarname=value" specifies it's a variable being passed to your build script, which overrides any instances of ${varname} in your pom.xml file.

It's also a good idea to enable verbose output to test your configuration until it's working properly. Place this under the jarsigner plugin > configuration:

<verbose>true</verbose>

You'll probably also run into this warning message:

[INFO] Warning:
[INFO] No -tsa or -tsacert is provided and this jar is not timestamped. Without a timestamp, users may not be able to validate this jar after the signer certificate's expiration date (2068-08-23) or after any future revocation date.

Don't worry, it's not too bad. The date is the expiry date of your certificate so it's fine if your certificate expires in an extraordinarily long period of time.

By now you should be able to produce a signed APK file, ready for release.

Sources

Eclipse, Android and Maven: Part 5 - How to debug your Android app with Maven?

We can see the light at the end of the tunnel now. I'll tell you the steps required, and then describe the nitty gritty details after for those who are interested.

Contents

How to debug with Maven?

  • First of all, connect your test Android device or start the emulator (preferably Genymotion)
  • Place this command into a batch/shell file called "mvn-debug.bat" because you'll run it quite regularly.
mvn package android:deploy android:run -U
  • While you're waiting for it to finish compiling and deploying, set Eclipse into DDMS perspective mode so you can see stuff like the activity log and devices tabs.
  • Each time you start a new Eclipse session, you'll have to tell it once (manually) to debug your app. (I haven't found a way around this yet)
  • Find the "Devices" tab.
  • When your app starts it'll display a message "Waiting for debugger" and wait there.
  • Find and select your app in the devices tab. If you enabled debugging in your app manifest file, it should have a red debug icon next to it, circled in red.
  • Click on the green debug icon in the devices tab toolbar circled in orange. You'll only need to do this the first time debugging.
  • Now you should be debugging like you were before Maven.
  • When you need to debug again in the same session, just run the "mvn-debug.bat" script and it'll start itself. No need to enable debugging again for this session.

image

What actually happened?

  • package: This goal compiles your app and spits out an APK to the "target" folder.
  • android:deploy: This goal uploads your newly compiled APK to the connected device. If there is no device, the build status will be "FAILED" so don't be alarmed if you see that.
  • android:run: This command runs your app on the device.
  • -U: As mentioned in the last tutorial, the -U flag tells Maven to fetch dependencies from the source. This is optional, so use it depending on your circumstances.
  • Because the build and deploy is done outside of Eclipse, it doesn't really have a way of knowing when to attach to ADB for debugging. That's why you have to tell it to debug separately.

Sources

Eclipse, Android and Maven: Part 4 - Share a library project using a local Maven repository

While you're converting your app to a Maven project, you'll soon realise that you need to tackle any shared libraries first. If you've got more than a couple of apps, it's likely you've created a common library between them to keep your code DRY (don't repeat yourself).

(Sorry for interlinked Part 3 and Part 4, but it's just the nature of this process)

Contents

The problem with Maven (and Gradle)

The way Maven works is it downloads shared libraries from a repository of code and attaches it to your code when needed. The problem with privately shared libraries is that you need to push it to a place that Maven can access.

A few answers on StackOverflow say you have to push your private library to a public Maven repository. To me, that's just not acceptable! A workflow should not force private code to be accessible to the public. Not to mention you'll clutter up public repos with code that should not be there in the first place and waste good namespaces. In short no, this is a shit solution.

A other sources suggested making a local repository. That sounds much more reasonable but how do I make that available across multiple dev machines?

Using Google's Support library from the Android SDK local repository

Most likely you're using Google's Android support libraries, so you'll also need to include the SDK local repository as a source for Maven.

First of all, make sure you have the files in the first place.

  • Fire up the Android SDK Manager
  • Scroll down to Extras and make sure "Android Support Repository" is ticked
  • Click Install

image

To include the local repository as a source, paste this under the <project> element.

<project ...>
<repositories>
<!-- Include Android Support Repository so we can access support-v4 and appcompat-v7 aar -->
<repository>
<id>google-sdk-support</id>
<url>file://D:/Android/sdk/extras/android/m2repository/</url>
<releases><enabled>true</enabled></releases>
<snapshots><enabled>false</enabled></snapshots>
</repository>
</repositories>
</project>

As you can see in  the <url>element, the value can also be a local file system. If we can somehow replicate that structure we can safely share our code, privately.

To gain some inspiration, I studied the local Maven repository found in the Android SDK under "android-sdk\extras\android\m2repository\" and found an answer!

Setting up a local repository for your library

Knowing that Google can make their libraries accessible locally, I decided to make my own library accessible to other projects using this local repository method.

The only difference is that I'm creating a folder called "local-repo" within the root of the library project. This way, projects that use this shared library can refer to it using relative paths.

Initially I tried using the exec-maven plugin to manually move the main artefact to a common folder, but it just wouldn't work from within the Eclipse IDE. It'd work from the command line, but it's too much work compared  to install:install-file" which does just that with far less configuration. Gotta use the right tool for the right job!

So I set up the goals so it'd run "package" to first create the APK, and then using the "install:install-file" goal from the Maven Install plugin, pushes the main artefact (AAR, JAR, etc) into the local-repo repository in a structure which Maven can use.

I stored this command into "mvn-update-local-repo.bat" (excuse the poor naming) in the root folder of the project and run it whenever I need to push something to the local repo.

mvn package install:install-file -DlocalRepositoryPath=local-repo
-DcreateChecksum=true -Dpackaging=aar
-Dfile=target\twig-android-library.aar
-DgroupId=org.twig.common -DartifactId=twig-android-library
-Dversion=1.0.0-SNAPSHOT

The script pushes the file target\twig-android-library.aar to the repository "local-repo". It specifies that it's v1.0.0-SNAPSHOT.

This should be made more flexible by reading the pom.xml file, but haven't found a way yet.

Snapshot builds and caching

Snapshot builds are fetched only once a day. If you're testing and making multiple changes in the same day, use the -U arg to any mvn command to force Maven to fetch from the source, rather than once a day.

Why is this important?

Because each time you build your APK project, the shared library's AAR files from your local-repo are automatically cached in %USERPROFILE%\.m2\repository (aka ~\.m2\repository).

If fixing bugs in your library, Maven is going to use the cached copy so your changes won't be compiled into the APK unless you specify the -U option.

Using your shared library from an app project

So now you're ready to use the shared library in your APK project.

Edit your pom.xml file and look under <projects><repositories>.

Add another <repository> entry for your shared library.

This example assumes that your app project and shared library projects are on the same folder level.

<repository>
<id>twig-android-library</id>
<releases><enabled>true</enabled></releases>
<snapshots><enabled>true</enabled></snapshots>
<url>${project.baseUri}../twig-android-library/local-repo</url>
</repository>

And there you have it, we've safely passed another hurdle without breaking any monitors.

Now you're ready to debug!

Figuring out the cryptic ClassNotFoundException: Didn't find class on path: DexPathList [zip file], nativeLibraryDirectories=[ /vendor/lib, /system/lib]

Fortunately this is a simple one. It's due to the incompatible versioning of libraries used between the Android Support Library and the Android Support v7 Compat library.

Make sure the support library project you've associated with your APK project is the same version as the support library you declared in the pom.xml dependencies.

This should clear you of any more cryptic errors.

Sources

Eclipse, Android and Maven: Part 3 - Converting an existing Android project to a Maven project

Once you get the hang of creating and compiling a Maven project, it's pretty easy to convert an existing project to a Maven one. Just follow a few basic steps and make sure the code compiles properly before continuing.

You'll need a "pom.xml" file in the root of your project. Just use the generic pom.xml file I shared earlier in Part 2 - Compiling and building your APK .

Contents

Steps to convert your project

The first thing you should do is replace as many lib/jar files as you can with their Maven dependency equivalent as covered in Part 2 of the tutorial. Visit the project's website and find the details as most of them will be available on Maven.

If you're using a shared library project then skim the points below before reading Part 4, which explains how to share library projects. You'll need to convert library projects first.

Once the pom.xml file is ready:

  • Right-click on your project > Configure > Convert to Maven project
  • It will now do things to your project and touch it in strange places.
  • Project > Clean to remove all the extra crud.
  • Maven > Update project to sync the settings and download dependencies
  • Run the mvn-debug script

OR

  • Build by right clicking on the project > Debug > Maven build
  • As before, enter in "package" as the goal.

 

You'll probably get some project specific errors so fix them as they come. Keep at it. Make sure it validates within Eclipse before continuing!

I know these steps are fairly generalised, but that's the main things to look for.

Women, children and library projects first

The pom.xml file for your library project should be very similar to the pom.xml for an APK project, with two minor differences.

Rather than using the <packaging> type "apk", we'll use "aar" instead which compiles to an Android ARchive format.

The other small difference is that it doesn't need to be signed for release builds.

Once your library projects are compiling cleanly, skip forward to the next tutorial to Part 4 for a moment to see how to include them into your APK project using local Maven repositories.

The tutorial also covers how you should include Google's support library in your code from the Android SDK.

Sorry for interlinked Part 3 and Part 4, but it's just the nature of this process.

Eclipse, Android and Maven: Part 2 - Compiling and building your APK

Now that you have Eclipse set up properly, you're ready to start building a project!

You should create a new project and learn how it works before you start migrating an existing project.

Contents

Creating a new Maven Android project

  • File > New > Other > Maven > Maven Project > Next > Next.
  • For "Catalog", select  Android (You'll need to be online for this to work)
  • Select "android-quickstart"
  • Next
  • Group ID is the package name portion of your app, such as "org.companyname"
  • Artifact ID is your app's compilation name, such as "codepeeker".
  • Version is pretty much the SEMVER version number. Snapshot indicates it's a dev build of sorts that's constantly pushing a compiled library to a repo. I usually set my version to 1.0.0 for new projects.
  • For more on naming conventions, see Maven – Guide to Naming Conventions
  • Package is prefilled as you enter in your Group and Artifact information.
  • Change platform to the Android API version needed.
  • Click finish when done.

image

  • And the result is this... a broken project.
image Same as regular Android development
gen: As before, this is the generated files folder.
src: Where your source files go
assets: Asset files
bin: Binary files. APK file no longer goes here!
res: Resource files. Feel free to remove all the ones you don't need. 

New folders
target: This is where you can find your APK file once it has been compiled.

New files
pom.xml: The configuration file which tells Maven how to build your project.

 

Ignoring the consume-aar goal

The pom.xml file generated for you is broken by default. At the bottom of pom.xml, you'll see this lint error:

Plugin execution not covered by lifecycle configuration: com.jayway.maven.plugins.android.generation2:android-maven-plugin:3.8.2:consume-aar (execution: default-consume-aar, phase: compile)

image

You can use the suggestions by clicking the red cross on the left to either:

  • Option 1: Mark it as ignored in pom.xml (preferred, but makes the XML messy)
  • Option 2: Ignore permanently in Eclipse (not advised if you work with other people or compile with command line)

Note: If neither of these options are available to you, close pom.xml, right-click it in the file explorer and select Open With > Maven POM Editor.

image

Choosing Option 1 will add the following <plugin> XML to ignore the consume-arr build goal (under build > pluginManagement > plugins). Don't copy paste, just let Eclipse do it for you.

This plugin's configuration is used to store Eclipse m2e settings only. It has no influence on the Maven build itself.

Here's one I prepared earlier

I've created a generic pom.xml that works great as a base for most Android app projects. Just fill in the important parts and the rest should be fairly straight forward. Main things to change are:

<groupId>org.twig.apps</groupId>
<artifactId>d2_runewords</artifactId>
<version>1.0.0</version>
<name>Diablo 2 Runewords</name>
  • The platform version (Android API level).
<sdk>
<platform>16</platform>
</sdk>
  • Make changes to anything under <properties> to suit your system setup such as "android.sdk.path". These are Maven variables which you can access using ${varname}.
  • Added a jarsigner plugin so the Android APK is signed when we're ready to release.

A few other things I had to configure are:

  • Java version 1.6 as a minimum for annotation processing (butterknife, parceler, android annotations, etc)
  • I've configured the Android SDK local repository so you can easily use the support libraries. (More about that in Part 3)
  • The way you include dependencies is via dependencies > dependency
  • defaultGoal tells the script what to do and in what order. The "package" goal creates the APK and "android:deploy" pushes it to the device for testing.

Here's the file:

Whenever you make changes to pom.xml, you may need to keep the project in sync with it. Right click on your project > Maven > Update project (or Alt+F5 for those keyboard savvy users). This ensures the Eclipse project has the same configuration as the Maven pom.xml file, dependencies are pre-downloaded ready for compilation and stuff like that.

Compiling from command line

Personally I much prefer compiling from command line. It avoids a lot of the unnecessary complexities and repetitive confirmations involved with the Eclipse UI when it comes to debugging.

I created a script/batch file that contains the following command:

mvn package android:deploy android:run

What this does is compile your APK (package), push the APK to the emulator (android:deploy) and then start it (android:run).

I saved it in the same folder as your project and named it "mvn-debug.bat". Just run that whenever you want to debug your app. It's a lot less trouble than going through the "Eclipse way".

At this point you should be able to compile an APK file and find it in the "target" folder.

Compiling the Eclipse way

Now the Eclipse way...

  • Right-click on your project > Debug > Maven build.
  • Type in "package android:deploy android:run" as a goal.
  • Click "Debug"
  • Check the Maven console to see the log.
  • If everything went according to plan, you should now have a healthy baby APK sitting in your target folder.

image

image

Adding dependencies

Now the main purpose of this lengthy exercise is to easily add new dependencies and removing the complicated mess involved with annotation settings.

The best way of finding the dependency groupID and artifactID is looking up the project homepage and copying the install instructions. For example Butterknife. Here's the Gradle version:

compile 'com.jakewharton:butterknife:6.0.0'

Compared to the Maven version:

<dependency>
<groupId>com.jakewharton</groupId>
<artifactId>butterknife</artifactId>
<version>6.0.0</version>
</dependency>

It's pretty easy to translate the Gradle version to Maven, so if the project doesn't provide the Maven dependency XML you can just convert it yourself.

Under the <dependencies> tag, add in your dependency.

<dependencies>
<dependency>
<groupId>com.google.android</groupId>
<artifactId>android</artifactId>
<version>${platform.version}</version>
<scope>provided</scope>
</dependency>

<!-- New dependency here -->
<dependency>
<groupId>com.jakewharton</groupId>
<artifactId>butterknife</artifactId>
<version>6.0.0</version>
</dependency>
<!-- /copy -->
</dependencies>

Optional: For some libraries which require multiple dependencies to be the same version (support-v4 and appcompat-v7), I like to have a dependency version under <properties>, so it's easy to find and change:

<android.support.version>19.1.0</android.support.version>

And replace the dependency version with:

<version>${android.support.version}</version>

Once you save pom.xml, update your project and it should automatically fetch any files needed from the Maven repository.

Perform a build to confirm everything is working fine.

Sources

Eclipse, Android and Maven: Part 1 - Installing Maven for Eclipse

Sadly, this tutorial series is far longer than it should be because of the sheer number of things that can go wrong during this process. I spent about a few days all up getting the whole process ironed out smoothly enough for A to Z Android APK development and release.

Some people may be wondering why I don't just use Android Studio because Gradle is awesome. I do, at home and yes, Gradle is good and I love the update process but I feel Android Studio still isn't quite there yet. There's still no one-button deploy+debug (something so trivial that happens so often in Android development) and it's constantly destroying my CPU/battery life while I'm travelling. That's just not feasible.

Note: Before you start, back up your Eclipse folder. If history is anything to go by, something may go wrong even if you did everything correctly. This quick backup can save you hours of configuration if something does go pear shaped.

Contents

Downloads

  • Eclipse v3.8.2 (I've found these instructions don't work for Eclipse below 3.8, unsure about v4.x+)
  • Java 7 JDK (not JRE. Java 6 is ok, but not Java 8)
  • Download Maven 3.2.3 (Binary zip).
  • While that's downloading, using the Eclipse software installer: Download Maven for Eclipse (m2e-apt) via http://download.eclipse.org/technology/m2e/releases
  • Untick "Show only the latest versions of available software"
  • Install "m2e - Maven Integration for Eclipse v1.4.1.20140328-1905".
    Don't use the latest version 1.5.0, it'll cause this error:

Cannot complete the install because one or more required items could not be found.
  Software being installed: m2e - Maven Integration for Eclipse (includes Incubating components) 1.5.0.20140606-0033 (org.eclipse.m2e.feature.feature.group 1.5.0.20140606-0033)
  Missing requirement: Maven Integration for Eclipse 1.5.0.20140606-0033 (org.eclipse.m2e.core 1.5.0.20140606-0033) requires 'bundle com.google.guava [14.0.1,16.0.0)' but it could not be found
  Cannot satisfy dependency:
    From: Maven Integration for Eclipse (Editors) 1.5.0.20140606-0033 (org.eclipse.m2e.editor 1.5.0.20140606-0033)
    To: bundle org.eclipse.m2e.core [1.5.0,1.6.0)
  Cannot satisfy dependency:
    From: m2e - Maven Integration for Eclipse (includes Incubating components) 1.5.0.20140606-0033 (org.eclipse.m2e.feature.feature.group 1.5.0.20140606-0033)
    To: org.eclipse.m2e.editor [1.5.0.20140606-0033]

  • Restart Eclipse when done.
  • Again using the Eclipse software installer: Download Android for Maven (Android Connector) from http://rgladwell.github.io/m2e-android/updates/. This lets you create Android projects using Maven.
  • Restart Eclipse when done.

Setting up Eclipse for Maven

The plugin com.jayway.maven.plugins.android.generation2:android-maven-plugin:3.8.2 requires Maven version 3.1.1

The android-maven-eclipse plugin has an implementation of Maven built into it. That implementation is for an older API and no longer compatible with the builds that we need (AAR support is needed for Android's appcompat-v7 and shared libraries).

  • Extract "apache-maven-3.2.3.zip" to "C:\Development\Java\apache-maven-3.2.3\" (You can put this anywhere, but adjust the instructions to match your folder)
  • In Eclipse, go to Preferences > Maven > Installations > Add > C:\Development\Java\apache-maven-3.2.3\
  • OK to save

Make sure you're using the right runtime environment.

  • Open up Preferences > Java > Installed JREs
  • If "JRE" is ticked then you've got the wrong runtime environment installed. What you need for development is a JDK, and Java 7 (or 6) is the version you need (Android doesn't support Java 8 yet).
  • Click on Search and find your Java installation folder, normally C:\Program Files\Java
  • Search should now fill in some JREs for you.
  • If no JDK's appear, you'll have to download a Java7 JDK and repeat the search after it's installed.
  • Select the JDK and click OK to save.

image

If you didn't set this up correctly, it will cause the following error:

No compiler is provided in this environment. Perhaps you are running on a JRE rather than a JDK?

This might sound silly, but open up Preferences again. This step needed the JDK to be set up properly in order to work.

Now we're going to select which environment is executed. This prevents the stupidly undescriptive "The method of type new must override a superclass method" error when trying to compile your code.

  • Go to Preferences > Java > Installed JREs > Execution Environments.
  • Make sure that you have "JavaSE-1.7" (or JavaSE-1.6 if you're using Java 6) and that the "Compatible JREs" column shows the JDK option.
  • Tick it, then OK to save.

image

System environment variables

One final step for the future is to set some system environment variables.

  • Start menu > type in "system environment variables"
  • Click on "Edit the system environment variables"
  • Click on "Environment variables"
  • Click new on either user or system variable
  • Firstly, set "M2_HOME" to "C:\Development\Java\apache-maven-3.2.3" (no trailing slash)
  • Now edit "Path" and add "%M2_HOME%\bin" at the end.

This will let you run "mvn" from any command prompt, which is needed for building, deploying and debugging later on.

Now you're finally to start converting your Android project to Maven.

Sources

Django: Export comments to Disqus WXR / XML format

With the help of the django-disqus module, it's pretty easy to export our comments out to Disqus.

First you'll need to install the module django-disqus (v0.4.3 at time of writing).

The example provided was used when I switched www.thatawesomeshirt.com over to Disqus. The class used is loosely based off the regular class used in the syndication module.

urls.py

from tas.feeds import ShirtCommentsWxrFeed

urlpatterns += patterns('',
('^export/comments/', ShirtCommentsWxrFeed()),
)

feeds.py

As you can see, this is where all the heavy lifting happens.

Once you have the output, you can import it more than once without creating duplicates. I believe duplicate detection is done using the value from comment_id().

image

In your Disqus admin:

  • Go to Discussions > Import > Generic (WXR) (using WordPress one will give you strange errors regarding the thread)
  • Upload the WXR file generated
  • Wait until it's done

I've noticed that files which are less than 10mb are processed rather quickly. Anything closer to the 50mb limit will take almost 24hrs to process.

All in all, it's probably one of the best processes for migrating comments that I've used so far.

like-a-boss_o_177339

Almost as good as this guy.

Sources

 
Copyright © Twig's Tech Tips
Theme by BloggerThemes & TopWPThemes Sponsored by iBlogtoBlog