Search This Blog

Showing posts with label Maven. Show all posts
Showing posts with label Maven. Show all posts

03 January 2012

Fragments

Well, I've been needing to look into Fragments for awhile now...

First, to generate a blank project...

malachi@onyx:~/work$ mvn archetype:generate -DarchetypeCatalog=http://repository-malachid.forge.cloudbees.com/public-snapshot/archetype-catalog.xml
[INFO] Scanning for projects...
[INFO]                                                                        
[INFO] ------------------------------------------------------------------------
[INFO] Building Maven Stub Project (No POM) 1
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] >>> maven-archetype-plugin:2.2:generate (default-cli) @ standalone-pom >>>
[INFO]
[INFO] <<< maven-archetype-plugin:2.2:generate (default-cli) @ standalone-pom <<<
[INFO]
[INFO] --- maven-archetype-plugin:2.2:generate (default-cli) @ standalone-pom ---
[INFO] Generating project in Interactive mode
[INFO] No archetype defined. Using maven-archetype-quickstart (org.apache.maven.archetypes:maven-archetype-quickstart:1.0)
Choose archetype:
1: http://repository-malachid.forge.cloudbees.com/public-snapshot/archetype-catalog.xml -> org.eoti.kryten:kryten-archetype (kryten-archetype)
2: http://repository-malachid.forge.cloudbees.com/public-snapshot/archetype-catalog.xml -> org.eoti.galatea:galatea-archetype (galatea-archetype)
3: http://repository-malachid.forge.cloudbees.com/public-snapshot/archetype-catalog.xml -> org.eoti.archtest:archtest-archetype (archtest-archetype)
Choose a number or apply filter (format: [groupId:]artifactId, case sensitive contains): : 2
Define value for property 'groupId': : org.eoti.android.test.fragments
Define value for property 'artifactId': : FragmentTest
Define value for property 'version':  1.0-SNAPSHOT: :
Define value for property 'package':  org.eoti.android.test.fragments: :
Confirm properties configuration:
groupId: org.eoti.android.test.fragments
artifactId: FragmentTest
version: 1.0-SNAPSHOT
package: org.eoti.android.test.fragments
 Y: :
[INFO] ----------------------------------------------------------------------------
[INFO] Using following parameters for creating project from Archetype: galatea-archetype:1.1-SNAPSHOT
[INFO] ----------------------------------------------------------------------------
[INFO] Parameter: groupId, Value: org.eoti.android.test.fragments
[INFO] Parameter: artifactId, Value: FragmentTest
[INFO] Parameter: version, Value: 1.0-SNAPSHOT
[INFO] Parameter: package, Value: org.eoti.android.test.fragments
[INFO] Parameter: packageInPathFormat, Value: org/eoti/android/test/fragments
[INFO] Parameter: package, Value: org.eoti.android.test.fragments
[INFO] Parameter: version, Value: 1.0-SNAPSHOT
[INFO] Parameter: groupId, Value: org.eoti.android.test.fragments
[INFO] Parameter: artifactId, Value: FragmentTest
[WARNING] Don't override file /home/malachi/work/FragmentTest/src/main/android/res/values/strings.xml
[WARNING] Don't override file /home/malachi/work/FragmentTest/src/main/android/res/layout/main.xml
[INFO] project created from Archetype in dir: /home/malachi/work/FragmentTest
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 21.579s
[INFO] Finished at: Tue Jan 03 08:30:32 PST 2012
[INFO] Final Memory: 8M/245M
[INFO] ------------------------------------------------------------------------
malachi@onyx:~/work$ cd FragmentTest/
malachi@onyx:~/work/FragmentTest$ tree
.
├── pom.xml
└── src
    ├── AndroidManifest.xml
    └── main
        ├── android
        │   └── res
        │       ├── drawable
        │       │   └── icon.png
        │       ├── layout
        │       │   └── main.xml
        │       └── values
        │           └── strings.xml
        └── java
            └── org
                └── eoti
                    └── android
                        └── test
                            └── fragments
                                └── FragmentTestActivity.java

13 directories, 6 files


While it should be possible to use the Fragments with pre-HONEYCOMB (http://android-developers.blogspot.com/2011/03/fragments-for-all.html); but for now, let's just try it in the ICS emulator.

Based on the available jars in search.maven.org (http://search.maven.org/#artifactdetails|com.google.android|android|4.0.1.2|jar) let's set the version of the Android dependency in the pom to 4.0.1.2.

Also, change the <sdk><platform>10</platform></sdk> to <sdk><platform>14</platform></sdk>.

For now, comment out the maven-jarsigner-plugin as that requires setting up a keystore.
You'll also need to comment out the <sign /> portion of the android-maven-plugin.

Start up an ICS emulator and try 'mvn clean install' just to make sure we are able to grab the dependencies.
Launch the FragmentTest and you should see your hello world.

Let's move on to adding fragments... this part is derived from http://www.vogella.de/articles/Android/article.html#fragments_tutorial
I've modified their instructions quite a bit to fit into our Maven structure as well as to make things more clear (they had ListFragment extends ListFragment which tends to confuse people).

First, copy src/main/android/res/layout/main.xml to src/main/android/res/layout/detail.xml
On the new detail.xml, give the TextView an id:
android:id="@+id/ftDetailText"

Now, we'll deal the with portrait layout for FragmentTestActivity:
Replace the TextView in the layout/main.xml with:
    <fragment
        android:id="@+id/ftListFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        class="org.eoti.android.test.fragments.FTListFragment"
    />

Now, let's modify it for landscape...

Copy src/main/android/res/layout/main.xml to src/main/android/res/layout-land/main.xml [note: you will have to create the layout-land directory]

Now, for the layout-land/main.xml:
1. change the LinearLayout to Horizontal:
android:orientation="horizontal"
2. copy/paste the fragment so you now have 2 of them
3. for the first one. let's change the width to:
android:layout_width="300dip"
4. for the second one, change the id to:
android:id="@+id/ftDetailFragment"
5. Change the class to
class="org.eoti.android.test.fragments.FTDetailFragment"



Note: I changed the name of these classes so it would be obvious that these are our classes, and not some default Android implementation. Perhaps a redundant point since we also set the package name.


We'll need to have a separate activity for the detail portion in portrait mode, so let's create the layout for that.
Copy src/main/android/res/layout-land/main.xml to src/main/android/res/layout/details.xml [note: plural details not detail]

For the new details.xml:
1. change the LinearLayout back to Vertical:
android:orientation="vertical"
2. remove the ftListFragment <fragment />

We already have our main FragmentTestActivity.  We are missing our DetailActivity. Let's do some more copy/paste.
Copy FragmentTestActivity.java to DetailActivity.java (in the same directory)
Let's edit DetailActivity.java:
1. Change the class name to DetailActivity [I'd change the TAG to match]
2. change the setContentView to R.layout.details
3. add the following just after setContentView:
        Bundle extras = getIntent().getExtras();
        if (extras != null) {
            TextView view = (TextView) findViewById(R.id.ftDetailText);
            view.setText(System.getProperty(extras.getString("key")));
        }
4. Make sure to add the import android.widget.TextView;

We've defined two fragments in the layouts.  We need to create those.
First, we'll create our src/main/java/org/eoti/android/test/fragments/FTDetailFragment.java
This one is pretty simple:

package org.eoti.android.test.fragments;

import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

public class FTDetailFragment extends Fragment
{
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        return inflater.inflate(R.layout.detail, container,  false);
    }

    public void setText(String key)
    {
        ((TextView)getView().findViewById(R.id.ftDetailText)).setText(System.getProperty(key));
    }

}


Next, we'll create our src/main/java/org/eoti/android/test/fragments/FTListFragment.java
This one isn't much more difficult:

package org.eoti.android.test.fragments;

import android.*;
import android.R;
import android.app.ListFragment;
import android.content.Intent;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ListView;

import java.util.ArrayList;

public class FTListFragment extends ListFragment
{
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        ArrayList keys = new ArrayList(System.getProperties().keySet());
        ArrayAdapter adapter = new ArrayAdapter(getActivity(), R.layout.simple_list_item_1, keys);
        setListAdapter(adapter);
    }

    @Override
    public void onListItemClick(ListView l, View v, int position, long id) {
        String key = getListAdapter().getItem(position).toString();
        FTDetailFragment fragment = (FTDetailFragment)getFragmentManager().findFragmentById(org.eoti.android.test.fragments.R.id.ftDetailFragment);
        if(fragment != null)
        {
            fragment.setText(key);
        }else{
            Intent intent = new Intent(getActivity().getApplicationContext(), DetailActivity.class);
            intent.putExtra("key", key);
            startActivity(intent);
        }
    }
}

Last, but not least, we need to add the other activity to your AndroidManifest. Put this inside your <application /> tag.
          <activity android:name=".DetailActivity" />



mvn clean install

Launch the application (in portrait mode, I assume)
Select something. You should see a new activity popup with the value of the property you chose.
Hit back, and try it a few times.
When you are done, rotate the emulator using CTRL-F11.  I'm not sure about anyone else, but I have to hit it twice quickly or it won't rotate.
Now, when you select an item on the left, the answer is on the right.
When you are done playing with it, CTRL-F12 [twice in my case] to go back to portrait.

UPDATE: If you wish to use it with older devices...
First, I used the  maven-android-sdk-deployer to create the maven-compliant compat library for me.

        <dependency>
            <groupId>android.support</groupId>
            <artifactId>compatibility-v13</artifactId>
            <version>r6</version>
        </dependency>

This allows older versions of Android to use the Fragments. In my case, I tested against an Android 2.3.3 emulator.

I made no other changes to the pom.  IE: I compiled against a newer SDK and deployed against both.

Other changes to make:
  1. make FTDetailFragment extend android.support.v4.app.Fragment
  2. make FTListFragment extend android.support.v4.app.ListFragment
  3. make DetailActivity extend android.support.v4.app.FragmentActivity
  4. make FragmentTestActivity extend android.support.v4.app.FragmentActivity
  5. In FTListFragment, instead of using getFragmentManager you need to call getSupportFragmentManager.  This is part of the FragmentActivity, so you will need to do something like:
((FragmentActivity)getActivity()).getSupportFragmentManager()...

mvn clean install and it should all work... close down the 2.3.3 emulator, start up an ICS emulator and.. it will still work ;)

07 May 2010

Multi-module android project

Sometimes we may want to build two different APKs at the same time... let's examine making a multi-module android project....

F:\work> mkdir AndroidModules
F:\work> cd AndroidModules

F:\work\AndroidModules> mvn archetype:generate -DarchetypeCatalog=http://kallisti.eoti.org:8081/content/repositories/snapshots/archetype-catalog.xml
    (choose the galatea-archetype; specify a package, groupId, version, etc...)
    In my case, I am going to do org.eoti.android.module1 and Mod1
   
Now repeat for a second module
F:\work\AndroidModules> mvn archetype:generate -DarchetypeCatalog=http://kallisti.eoti.org:8081/content/repositories/snapshots/archetype-catalog.xml
    (choose the galatea-archetype; specify a package, groupId, version, etc...)
    In my case, I am going to do org.eoti.android.module2 and Mod2

Note that the two packages are different.  In my testing, anytime there were two projects with the same package, deploying the second one (even from a different window) would undeploy the first one as "conflicting".  As such, we will just keep each one to its own subpackage.

Create a top-level pom.xml (change the module names to match your selections above):
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

    <modelVersion>4.0.0</modelVersion>
    <groupId>org.eoti.galatea</groupId>
    <version>1.0-SNAPSHOT</version>
    <artifactId>AndroidModules</artifactId>
    <packaging>pom</packaging>
    <name>AndroidModules</name>
    <description>Testing a multi-module android project</description>

    <modules>
        <module>Mod1</module>
        <module>Mod2</module>
    </modules>
</project>

Open each modules pom and add a parent to match the top-level pom...  so, in my case, I put these lines just after the 'modelVersion' line in Mod1\pom.xml and Mod2\pom.xml:
    <parent>
        <groupId>org.eoti.galatea</groupId>
        <version>1.0-SNAPSHOT</version>
        <artifactId>AndroidModules</artifactId>
    </parent>


Deploy your new project to the emulator (mvn install). You should see both your projects (Mod1 and Mod2 above) available in the emulator with slightly different Hello World text.  From the top level, doing mvn install will install both and mvn clean will remove both.

MyActivity renamed

For anyone following the tutorials in this blog, you are used to the archetype creating MyActivity.java.  As I was writing up the tutorial for a multi-module android project, I realized that would cause conflicts when you have more than 1 project.  As such, I have changed the filename to ${artifactId}Activity.java...

So, if you created the project as such:
groupId: org.eoti.galatea
artifactId: SomeTest

Then the actual source file will now be:
SomeTest\src\main\java\org\eoti\galatea\SomeTestActivity.java
instead of
SomeTest\src\main\java\org\eoti\galatea\MyActivity.java


Hope that helps.

04 May 2010

Moving existing samples to Maven: GestureBuilder

So I was intrigued by the Gesture API, but did not want to install Eclipse.  What to do?

First, let's create a blank project:
mvn archetype:generate -DarchetypeCatalog=http://kallisti.eoti.org:8081/content/repositories/snapshots/archetype-catalog.xml

Archetype: galatea
GroupId: com.android.gesture.builder
ArtifactId: GestureBuilder
Version: 1.0-SNAPSHOT

  • Remove the src/main/android/res/layout/main.xml
  • Remove the src/main/java/com/android/gesture/builder/MyActivity.java

Now we will copy some files over from our sample directory.  For me, that is at C:\java\android-sdk-windows\samples\android-7\GestureBuilder. We will call that directory $SRC and our top-level maven directory $DEST.

  • Replace $DEST/src/AndroidManifest.xml with $SRC/AndroidManifest.xml
  • Copy $SRC/src/* into $DEST/src
  • Copy $SRC/src/* into $DEST/src/main/java
  • Copy $SRC/res/* into $DEST/src/android/res
  • Copy $SRC/res/* into $DEST/src/main/android/res

Make sure your emulator is running then, from your command-line (in $DEST) deploy the app (mvn clean install).  The app will run in the emulator and save the gestures onto the emulated SD card*.


* What? You didn't have an emulated SD card? Oh.  This is how I created my emulator image:
android create avd -n Device1 -t 11 -c 1000M

21 April 2010

Galatea - A Maven/Android Archetype



After my experience writing an archetype for Red Dwarf; I decided to do the same thing for Android.  This archetype is based on the maven-android-plugin. I'd like to thank Hugo for helping me get the configuration correct.   So let's get started.

As before...
F:\work>mvn archetype:generate -DarchetypeCatalog=http://kallisti.eoti.org:8081/content/repositories/snapshots/archetype-catalog.xml
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building Maven Stub Project (No POM) 1
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-archetype-plugin:2.0-alpha-5-SNAPSHOT:generate (default-cli) @ standalone-pom ---
[INFO] Generating project in Interactive mode
[INFO] No archetype defined. Using maven-archetype-quickstart (org.apache.maven.archetypes:maven-archetype-quickstart:1.0)
Choose archetype:
1: http://kallisti.eoti.org:8081/content/repositories/snapshots/archetype-catalog.xml -> ardor3d-archetype (null)
2: http://kallisti.eoti.org:8081/content/repositories/snapshots/archetype-catalog.xml -> kryten-archetype (null)
3: http://kallisti.eoti.org:8081/content/repositories/snapshots/archetype-catalog.xml -> galatea-archetype (null)
4: http://kallisti.eoti.org:8081/content/repositories/snapshots/archetype-catalog.xml -> kryten-archetype (null)
Choose a number: : 3 ((use whatever number is assigned to galatea))
[INFO] snapshot org.eoti.galatea:galatea-archetype:1.0-SNAPSHOT: checking for updates from eoti-public
[WARNING] *** CHECKSUM FAILED - Checksum failed on download: local = 'c4696ce0aeee29d53523e63f02230636518f8ad2'; remote = 'deb2361707aa66b51d4055be6452e818b9283be2' - RETRYING
[WARNING] *** CHECKSUM FAILED - Checksum failed on download: local = 'c4696ce0aeee29d53523e63f02230636518f8ad2'; remote = 'deb2361707aa66b51d4055be6452e818b9283be2' - IGNORING
Define value for property 'groupId': : org.eoti.android.test
Define value for property 'artifactId': : TestAndroid
Define value for property 'version':  1.0-SNAPSHOT: :
Define value for property 'package':  org.eoti.android.test: :
Confirm properties configuration:
groupId: org.eoti.android.test
artifactId: TestAndroid
version: 1.0-SNAPSHOT
(hit enter)
package: org.eoti.android.test
(hit enter)
 Y: : (hit enter)
[WARNING] Don't override file F:\work\TestAndroid\src\main\android\res\values\strings.xml
[WARNING] Don't override file F:\work\TestAndroid\src\main\android\res\layout\main.xml
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1:02.860s
[INFO] Finished at: Sun Apr 18 19:58:56 PDT 2010
[INFO] Final Memory: 6M/11M
[INFO] ------------------------------------------------------------------------

F:\work> cd TestAndroid
The resulting files:

F:\work\TestAndroid>tree /F
Folder PATH listing for volume WINDOWS
Volume serial number is 9827-5E66
F:.
│   pom.xml

└───src
    │   AndroidManifest.xml
    │
    └───main
        ├───android
        │   └───res
        │       ├───drawable
        │       │       icon.png
        │       │
        │       ├───layout
        │       │       main.xml
        │       │
        │       └───values
        │               strings.xml
        │
        └───java
            └───org
                └───eoti
                    └───android
                        └───test
                                MyActivity.java *

Make sure you have your emulator running (emulator -avd Device1, in my case):
F:\work\TestAndroid>mvn install
This will build, package and deploy it to your emulator.
 
F:\work\TestAndroid>mvn clean
In addition to cleaning your build tree, that will undeploy your app from the emulator.

Update: The repository has been moved from kallisti.eoti.org to repository-malachid.forge.cloudbees.com.  As such, the new command line to launch the archetype is:
F:\work>mvn archetype:generate -DarchetypeCatalog=http://repository-malachid.forge.cloudbees.com/public-snapshot/archetype-catalog.xml