Friday, July 5, 2013

Initializing An AngularJS App with Oauth2 and Google Endpoints (Hackish)

So - I originally wrote this back around July 4th but wasn't really happy with it (I ended up writing it down in Florida, away from my actual work computer).  Recently, our last Android app (CoinPrice) got shut down over a cease and desist (which I was stressing about and took some time to figure that one out) and Exposure101 as a whole kind of fell apart.  That being said, I still love technology and plan to update this blog occasionally whenever I get to play with something new.

So - back when I wrote this I had to do something I considered to be kind of kludge to get an AngularJS app initialized with your Google account.  The snag I hit in doing this is as follows: Google provides a demo of bootstrap your app using the endpoints and oauth2 JavaScript libraries - but it doesn't show how to integrate your login status into the AngularJS framework (e.g. putting it in a variable on your controller's scope such that you can hide or display certain things on your page).  Now, I'm a server side Java person, I've been really digging being able to write client stuff recently with AngularJS & Bootstrap, but I am by no means a JavaScript expert.  I started reading Effective JavaScript and have been brushing up on it for work, but there may be better ways out there of doing this.

So - here's what I ended up doing:

Firstly, my goal here was to have your data show up immediately if you were already logged into your account - otherwise your data should show up after you login.

In order to bootstrap Google's Oauth library you have to have the following line in your index.html - when the script has finished loading it will run the init function you give it in the onload parameter.

<script src="https://apis.google.com/js/client.js?onload=init"></script>

Now you actually need to implement the init method somewhere in your JavaScript.  I put mine in a script tag in my index.html as shown below:

<!-- google user authentication -->
<script>
var init = function() {
  var $injector = angular.injector(['ng', 'exposure101.services', 'exposure101.lifelogger']);
  var InitializationService = $injector.get('InitializationService');
  var AuthenticationService = $injector.get('AuthenticationService');
  var EventService = $injector.get('EventService');
  InitializationService.initialize().then(function() {
    AuthenticationService.login(true).then(function(authenticationModel) {
      var $scope = angular.element('body').scope();
      $scope.$apply(function() {
        $scope.authenticationModel = authenticationModel;
      });
      EventService.loadEvents().then(function(events) {
        $scope.$apply(function() {
          $scope.events = events;
        });
      });
    });
  });
};
</script>
<script src="https://apis.google.com/js/client.js?onload=init"></script>

Now there's some stuff here you need to understand.

  • Apparently on every DOM element in Angular there exists an ng-scope that you have access to - you can get access to this scope by using Angular's element selector and calling scope() on it (When you would do this outside of this example, I'm not really sure, but you do have the option available to you).  
  • Services/Factories are normally singleton instances - you can persist state across Controllers by keeping the state in the Service/Factory objects and injecting them into your Controllers.   Normally, using Angular's Dependency Injection system, you only have to worry about single instances of your Services/Factories.  Now that this is drilled in, when you use angular.injector ($injector.get(...) in our case) it will create a new instance of the Service/Factory, this instance is not the one that will be injected into your controller (by default) through Angular's Dependency Injection system.  So - be careful with these Services/Factories and don't persist anything in them you plan to use in your controller (think of them almost like Stateless Session beans)
Now - all that being said, here's the rest of the code starting with the Authentication Model:

angular.module('exposure101.models')
.factory('AuthenticationModel', function() {
  'use strict';

  var authenticationModel = {
    isLoggedIn: false,
    authenticationToken: {}
  };

  return authenticationModel;
});


Here's The Authentication Service:

angular.module('exposure101.services')
.service('AuthenticationService', function($q, $rootScope, AuthenticationModel) {
  'use strict';

  var authenticationToken = {};
  var deferred = {}; // hackity hack hack

  this.login = function(initialLogin) {
    deferred = $q.defer();
    doLogin(initialLogin);
    return deferred.promise;
  };

  var doLogin = function(mode) {
    var opts = {
      // localhost client id (make sure this is set to port 9000 in google api console)
      client_id: 'your_client_id.apps.googleusercontent.com',
      scope: 'https://www.googleapis.com/auth/userinfo.email',
      immediate: mode,
      response_type: 'token id_token'
    };
    gapi.auth.authorize(opts, handleLogin);
  };

  var handleLogin = function() {
    gapi.client.oauth2.userinfo.get().execute(function(response) {
      if (!response.code) {
        authenticationToken = gapi.auth.getToken();
        authenticationToken.access_token = authenticationToken.id_token;
        AuthenticationModel.isLoggedIn = true;
        AuthenticationModel.authenticationToken = authenticationToken;
        $rootScope.$apply(function() {
          deferred.resolve(AuthenticationModel);
        });
      }
    });
  };
});


Here's The Initialization Service:

angular.module('exposure101.services')
.service('InitializationService', function($q, $rootScope) {
  'use strict';

  this.initialize = function() {
    var deferred = $q.defer();
    var apisToLoad = 2;

    var loginCallback = function() {
      if (--apisToLoad === 0) {
        $rootScope.$apply(function() {
          // console.log('finished loading up client libraries - should be resolving');
          deferred.resolve();
        });
      }
    };

    gapi.client.load('events', 'v1', loginCallback, 'http://localhost:8888/_ah/api');
    gapi.client.load('oauth2', 'v2', loginCallback);

    return deferred.promise;
  };
});

And lastly here's the controller:

angular.module('exposure101.lifelogger')
.controller('LifeLoggerController', function($scope, AuthenticationModel, AuthenticationService, EventService) {
  'use strict';

  $scope.authenticationModel = AuthenticationModel;

  $scope.categories = [];
  $scope.events = [];
  $scope.labels = [];

  ...

  $scope.login = function() {
    AuthenticationService.login(false).then(function(authenticationModel) {
      $scope.authenticationModel = authenticationModel;
      EventService.loadEvents().then(function(events) {
        $scope.events = events;
      });
    });
  };
});


Now - recently I got a comment asking how this was different than the directive on this site: https://github.com/sirkitree/angular-directive.g-signin/blob/master/google-plus-signin.js.  This directive is actually a solid implementation of what I was trying to do - instead of going through all the setup described in this blog this actually works very well (and with a nicer interface including a more standardized Google login button).  I'm going to modify this directive a bit to make it more to my liking and I'll post the code in a new article on this blog.

Monday, April 22, 2013

An example AngularJS app with Google App Engine RESTful Web Services

So I'm fortunate enough to work with people that are constantly pushing for us to learn new technologies.  I released a pretty niche Android app maybe 2 weeks ago for Exposure101 - that aside I've had to start learning AngularJS at my real job over the last week.  Over the last year or two I've been working pretty heavily with GWT - and in all honesty had never really done any type of JavaScript - just doing Angular for these last few days has completely sold me on it.

I put together a pretty simple App to show the basics of Angular and how to setup a simple Web Service environment.  I'm a huge fan of Google App Engine - it's really simple to use and you can stand up an entire backend in 20 minutes once you've done it before.  I'm gonna start with the server first and move on to Angular after.

Before we start you'll need the following libraries (versions are just the ones I used) - Make a new App Engine project (for this example my project name is "angular-demo" with the package "com.exposure101.angular.demo") and copy them into your war/WEB-INF/lib directory.


I try to avoid XML configurations and Guice has a really nice way of helping you to setup your server.  The first thing to do is open up your web.xml, clear everything between the web-app tags and setup the Guice Filter and Servlet Context Listener as follows:

 <!-- Servlets -->
  <listener>
    <listener-class>com.exposure101.angular.demo.server.AngularDemoServletContextListener</listener-class>
  </listener>

  <filter>
    <filter-name>guiceFilter</filter-name>
    <filter-class>com.google.inject.servlet.GuiceFilter</filter-class>
  </filter>

  <filter-mapping>
    <filter-name>guiceFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

  <!-- Default page to serve -->
  <welcome-file-list>
    <welcome-file>Angular_demo.html</welcome-file>
  </welcome-file-list>

  <!-- generated by app engine - if you delete it it will come back -->
  <servlet>
    <servlet-name>SystemServiceServlet</servlet-name>
    <servlet-class>com.google.api.server.spi.SystemServiceServlet</servlet-class>
    <init-param>
      <param-name>services</param-name>
      <param-value />
    </init-param>
  </servlet>
  <servlet-mapping>
    <servlet-name>SystemServiceServlet</servlet-name>
    <url-pattern>/_ah/spi/*</url-pattern>
  </servlet-mapping>

The web.xml and the next 3 classes are basically boilerplate server side Guice. If you're not familiar this I would highly advise you go check it out - it's pretty awesome.  At a really high level here's what each one does:
  • ServletContextListener - provides all the modules you plan to inject (in our case it will only be 2)
  • ServletModule - this is now your web.xml - you define your servlets and filters in here
  • AbstractModule - this binds the classes that will be injected through your app.

Here's the ServletContextListener:

package com.exposure101.angular.demo.server;

import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.servlet.GuiceServletContextListener;

public class AngularDemoServletContextListener extends GuiceServletContextListener {

  @Override
  protected Injector getInjector() {
    return Guice.createInjector(new AngularDemoModule(), new AngularDemoServletModule());
  }
}


The ServletContextListener's pretty simple - Here's the ServletModule:

package com.exposure101.angular.demo.server;

import java.util.HashMap;
import java.util.Map;

import com.google.inject.servlet.ServletModule;
import com.sun.jersey.guice.spi.container.servlet.GuiceContainer;

public class AngularDemoServletModule extends ServletModule {

  @Override
  protected void configureServlets() {
    final Map<String, String> params = new HashMap<String, String>();
    params.put("javax.ws.rs.Application",
        "com.exposure101.angular.demo.server.AngularDemoRestApplication");
    serve("/rest/*").with(GuiceContainer.class, params);
  }
}


The ServletModule does a little bit more.  REST Web Service classes (I've been calling them Providers for forever - I've seen people call them Providers and Services - I'm not sure if there's a standard naming convention - if I broke it I'm sorry) are kicked off as Singletons during deployment via an Application class.  Whenever a call is made to our server's rest path it will be served with the Application class passed in above.  Since we're serving it up through the GuiceContainer we can inject any dependencies into it through our implemented AbstractModule:

package com.exposure101.angular.demo.server;

import com.exposure101.angular.demo.server.dao.Dao;
import com.exposure101.angular.demo.server.dao.PersonDao;
import com.exposure101.angular.demo.server.model.Person;
import com.exposure101.angular.demo.server.services.AbstractProvider;
import com.exposure101.angular.demo.server.services.PersonProvider;
import com.google.inject.AbstractModule;
import com.google.inject.TypeLiteral;

public class AngularDemoModule extends AbstractModule {

  @Override
  protected void configure() {
    bind(new TypeLiteral<Dao<Person>>() {}).to(PersonDao.class);
    bind(new TypeLiteral<AbstractProvider<Person>>() {}).to(PersonProvider.class);
  }
}

These classes haven't been implemented yet - but basically any time an @Inject is used for a Dao<Person> or an AbstractProvider<Person>, the bound PersonDao or PersonProvider will be injected for you.  For this project the Guice is definitely overkill - but I've used it in every project I've worked on in the last year and I'm really trying to push it on you.

The last important class here is the actual Application class:

package com.exposure101.angular.demo.server;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

import javax.inject.Inject;
import javax.ws.rs.Path;
import javax.ws.rs.core.Application;

import com.exposure101.angular.demo.server.model.Person;
import com.exposure101.angular.demo.server.services.AbstractProvider;

@Path("/rest")
public class AngularDemoRestApplication extends Application {

  @Inject
  AbstractProvider<Person> personService;
  
  @Override
  public Set<Object> getSingletons() {
    return new HashSet<Object>(Arrays.asList(new Object[] { personService }));
  }
}


Since there's only 1 provider this class seems pretty trivial, if there were more you would bind them in your Module and inject them in the same way.  Now that the boilerplate code is up you're just going to build out the rest of the backend.  All you have to do is create the Person model, a DAO to interface with the datastore (Objectify is hands down The best framework for App Engine's datastore) and the actual Web Service Provider for your Angular app to query against.  I'm just going to lay these out in bulk here.

Here's the actual Person Model:

package com.exposure101.angular.demo.server.model;

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.PrePersist;

import com.googlecode.objectify.annotation.Indexed;
import com.googlecode.objectify.annotation.Unindexed;

@Entity
public class Person {

  @Id
  private Long id;
  
  @Unindexed
  private Integer version;
  
  @Indexed
  private String firstName;
  
  @Indexed
  private String lastName;
  
  @Indexed
  private String ssn;
  
  @Indexed
  private String gender;
  
  @Indexed
  private String dateOfBirth;
  
  public Person() {
  }

  public Long getId() {
    return id;
  }

  public Integer getVersion() {
    return version;
  }

  public String getFirstName() {
    return firstName;
  }

  public String getLastName() {
    return lastName;
  }

  public String getSsn() {
    return ssn;
  }

  public String getGender() {
    return gender;
  }

  public String getDateOfBirth() {
    return dateOfBirth;
  }

  public void setId(Long id) {
    this.id = id;
  }

  public void setVersion(Integer version) {
    this.version = version;
  }

  public void setFirstName(String firstName) {
    this.firstName = firstName;
  }

  public void setLastName(String lastName) {
    this.lastName = lastName;
  }

  public void setSsn(String ssn) {
    this.ssn = ssn;
  }

  public void setGender(String gender) {
    this.gender = gender;
  }

  public void setDateOfBirth(String dateOfBirth) {
    this.dateOfBirth = dateOfBirth;
  }
  
  @PrePersist
  public void updateVersion() {
    if (version == null) {
      version = Integer.valueOf(0);
    }
    version++;
  }
}



Here's The Objectify classes:

The DAO interface:

package com.exposure101.angular.demo.server.dao;

import java.util.List;

import com.googlecode.objectify.Key;

public interface Dao<T> {

  T find(Long id);

  T find(String id);

  T find(Key<T> key);

  Key<T> key(T t);

  List<T> findAll();

  List<T> findAll(List<Key<T>> keys);

  List<Key<T>> key(List<T> list);

  T persist(T t);

  void delete(T t);
}


The Abstract DAO:

package com.exposure101.angular.demo.server.dao;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import com.exposure101.angular.demo.server.model.Person;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.ObjectifyService;
import com.googlecode.objectify.util.DAOBase;

public abstract class AbstractDao<T> extends DAOBase implements Dao<T> {

  static {
    ObjectifyService.register(Person.class);
  }
  
  private final Class<T> clazz;
  
  public AbstractDao(Class<T> clazz) {
    this.clazz = clazz;
  }
  
  @Override
  public T find(Long id) {
    return ObjectifyService.begin().find(clazz, id);
  }
  
  @Override
  public T find(String id) {
    return ObjectifyService.begin().find(clazz, id);
  }
  
  @Override
  public T find(Key<T> key) {
    return ObjectifyService.begin().find(key);
  }
  
  @Override
  public Key<T> key(T t) {
    return ObjectifyService.factory().getKey(t);
  }
  
  @Override
  public List<T> findAll() {
    return ObjectifyService.begin().query(clazz).list();
  }
  
  @Override
  public List<T> findAll(List<Key<T>> keys) {
    if (keys == null) {
      return null;
    }
    final Map<Key<T>, T> map = ObjectifyService.begin().get(keys);
    final List<T> list = new ArrayList<T>();
    for (final T t : map.values()) {
      list.add(t);
    }
    return list;
  }
  
  @Override
  public List<Key<T>> key(List<T> list) {
    if (list == null) {
      return null;
    }
    final List<Key<T>> keys = new ArrayList<Key<T>>(list.size());
    for (final T t : list) {
      final Key<T> key = ObjectifyService.factory().getKey(t);
      keys.add(key);
    }
    return keys;
  }
  
  @Override
  public T persist(T t) {
    ObjectifyService.begin().put(t);
    return t;
  }
  
  @Override
  public void delete(T t) {
    ObjectifyService.begin().delete(t);
  }
}

And the actual Person DAO implementation:

package com.exposure101.angular.demo.server.dao;

import com.exposure101.angular.demo.server.model.Person;

public class PersonDao extends AbstractDao<Person> {

  public PersonDao() {
    super(Person.class);
  }
}

And here's the Provider classes:
The Abstract Provider
package com.exposure101.angular.demo.server.services;

import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;

import com.exposure101.angular.demo.server.dao.Dao;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;


public abstract class AbstractProvider<T> {

  @Inject
  private Dao<T> dao;
  
  private final Class<T> clazz;
  
  public AbstractProvider(Class<T> clazz) {
    this.clazz = clazz;
  }
  
  @GET
  @Path("/get/{id}")
  public String get(@PathParam("id") String id) {
    final Gson gson = new GsonBuilder().setPrettyPrinting().create();
    if ("all".equals(id)) {
      return gson.toJson(dao.findAll().toArray());
    } else {
      return gson.toJson(dao.find(Long.valueOf(id)));
    }
  }
  
  @POST
  @Path("/persist")
  public void persist(String body) {
    final Gson gson = new GsonBuilder().setPrettyPrinting().create();
    final T t = gson.fromJson(body, clazz);
    dao.persist(t);
  }
}


And the Person Provider Implementation

package com.exposure101.angular.demo.server.services;

import javax.ws.rs.Path;

import com.exposure101.angular.demo.server.model.Person;

@Path("/person")
public class PersonProvider extends AbstractProvider<Person> {

  public PersonProvider() {
    super(Person.class);
  }
}

Now that this is all up we can test the Web Services.  You should be able to start your server in debug mode and put a breakpoint on the first line in the GET method of your AbstractProvider class.  Once the server comes up try hitting the following link (assuming you didn't change the default port of 8888): http://127.0.0.1:8888/rest/person/get/all.

Time to start the Angular side.


Create a new Angular project using the following commands:

npm install generator-angular generator-karma
yo angular
bower install angular-ui
npm install grunt-proxy

The first thing you'll need to do once you'e finished creating the project is to setup a proxy in your Gruntfile.  By default your Angular Server will run on port 9000 - if you try to access an App Engine Web Service on port 8888 you will get a cross domain error.  In the root of your Angular App open up the Gruntfile.js and edit the following:

  • Before the grunt.initConfig call load the grunt proxy server
  • Create the proxy on an arbitrary port (not 8888 or 9000) and setup the routes as shown below
  • Edit the open task to load your Angular app on the correct port


  grunt.loadNpmTasks('grunt-proxy');
  grunt.initConfig({
    yeoman: yeomanConfig,
    watch: {
      ...
    },
    proxy: {
      proxy1: {
        options: {
          port : 8050,
          host : 'localhost',
          router : {
            'localhost/rest/*' : 'localhost:8888',
            'localhost' : 'localhost:9000'
          }
        }
      }
    },
    connect: {
       ...
    },
    open: {
      server: {
        url: 'http://localhost:<%= proxy.proxy1.options.port %>'
      }
    },
    ...
  });    


From now on, when you start your Grunt server, instead of typing in "grunt server" you will need to call "grunt proxy server".

The first thing I did was build up the views since that's an easy place to start. Under your app's views directory create a file called "CreatePerson.html".  Once you copy in the HTML below it will look as follows:



<div class="hero-unit">
  <div class="row">
    <div class="span2">First Name:</div>
    <div class="span3">
      <input type="text" ng-model="person.firstName"></input>
    </div>
  </div>
  <div class="row">
    <div class="span2">Last Name:</div>
    <div class="span3">
      <input type="text" ng-model="person.lastName"></input>
    </div>
  </div>
  <div class="row">
    <div class="span2">SSN:</div>
    <div class="span3">
      <input type="text" ng-model="person.ssn"></input>
    </div>
  </div>
  <div class="row">
    <div class="span2">Gender:</div>
    <div class="span3">
      <input type="text" ng-model="person.gender"></input>
    </div>
  </div>
  <div class="row">
    <div class="span2">Date of Birth:</div>
    <div class="span3">
      <input type="text" ng-model="person.dateOfBirth"></input>
    </div>
  </div>
  <div class="row">
    <div class="span2">
      <button class="btn" ng-click="doSubmit()">Submit</button>
    </div>
  </div>
</div>


There's some important things to cover before continuing.  Currently there is no Controller for this View, that doesn't matter right now but it will.  In Angular, each View is mapped to a Controller and the two communicate through a Scope object.  There's some nice visualizations for this on Angular's site but I found it made a lot more sense to me after just implementing it.  It's important to note (and I'll cover it in more detail soon) that everything in Angular is injected - you don't have to worry about instantiating these Controllers or Scope objects.

You'll notice in the above HTML there's a bunch of attributes that start with "ng".  These are Angular's built in directives.  You can think of directives as HTML tasks - they encapsulate both HTML and JavaScript in order to do a certain task.  Let's say in your HTML code you want to turn an input into a lookup widget such that when you begin typing into it, it queries a server and creates a typeahead to display the results.  You can write all the necessary JavaScript and HTML in a directive called "lookupWidget" and simply create your input in HTML along the lines of
<input type="text" data-lookup-widget="lookupResults"/>.

In this file particularly you'll notice the "ng-model" directives.  There is no Controller currently but this directive is actually binding the fields of the "person" object in the Scope to the text value of the inputs.  If that doesn't quite click yet I'm sure it will a few paragraphs down.  It is really important to note that you do not have to explicitly define any of your objects in your Controller, any object used/defined in the View will be present in the Scope.

Now I'm going to setup the actual Controller for this View.

In your app/script/controllers directory, create a file called CreatePersonController.js.  (Whenever you create a new JavaScript file remember to import it in your index.html file)  All this class is going to be responsible for is taking the person object from the view and persisting it to the server.  The code to do this is shown below:

angular.module('angular.demoApp')
  .controller('CreatePersonController', function ($scope, $routeParams, PersonService) {
    'use strict';

    $scope.doSubmit = function() {
        PersonService.persist($scope.person);
    };
});


The syntax on this is a little funky at first but I'll try and break it down.  The first 2 lines are basically "constructing" (not really, more like getting as far as I understand) the Controller and injecting the $scope and $routeParams objects directly from Angular.  As far as I understand, anything injected into the Controller's constructor function starting with a '$' is injected by Angular.  You're also injecting a PersonService object which is not prefaced with a '$' which means it was created by the developer. (This doesn't exist yet but you're going to create it here in a second).  Since everything is injected, any time you find yourself needing an Angular resource you can simply pass it in the constructor function and the Controller will have access to it magically.

You can see some binding from the View to the Controller here as well.  The "$scope.doSubmit()" function will be called on click from the View's submit button, and the person that we built up in the View is accessed in the doSubmit function as $scope.person.  The Controller and View share data through the Scope object, the default scope for the Controller is passed in as $scope.  I'm sure I'll find a better way to word this tomorrow and change up the article.

The last thing to do here is to create the actual PersonService object.  This will be responsible for actually making the Web Service call.  In your app/scripts/services directory create a file called PersonService.js.

angular.module('PersonServices', ['ngResource']).
  factory('PersonService', function($resource) {
    'use strict';

    /* the actual person json object */
    var person = {};

    /* the actual service to hit the server */
    var personService = {};

    var getResource = $resource('/rest/person/get/:id', {id:'@id'}, {
      get: {method:'GET'},
      query: {method:'GET', isArray:true}
    });

    var postResource = $resource('/rest/person/persist', {}, {
      persist: {method:'POST'}
    });

    /* retrieve the medical details from the server for the medical details htid */
    personService.getPerson = function(id) {
      person = getResource.get({id:id});
      return person;
    };

    personService.getPersonArray = function() {
      person = getResource.query({id:'all'});
      return person;
    };

    personService.persist = function(person) {
      postResource.persist(person);
    };

    return personService;
});


This is the actual PersonService Factory.  Angular creates Factories one time only, from now on anything needing access to the Person web services can inject this and access them.  This service object makes use of something in Angular known as a promise - I don't really have anything in the Java world to compare this too but you can kind of think of this similar to a Proxy object (but not really).  When your Controller calls the getPerson() or getPersonArray() methods, a promise object is returned while the resource goes off and makes the call to the server asynchronously.  Once the call finishes, it magically populates the promise object with the data from the server.  So, in your View classes you can use the promise object in each of your fields - when the call to the server starts they will all be blank, when the call to the server is finished your View will populate automatically.

As an implementation detail, in the 'PersonServices' Module constructor you have to pass in the 'ngResource' string as a parameter - this is absolutely mandatory for Angular's $resource object to work.  I think of this similar to a java import.

The last thing you have to do is edit the app.js file to include the CreatePersonView and CreatePersonController.

angular.module('angular.demoApp', ['PersonServices'])
  .config(function ($routeProvider) {

    'use strict';
    $routeProvider
      .when('/create', {
        templateUrl: 'views/CreatePerson.html',
        controller: 'CreatePersonController'
      })
});


Now you can create a new Person in the Create View:



And verify that they were created using the Web Service in a browser.




For anybody that's interested in the read section of this, the HTML and Controller code is below.

<div class="hero-unit">
  <div class="row">
    <div class="span2">First Name:</div>
    <div class="span3">{{person.firstName}}</div>
  </div>
  <div class="row">
    <div class="span2">Last Name:</div>
    <div class="span3">{{person.lastName}}</div>
  </div>
  <div class="row">
    <div class="span2">SSN:</div>
    <div class="span3">{{person.ssn}}</div>
  </div>
  <div class="row">
    <div class="span2">Gender:</div>
    <div class="span3">{{person.gender}}</div>
  </div>
  <div class="row">
    <div class="span2">Date of Birth:</div>
    <div class="span3">{{person.dateOfBirth}}</div>
  </div>
</div>

angular.module('angular.demoApp')
  .controller('ViewPersonController', function ($scope, $routeParams, PersonService) {
    'use strict';

    var id = $routeParams.id;
    $scope.person = PersonService.getPerson(id);
});


And the last thing you'd have to do is add the following lines to the app.js file
      .when('/person/:id', {
        templateUrl: 'views/ViewPerson.html',
        controller: 'ViewPersonController'
      })

And this will be your end result:



Saturday, January 19, 2013

Simple Skeleton Setup for a 2D Google PlayN Game


Over the last few months one of my good friends, Trevor Marsh, and I have been trying to develop a ridiculously intense, overly difficult, seizure inducing Contra/Ikaruga reminiscent 2D game for Android.

We have developed (still in the process of developing really) a level editor - Ubik. The goal of Ubik was to make a level editor robust enough to build a large majority of 2D games.  You simply build your layers and levels and export them as JSON such that, for the most part, the entire engine can run off of the data exported in the JSON.

Currently Ubik is 2 projects - Ubik and Ubik-Shared.  The whole project was designed with the intent of being able to put multiple front ends on it - currently the front end is done in Swing, but it would be really cool to have a Web version hosted on App Engine that anybody could use.  The project was written using MVP with all the Models, View interfaces and Presenter logic contained in the Shared project.  The Swing implementation uses simple adapter interfaces (very similar to GWT's HasText and all that) such that if we made an Ubik-Web all we should have to do is implement the Views and an export Servlet to get at least a basic version of it working.

Here are 2 simple screenshots of its current state:



We plan on open sourcing the level editor once we actually release the game and have it usable (currently we've only implemented what we need).  If you are at all interested in the source code for any of this feel free to email me at sean.exposure@gmail.com for a zip distribution.  (The code is fairly clean but like I said, we've only implemented exactly what we need for now).

As of right now - I'd like to say our game engine is roughly 80% done - we have no music or art so... that's kind of a buzzkill... but I'll burn that bridge when I get there. The heart of the game is nothing more than a simple state pattern which is responsible for loading up everything from JSON, caching most of it to be used in the game, and also simply going from splash screens, through menus, to the real game itself.

Here's a simple screenshot of the demo level with the sweet Mario graphics shown above being played in PlayN.


Now, I'm just going to post a lot of the source code I've used to get the game up to this point.

This demo is using Google Guice to set everything up. The game kicks off by starting a Controller and a State Manager - they are listed below.

This is the actual entry point.  All this does is spin up the Controller then delegate everything to it.

package com.exposure101.playn.runrun;

import playn.core.Game;

import com.google.inject.Guice;
import com.google.inject.Injector;

public class RunRun implements Game {

  private final Injector injector;
  private final Controller controller;

  public RunRun() {
    injector = Guice.createInjector(new RunRunModule());
    controller = injector.getInstance(Controller.class);
  }

  @Override
  public void init() {
    controller.initialize();
  }

  @Override
  public void update(float delta) {
    controller.update(delta);
  }

  @Override
  public void paint(float alpha) {
    controller.paint(alpha);
  }

  @Override
  public int updateRate() {
    return 25;
  }
}




The controller class is given here.  All this guy really does is kick off the state machine.

This Controller is dependent on a Context Object which is really nothing more than a glorified HashMap.  This is basically used such that - let's say you select a Level from a specific World (think Angry Birds).  If you exit back to the menu from a given World, the World will be stored in the Context such that the background of the Menu can reflect this (think Angry Birds Seasons).

package com.exposure101.playn.runrun;

import static com.exposure101.playn.runrun.context.ContextKeys.FPS;

import javax.inject.Inject;
import javax.inject.Singleton;

import playn.core.PlayN;

import com.exposure101.playn.runrun.context.Context;
import com.exposure101.playn.runrun.shared.Initializable;
import com.exposure101.playn.runrun.shared.Paintable;
import com.exposure101.playn.runrun.shared.Updatable;
import com.exposure101.playn.runrun.state.StateManager;

@Singleton
public class Controller implements Initializable, Paintable, Updatable {

  private final Context context;
  private final StateManager stateManager;

  @Inject
  public Controller(Context context, StateManager stateManager) {
    this.context = context;
    this.stateManager = stateManager;
  }

  @Override
  public void initialize() {
    context.set(FPS, Integer.valueOf(30));
  }

  @Override
  public void update(float delta) {
    if (stateManager.getCurrentState() == null) {
      stateManager.goTo(stateManager.getExposure101SplashScreenState());
    }
    stateManager.getCurrentState().update(delta);
  }

  @Override
  public void paint(float alpha) {
    if (stateManager.getCurrentState() != null) {
      stateManager.getCurrentState().paint(alpha);
    } else {
      PlayN.log().error("current state is null");
    }
  }

  public Context getContext() {
    return context;
  }
}



The StateManager is dependent on the State interface, this is fairly simple. 

package com.exposure101.playn.runrun.state;

import com.exposure101.playn.runrun.shared.Destroyable;
import com.exposure101.playn.runrun.shared.HandlesError;
import com.exposure101.playn.runrun.shared.HasKeyboardListener;
import com.exposure101.playn.runrun.shared.Initializable;
import com.exposure101.playn.runrun.shared.Paintable;
import com.exposure101.playn.runrun.shared.Updatable;

public interface State extends Destroyable, Updatable, Paintable, Initializable,
    HasKeyboardListener, HandlesError {
}


The Abstract State is also fairly simple.

package com.exposure101.playn.runrun.state;

import playn.core.Keyboard;

import com.google.inject.Inject;

public abstract class AbstractState implements State {

  protected final StateManager stateManager;

  @Inject
  public AbstractState(StateManager stateManager) {
    this.stateManager = stateManager;
  }

  @Override
  public Keyboard.Listener getKeyboardListener() {
    return null;
  }

  @Override
  public void reportError(Throwable error) {
    stateManager.reportError(error);
  }
}





The StateManager implementation is given below - this is pretty stripped down.  If you notice in the goTo(State to) method that before the State is switched, the current State is destroyed.  Any Group Layers or Layers added to the Root Layer should be removed - any other resources should definitely be freed up in this method.

package com.exposure101.playn.runrun.state;

import javax.inject.Inject;

import com.exposure101.playn.runrun.context.Context;
import com.exposure101.playn.runrun.state.level.LevelState;
import com.exposure101.playn.runrun.state.level.LevelStateBinding;
import com.exposure101.playn.runrun.state.loading.LevelLoadingStateBinding;
import com.exposure101.playn.runrun.state.loading.LoadingState;
import com.exposure101.playn.runrun.state.loading.LoadingStateBinding;
import com.exposure101.playn.runrun.state.menu.LevelGroupMenuStateBinding;
import com.exposure101.playn.runrun.state.menu.MainMenuStateBinding;
import com.exposure101.playn.runrun.state.menu.MenuState;
import com.exposure101.playn.runrun.state.splash.Exposure101SplashScreenStateBinding;

public class StateManagerImpl implements StateManager {

  private final Context context;
  private State currentState;
  private final State exposure101SplashScreenState;
  private final State loadingState;
  private final State menuState;
  private final State levelGroupState; // NEED BETTER NAME
  private final State levelLoadingState;
  private final State levelState;

  @Inject
  public StateManagerImpl(
      Context context,
        @Exposure101SplashScreenStateBinding State exposure101SplashScreenState,
        @LoadingStateBinding LoadingState loadingState,
        @MainMenuStateBinding MenuState menuState,
        @LevelGroupMenuStateBinding MenuState levelGroupState,
        @LevelLoadingStateBinding LoadingState levelLoadingState,
        @LevelStateBinding LevelState levelState) {
    this.context = context;
    this.exposure101SplashScreenState = exposure101SplashScreenState;
    this.loadingState = loadingState;
    this.menuState = menuState;
    this.levelGroupState = levelGroupState;
    this.levelLoadingState = levelLoadingState;
    this.levelState = levelState;
  }

  @Override
  public void goTo(State to) {
    if (currentState != null) {
      currentState.destroy();
    }
    currentState = to;
    currentState.initialize();
  }

  @Override
  public void reportError(Throwable error) {
    error.printStackTrace();
  }

  @Override
  public State getCurrentState() {
    return currentState;
  }

  @Override
  public Context getContext() {
    return context;
  }

  @Override
  public State getExposure101SplashScreenState() {
    return exposure101SplashScreenState;
  }

  @Override
  public State getLoadingState() {
    return loadingState;
  }

  @Override
  public State getMenuState() {
    return menuState;
  }

  @Override
  public State getLevelGroupMenuState() {
    return levelGroupState;
  }

  @Override
  public State getLevelLoadingState() {
    return levelLoadingState;
  }

  @Override
  public State getLevelState() {
    return levelState;
  }
}


So, now that this is all given we can see where the game kicks off.  The first thing we use is a Splash Screen State.  Some people do loading in this state - but I definitely don't.  Since many games have more than one Splash Screen - I made an Abstract Splash Screen State.  We only use the Exposure101 Splash Screen, but if you wanted to add another all you would have to do is add it to the State Manager.

Here's the Abstract Splash State - it's pretty simple.

package com.exposure101.playn.runrun.state.splash;

import playn.core.Keyboard;

import com.exposure101.playn.runrun.state.AbstractState;
import com.exposure101.playn.runrun.state.StateManager;

public abstract class AbstractSplashScreenState extends AbstractState {

  protected final long SPLASH_SCREEN_TIMEOUT = 2600;

  public AbstractSplashScreenState(StateManager stateManager) {
    super(stateManager);
  }

  @Override
  public Keyboard.Listener getKeyboardListener() {
    return null;
  }
}


And here's the full blown Splash Screen State.  Note that the paint() method is empty - this is because any Image Layers added to the Root Layer will be drawn by PlayN automatically (somebody feel free to correct me if this is wrong).

package com.exposure101.playn.runrun.state.splash;

import static playn.core.PlayN.assets;
import static playn.core.PlayN.graphics;

import javax.inject.Inject;

import playn.core.Image;
import playn.core.ImageLayer;
import playn.core.PlayN;

import com.exposure101.playn.runrun.shared.Command;
import com.exposure101.playn.runrun.shared.Timeout;
import com.exposure101.playn.runrun.state.StateManager;

public class Exposure101SplashScreenState extends AbstractSplashScreenState {

  private Image splashScreenImage;
  private ImageLayer imageLayer;
  private Timeout timeout;

  @Inject
  public Exposure101SplashScreenState(StateManager stateManager) {
    super(stateManager);
  }

  @Override
  public void initialize() {
    splashScreenImage = assets().getImage("img/exposure101_logo.png");
    imageLayer = graphics().createImageLayer(splashScreenImage);
    graphics().rootLayer().add(imageLayer);
    final float xScale = graphics().width() / splashScreenImage.width();
    final float yScale = graphics().height() / splashScreenImage.height();
    PlayN.log().debug("screen width = " + graphics().width());
    PlayN.log().debug("screen height = " + graphics().height());
    PlayN.log().debug("splash screen width = " + splashScreenImage.width());
    PlayN.log().debug("splash screen height = " + splashScreenImage.height());
    imageLayer.setScale(xScale, yScale);
    graphics().rootLayer().add(imageLayer);
    timeout = new Timeout(SPLASH_SCREEN_TIMEOUT, new Command() {

      @Override
      public void execute() {
        stateManager.goTo(stateManager.getLoadingState());
      }
    });
  }

  @Override
  public void destroy() {
    imageLayer.destroy();
  }

  @Override
  public void update(float delta) {
    if (timeout != null) {
      if (timeout.isRunning() == false) {
        timeout.start();
      } else {
        timeout.update(delta);
      }
    }
  }

  @Override
  public void paint(float alpha) {
  }
}



If you notice, this class used a Timeout Object - this is given below as it's worth knowing a somewhat easy way to do this in PlayN. 

package com.exposure101.playn.runrun.shared;

import static playn.core.PlayN.currentTime;

public class Timeout implements Updatable {

  private final float timeout;
  private final Command command;
  
  private boolean running;
  private double startTime;
  private double endTime;

  public Timeout(float timeout, Command command) {
    this.timeout = timeout;
    this.command = command;
  }

  public void start() {
    startTime = currentTime();
    endTime = startTime + timeout;
    running = true;
  }
  
  public boolean isRunning() {
    return running;
  }

  @Override
  public void update(float delta) {
    if (running) {
      if (currentTime() >= endTime) {
        running = false;
        command.execute();
      }
    }
  }
}


So - once the Timeout is finished it will execute the Command to go to the Loading State.  This one's a little bit more complicated.

The Entity Metadata Cache as well as the Level Group Metadata Cache are passed into this state, added to a List of Loadable (which is a simple Interface with a load() method), and then loaded in the initialize() method when the goTo(...) method is called on the State Manager.


package com.exposure101.playn.runrun.state.loading;

import static playn.core.PlayN.assets;
import static playn.core.PlayN.graphics;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.inject.Inject;
import javax.inject.Singleton;

import playn.core.Image;
import playn.core.ImageLayer;
import playn.core.PlayN;

import com.exposure101.playn.runrun.state.AbstractState;
import com.exposure101.playn.runrun.state.StateManager;

@Singleton
public class LoadingStateImpl extends AbstractState implements LoadingState {

  private final List<Loadable> loadables;
  private final Iterator<Loadable> iterator;

  private Image loadingImage;
  private ImageLayer imageLayer;

  @Inject
  public LoadingStateImpl(
      StateManager stateManager,
        @EntityMetadataCacheLoaderBinding Loadable entityMetadataCacheLoader,
        @LevelGroupMetadataCacheLoaderBinding Loadable levelGroupMetadataCacheLoader) {
    super(stateManager);
    loadables = new ArrayList<Loadable>();
    loadables.add(entityMetadataCacheLoader);
    loadables.add(levelGroupMetadataCacheLoader);
    iterator = loadables.iterator();
  }

  @Override
  public void loadNext() {
    if (iterator.hasNext()) {
      iterator.next().load();
    } else {
      stateManager.goTo(stateManager.getMenuState());
    }
  }

  @Override
  public void initialize() {
    loadingImage = assets().getImage("img/loading.png");
    imageLayer = graphics().createImageLayer(loadingImage);
    graphics().rootLayer().add(imageLayer);
    final float xScale = graphics().width() / loadingImage.width();
    final float yScale = graphics().height() / loadingImage.height();
    PlayN.log().debug("loading image width = " + loadingImage.width());
    PlayN.log().debug("loading image height = " + loadingImage.height());
    imageLayer.setScale(xScale, yScale);
    graphics().rootLayer().add(imageLayer);
    loadNext();
  }

  @Override
  public void destroy() {
    imageLayer.destroy();
  }

  @Override
  public void update(float delta) {
  }

  @Override
  public void paint(float alpha) {
  }
}


The Metadata Caches are loaded up at start up with just the basic Metadata for all the entities.  The entities are actually read from the Level JSON files and populated in the Level state which we'll see later.

Here is the Entity Metadata Cache.  This is somewhat complex but basically just uses a series of callbacks to manage loading up all the entity metadata from a set of JSON files - the path to this set is passed in via Guice.

package com.exposure101.playn.runrun.state.loading;

import static com.exposure101.playn.runrun.context.ContextKeys.ENTITY_METADATA_CACHE;
import static playn.core.PlayN.assets;
import static playn.core.PlayN.json;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Provider;
import javax.inject.Singleton;

import playn.core.Json;
import playn.core.ResourceCallback;

import com.exposure101.playn.runrun.context.Context;
import com.exposure101.playn.runrun.entity.EntityMetadata;
import com.exposure101.playn.runrun.entity.EntityMetadataCache;
import com.exposure101.playn.runrun.json.JsonReader;
import com.exposure101.playn.runrun.shared.Callback;

@Singleton
public class EntityMetadataCacheLoader implements Loadable {

  private final LoadingState loadingState;
  private final Context context;
  private final EntityMetadataCache cache;
  private final Provider<JsonReader<EntityMetadata>> jsonReader;
  private final String entityMetadata;

  private List<String> paths;
  private Iterator<String> iterator;

  @Inject
  public EntityMetadataCacheLoader(
      @LoadingStateBinding LoadingState loadingState,
        Context context,
        EntityMetadataCache entityMetadataCache,
        Provider<JsonReader<EntityMetadata>> jsonReader,
        @Named("EntityMetadata") String entityMetadata) {
    this.loadingState = loadingState;
    this.context = context;
    this.cache = entityMetadataCache;
    this.jsonReader = jsonReader;
    this.entityMetadata = entityMetadata;
  }

  @Override
  public void load() {
    assets().getText(entityMetadata, new ResourceCallback<String>() {

      @Override
      public void error(Throwable error) {
        loadingState.reportError(error);
      }

      @Override
      public void done(String resource) {
        final Json.Object json = json().parse(resource);
        final Json.Array array = json.getArray("entities");
        paths = new ArrayList<String>(array.length());
        for (int i = 0; i < array.length(); i++) {
          paths.add(array.getObject(i).getString("path"));
        }
        iterator = paths.iterator();
        cache();
      }
    });
  }

  private void cache() {
    if (iterator.hasNext()) {
      cache(iterator.next(), new Callback.Default<String>(loadingState) {

        @Override
        public void onSuccess(String t) {
          cache();
        }
      });
    } else {
      context.set(ENTITY_METADATA_CACHE, cache);
      loadingState.loadNext();
    }
  }

  private void cache(String path, final Callback<String> callback) {
    jsonReader.get().read(path, new ResourceCallback<EntityMetadata>() {

      @Override
      public void error(Throwable error) {
        callback.onFailure(error);
      }

      @Override
      public void done(EntityMetadata resource) {
        cache.cache(resource);
        callback.onSuccess(resource.getName());
      }
    });
  }
}


The Level Group Metadata Cache Loader is basically the same thing.

package com.exposure101.playn.runrun.state.loading;

import static com.exposure101.playn.runrun.context.ContextKeys.LEVEL_GROUP_METADATA_CACHE;

import java.util.Iterator;
import java.util.List;

import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Provider;
import javax.inject.Singleton;

import playn.core.ResourceCallback;

import com.exposure101.playn.runrun.context.Context;
import com.exposure101.playn.runrun.json.JsonReader;
import com.exposure101.playn.runrun.level.LevelGroupMetadata;
import com.exposure101.playn.runrun.level.LevelGroupMetadataCache;
import com.exposure101.playn.runrun.shared.Callback;

/**
 * 
 * @author sean christe (sean.exposure@gmail.com)
 * @author trevor marsh
 *
 */
@Singleton
public class LevelGroupMetadataCacheLoader implements Loadable {

  private final LoadingState loadingState;
  private final Context context;
  private final LevelGroupMetadataCache cache;
  private final Provider<JsonReader<LevelGroupMetadata>> jsonReader;
  private final Iterator<String> iterator;

  @Inject
  public LevelGroupMetadataCacheLoader(
      @LoadingStateBinding LoadingState loadingState,
        Context context,
        LevelGroupMetadataCache cache,
        Provider<JsonReader<LevelGroupMetadata>> jsonReader,
        @Named("LevelGroups") List<String> levelGroups) {
    this.loadingState = loadingState;
    this.cache = cache;
    this.context = context;
    this.jsonReader = jsonReader;
    this.iterator = levelGroups.iterator();
  }

  @Override
  public void load() {
    if (iterator.hasNext()) {
      load(iterator.next(), new Callback.Default<String>(loadingState) {

        @Override
        public void onSuccess(String t) {
          load();
        }
      });
    } else {
      context.set(LEVEL_GROUP_METADATA_CACHE, cache);
      loadingState.loadNext();
    }
  }

  private void load(String levelGroup, final Callback<String> callback) {
    jsonReader.get().read(levelGroup, new ResourceCallback<LevelGroupMetadata>() {

      @Override
      public void done(LevelGroupMetadata resource) {
        cache.cache(resource);
        callback.onSuccess(resource.getName());
      }

      @Override
      public void error(Throwable error) {
        callback.onFailure(error);
      }
    });
  }
}

The JSON Readers are fairly technical and not really important right now - they simply interpret the JSON produced by Ubik and return an Entity Metadata or Level Group Metadata Object.


Once the Loading State has completed it uses the State Manager to go to the Menu State.  The Menu State is currently not very pretty - I'm using the Triple Play UI to just make some buttons on the page - in the future (when I actually have graphics to work with that don't suck or aren't just ripped from another game) I'll make the Menu look better.

There are 2 Menus - the Main Menu and the Level Group Menu (I couldn't think of a better name for this - think Angry Birds how you select a World then a Level inside that world).  If you wanted to add any more Menus you would just add them to the state machine and change the goTo(...) to whatever your Menu State is.

package com.exposure101.playn.runrun.state.menu;

import static com.exposure101.playn.runrun.context.ContextKeys.LEVEL_GROUP;
import static com.exposure101.playn.runrun.context.ContextKeys.LEVEL_GROUP_METADATA_CACHE;
import static playn.core.PlayN.graphics;

import javax.inject.Inject;

import playn.core.GroupLayer;
import react.UnitSlot;
import tripleplay.ui.Background;
import tripleplay.ui.Button;
import tripleplay.ui.Group;
import tripleplay.ui.Interface;
import tripleplay.ui.Label;
import tripleplay.ui.Root;
import tripleplay.ui.SimpleStyles;
import tripleplay.ui.Style;
import tripleplay.ui.layout.AxisLayout;

import com.exposure101.playn.runrun.level.LevelGroupMetadata;
import com.exposure101.playn.runrun.level.LevelGroupMetadataCache;
import com.exposure101.playn.runrun.state.AbstractState;
import com.exposure101.playn.runrun.state.StateManager;

public class MainMenuStateImpl extends AbstractState implements MenuState {

  private Interface ui;
  private GroupLayer groupLayer;

  @Inject
  public MainMenuStateImpl(StateManager stateManager) {
    super(stateManager);
  }

  @Override
  public void initialize() {
    groupLayer = graphics().createGroupLayer();
    graphics().rootLayer().add(groupLayer);
    ui = new Interface();

    final Root root = ui.createRoot(AxisLayout.vertical().gap(15), SimpleStyles.newSheet());
    root.setSize(graphics().width(), graphics().height());
    root.addStyles(Style.BACKGROUND.is(Background.solid(0xFF99CCFF).inset(5)));
    groupLayer.add(root.layer);

    final Group buttons = new Group(AxisLayout.vertical().offStretch());
    root.add(new Label("RunRun:"), buttons);

    final LevelGroupMetadataCache cache = (LevelGroupMetadataCache) stateManager.getContext().get(
        LEVEL_GROUP_METADATA_CACHE);
    for (final LevelGroupMetadata levelGroup : cache) {
      final Button button = new Button(levelGroup.getName());
      buttons.add(button);
      button.clicked().connect(new UnitSlot() {

        @Override
        public void onEmit() {
          stateManager.getContext().set(LEVEL_GROUP, cache.get(levelGroup.getName()));
          stateManager.goTo(stateManager.getLevelGroupMenuState());
        }
      });
    }
  }

  @Override
  public void destroy() {
    groupLayer.destroy();
  }

  @Override
  public void update(float delta) {

  }

  @Override
  public void paint(float alpha) {
    if (ui != null) {
      ui.paint(alpha);
    }
  }
}



The Main Menu state just directs to this.


package com.exposure101.playn.runrun.state.menu;

import static com.exposure101.playn.runrun.context.ContextKeys.LEVEL_GROUP;
import static com.exposure101.playn.runrun.context.ContextKeys.LEVEL_JSON;
import static playn.core.PlayN.graphics;
import static playn.core.PlayN.keyboard;

import javax.inject.Inject;

import playn.core.GroupLayer;
import playn.core.Key;
import playn.core.Keyboard;
import playn.core.Keyboard.Event;
import react.UnitSlot;
import tripleplay.ui.Background;
import tripleplay.ui.Button;
import tripleplay.ui.Group;
import tripleplay.ui.Interface;
import tripleplay.ui.Label;
import tripleplay.ui.Root;
import tripleplay.ui.SimpleStyles;
import tripleplay.ui.Style;
import tripleplay.ui.layout.AxisLayout;

import com.exposure101.playn.runrun.level.LevelGroupMetadata;
import com.exposure101.playn.runrun.level.LevelMetadata;
import com.exposure101.playn.runrun.state.AbstractState;
import com.exposure101.playn.runrun.state.StateManager;

/**
 * 
 * @author sean.exposure
 * @author trevor.exposure
 * 
 */
public class LevelGroupMenuStateImpl extends AbstractState implements MenuState {

  private Interface ui;
  private GroupLayer groupLayer;

  @Inject
  public LevelGroupMenuStateImpl(StateManager stateManager) {
    super(stateManager);
  }

  @Override
  public void initialize() {
    groupLayer = graphics().createGroupLayer();
    graphics().rootLayer().add(groupLayer);
    ui = new Interface();

    final LevelGroupMetadata levelGroup = (LevelGroupMetadata) stateManager.getContext().get(
        LEVEL_GROUP);

    final Root root = ui.createRoot(AxisLayout.vertical().gap(15), SimpleStyles.newSheet());
    root.setSize(graphics().width(), graphics().height());
    root.addStyles(Style.BACKGROUND.is(Background.solid(0xFF99CCFF).inset(5)));
    groupLayer.add(root.layer);

    final Group buttons = new Group(AxisLayout.vertical().offStretch());
    root.add(new Label(levelGroup.getName()), buttons);

    final LevelMetadata[] levels = levelGroup.getLevels();
    for (final LevelMetadata level : levels) {
      final Button button = new Button(level.getName());
      buttons.add(button);
      button.clicked().connect(new UnitSlot() {

        @Override
        public void onEmit() {
          stateManager.getContext().set(LEVEL_JSON, level.getPath());
          stateManager.goTo(stateManager.getLevelLoadingState());
        }
      });
    }

    keyboard().setListener(new Keyboard.Adapter() {

      @Override
      public void onKeyDown(Event event) {
        if (event.key().equals(Key.ESCAPE)) {
          destroy();
          stateManager.goTo(stateManager.getMenuState());
        }
      }
    });
  }

  @Override
  public void destroy() {
    groupLayer.destroy();
  }

  @Override
  public void update(float delta) {

  }

  @Override
  public void paint(float alpha) {
    if (ui != null) {
      ui.paint(alpha);
    }
  }
}
  

As you can see - when a State is selected the State Manager goes to the Level Loading State.  The Level Loading State starts the Level World up (which creates actual Entities from the Entity Metadata Cache and populates them into the Level and the Physics Engine - that will not be displayed since it's kind of out of the scope of this).  The Level World is given to the Context and the Level State is started.

package com.exposure101.playn.runrun.state.loading;

import static com.exposure101.playn.runrun.context.ContextKeys.LEVEL;
import static com.exposure101.playn.runrun.context.ContextKeys.LEVEL_JSON;
import static playn.core.PlayN.assets;
import static playn.core.PlayN.graphics;

import javax.inject.Inject;
import javax.inject.Provider;

import playn.core.Image;
import playn.core.ImageLayer;
import playn.core.PlayN;
import playn.core.ResourceCallback;

import com.exposure101.playn.runrun.json.JsonReader;
import com.exposure101.playn.runrun.level.Level;
import com.exposure101.playn.runrun.level.LevelWorld;
import com.exposure101.playn.runrun.state.AbstractState;
import com.exposure101.playn.runrun.state.StateManager;

/**
 * 
 * @author sean.exposure
 * @author trevor.exposure
 * 
 */
public class LevelLoadingStateImpl extends AbstractState implements LoadingState {

  private final Provider<JsonReader<Level>> jsonReader;
  private Image loadingImage;
  private ImageLayer imageLayer;

  @Inject
  public LevelLoadingStateImpl(StateManager stateManager, Provider<JsonReader<Level>> jsonReader) {
    super(stateManager);
    this.jsonReader = jsonReader;
  }

  @Override
  public void loadNext() {
    stateManager.goTo(stateManager.getLevelState());
  }

  @Override
  public void initialize() {
    loadingImage = assets().getImage("img/loading.png");
    imageLayer = graphics().createImageLayer(loadingImage);
    graphics().rootLayer().add(imageLayer);
    final float xScale = graphics().width() / loadingImage.width();
    final float yScale = graphics().height() / loadingImage.height();
    PlayN.log().debug("loading image width = " + loadingImage.width());
    PlayN.log().debug("loading image height = " + loadingImage.height());
    imageLayer.setScale(xScale, yScale);
    graphics().rootLayer().add(imageLayer);
    final String path = (String) stateManager.getContext().get(LEVEL_JSON);
    jsonReader.get().read(path, new ResourceCallback<Level>() {

      @Override
      public void done(Level resource) {
        stateManager.getContext().set(LEVEL, new LevelWorld(resource, stateManager.getContext()));
        stateManager.goTo(stateManager.getLevelState());
      }

      @Override
      public void error(Throwable error) {
        stateManager.reportError(error);
      }
    });
  }

  @Override
  public void destroy() {
    imageLayer.destroy();
  }

  @Override
  public void update(float delta) {
  }

  @Override
  public void paint(float alpha) {
  }
}


And, here's the finale - the Level State.

package com.exposure101.playn.runrun.state.level;

import static com.exposure101.playn.runrun.context.ContextKeys.LEVEL;
import static playn.core.PlayN.graphics;
import static playn.core.PlayN.keyboard;
import static playn.core.PlayN.pointer;

import javax.inject.Inject;

import playn.core.Key;
import playn.core.Keyboard;
import playn.core.Pointer;
import playn.core.Pointer.Event;

import com.exposure101.playn.runrun.level.LevelWorld;
import com.exposure101.playn.runrun.state.AbstractState;
import com.exposure101.playn.runrun.state.StateManager;

public class LevelStateImpl extends AbstractState implements LevelState {

  private LevelWorld levelWorld;

  @Inject
  public LevelStateImpl(StateManager stateManager) {
    super(stateManager);
  }

  @Override
  public void initialize() {
    levelWorld = (LevelWorld) stateManager.getContext().get(LEVEL);
    pointer().setListener(new Pointer.Adapter() {

      @Override
      public void onPointerStart(Event event) {
        // mouse stuff - don't wanna give away our idea in here just yet - sorry guys

        // but basically - just get mouse x & y and delegate it to the Level World

      }
    });
    keyboard().setListener(new Keyboard.Adapter() {

      @Override
      public void onKeyDown(playn.core.Keyboard.Event event) {
        if (event.key().equals(Key.ESCAPE)) {
          destroy();
          stateManager.goTo(stateManager.getLevelGroupMenuState());
        }
        if (event.key().equals(Key.P)) {
          levelWorld.setPaused(!levelWorld.isPaused());
        }
      }
    });
  }

  @Override
  public void destroy() {
    levelWorld.destroy();
  }

  @Override
  public void update(float delta) {
    levelWorld.update(delta);
  }

  @Override
  public void paint(float alpha) {
    levelWorld.paint(alpha);
  }
}


As you can see the Level State really doesn't do anything but delegate to the Level World.

This is the whole Skeleton of the Game.  All of the actual intense game logic takes place in the Entity classes and the Level World class.  Other things can also be set from here such as various Commands passed to the Level World on what to execute if your player dies or the time's up or anything along those lines.

I know this isn't too PlayN intensive - but everything we've done so far has actually just been figured out from the Peas demo that comes with it - that's a great source of knowledge for a lot of this stuff.

I may post some information about the actual Level World soon - it is definitely outside of this though.

Also - one last thing, the Context and Context Keys.


package com.exposure101.playn.runrun.context;

import com.exposure101.playn.runrun.entity.EntityMetadataCache;
import com.exposure101.playn.runrun.level.LevelGroupMetadata;
import com.exposure101.playn.runrun.level.LevelGroupMetadataCache;
import com.exposure101.playn.runrun.level.LevelWorld;

public enum ContextKeys implements Context.Key {

  @Context.Key.Type(Integer.class)
  FPS,

  @Context.Key.Type(EntityMetadataCache.class)
  ENTITY_METADATA_CACHE,

  @Context.Key.Type(LevelGroupMetadataCache.class)
  LEVEL_GROUP_METADATA_CACHE,

  @Context.Key.Type(LevelGroupMetadata.class)
  LEVEL_GROUP,

  @Context.Key.Type(String.class)
  LEVEL_JSON,

  @Context.Key.Type(LevelWorld.class)
  LEVEL
}


package com.exposure101.playn.runrun.context;

import java.util.HashMap;
import java.util.Map;

import javax.inject.Singleton;

@Singleton
public class ContextImpl implements Context {

  private final Map<Context.Key, Object> context;

  public ContextImpl() {
    context = new HashMap<Context.Key, Object>();
  }

  @Override
  public Object get(Context.Key key) {
    return context.get(key);
  }

  @Override
  public void set(Context.Key key, Object value) {
    context.put(key, value);
  }
}



package com.exposure101.playn.runrun.context;

import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.SOURCE;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

public interface Context {

  interface Key {

    @Target({ FIELD })
    @Retention(SOURCE)
    @interface Type {
      Class<?> value();
    }
  }

  Object get(Context.Key key);

  void set(Context.Key key, Object value);
}

Monday, September 3, 2012

Serializing a JBox2D Shape with GSON

For JBox2D 2.1.2, The JBox2D Shape is an abstract class which does not have a default no-arg constructor.  Serializing it with GSON works fine, but trying to deserialize it will throw the following exception:

java.lang.RuntimeException: 
Failed to invoke public org.jbox2d.collision.shapes.Shape() with no args ...
Caused by: java.lang.InstantiationException ...

The Shape constructor takes a ShapeType argument - this is stored as the field m_type.  In order to deserialize a Shape you need to read the m_type field out of the JSON string and instantiate the concrete class based on the ShapeType.  The code to do this is given below:


package com.exposure101.ubik.shared.io.json;

import java.lang.reflect.Type;

import org.jbox2d.collision.shapes.CircleShape;
import org.jbox2d.collision.shapes.PolygonShape;
import org.jbox2d.collision.shapes.Shape;
import org.jbox2d.collision.shapes.ShapeType;

import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;

public class ShapeTypeAdapter implements JsonDeserializer<Shape> {
  
  @Override
  public Shape deserialize(JsonElement element, Type type, 
        JsonDeserializationContext context) throws JsonParseException {
    final JsonObject wrapper = (JsonObject) element;
    final ShapeType shapeType = ShapeType.valueOf(wrapper.get("m_type").getAsString());
    Type actualType = getClass(shapeType);
    return context.deserialize(element, actualType);
  }

  private Class<? extends Shape> getClass(ShapeType shapeType) {
    switch (shapeType) {
    case CIRCLE:
      return CircleShape.class;
    case POLYGON:
      return PolygonShape.class;
    default:
      return null;
    }
  }
}

Tuesday, August 28, 2012

Registering a Close Handler for a newly opened Window in GWT

It seems that GWT doesn't have any built in way to add handlers to a new Window opened with the standard Window.open(...) call. In all honesty, I don't have any real useful reason for doing this, but here's a simple example of how to in case it ever needs to happen. keep in mind this is a really trivial example meant to show how to do this as simply as possible

Here's the setup - I created a simple project and dropped the exposure101 logo into the war file. I added a button on the screen which opens the picture in a new window whenever the button is clicked.

Currently, When you use Window.open(...) in GWT, it does not give you a handle to the new Window. According to Joel Webber from 2006 (This is actually the most recent explanation I could find), "GWT cannot allow you to pass objects easily back and forth, because the methods and properties on those objects are compressed and unpredictable (i.e. Foo.bar() in Java is probably called something like x.y() in Javascript). There are also other optimizations that the compiler performs that would make this problematic." So, in order to actually get a handle to it we will have to drop into JSNI.

First thing is a method to get a handle to the Window. (the syntax formatter is having trouble with the JSNI blocks - they aren't comments)

native JavaScriptObject openWindow(String url) /*-{
  return $wnd.open(url, 'blank');
}-*/;  

Next thing to do is to create a Java callback that the new Window JavaScriptObject can call when it's closed.

private void onWindowClosed() {
  // do something useful
}

Next thing is to add another JSNI function to listen for the window close event.  I'm not a big Javascript guy and from what I've read on stackoverflow it seems like this function may not be the best way to handle window close events.  So far I've been using this on both Chrome and Firefox and haven't seen an issue - that being said people that are way better at Javascript than I am have said it's unreliable - so if you actually do need to create this type of listener, you might need to look further into handling window close events with Javascript.  Anyway, for this example, here's the function I've been using.

native JavaScriptObject registerHandlers(Jsni jsni, JavaScriptObject window) /*-{
  window.onbeforeunload = doOnbeforeunload;
  function doOnbeforeunload() {
    jsni.@com.exposure101.examples.jsni.client.Jsni::onWindowClosed()();
  }
}-*/;

* There's something kind of tricky here.  the JSNI documentation says you can call Java instance methods from JSNI using both an instance of the class or this.  I have not been able to call instance methods using this.  I could not get anything to happen using this:
 this.@com.exposure101.examples.jsni.client.Jsni::onWindowClosed()();

Anyway, those 3 functions are pretty much the bulk of it.  The complete code is given below.  I don't know how useful this will really be to anybody but I couldn't find any examples of how to actually implement this anywhere.

package com.exposure101.examples.jsni.client;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.VerticalPanel;

/**
 * Entry point classes define <code>onModuleLoad()</code>.
 */
public class Jsni implements EntryPoint {

  /**
   * This is the entry point method.
   */
  public void onModuleLoad() {
    final Button openButton = new Button("open");
    final VerticalPanel panel = new VerticalPanel();
    panel.add(openButton);
    RootPanel.get("center").add(panel);

    openButton.addClickHandler(new ClickHandler() {

      @Override
      public void onClick(ClickEvent event) {
        final String url = GWT.getHostPageBaseURL() + "exposure101-logo.png";
        final JavaScriptObject window = openWindow(url);
        registerHandlers(Jsni.this, window);
      }
    });
  }

  native JavaScriptObject openWindow(String url) /*-{
    return $wnd.open(url, 'blank');
  }-*/;
  
  native JavaScriptObject registerHandlers(Jsni jsni, JavaScriptObject window) /*-{
    window.onbeforeunload = doOnbeforeunload;
    function doOnbeforeunload() {
      jsni.@com.exposure101.examples.jsni.client.Jsni::onWindowClosed()();
    }
  }-*/;
  
  private void onWindowClosed() {
    Window.alert("nice catch blanco nino");
  }
}




Wednesday, July 18, 2012

A really simple Android Jersey/Gson example


Last weekend I wanted to start playing with REST on android.  I set up a really simple Google App Engine server that basically runs nothing more than a Hello World server - you send it a shared User object with nothing but an emailAddress and it will give it back to you.

To recreate this you're going to need to download the following libraries: (the downloads are at the bottom - the jersey-json was a nightmare to track down the first time)

gson (i used 2.2.2):
jersey-client (i used 1.12):
jersey-core (i used 1.12):
jersey-json (i used 1.12):

The first thing I did was create a simple network handler.

There's 2 points to this that took me a minute to find:
  1. Android doesn't allow you to run Network Code from the main thread.  In order to return anything it would be necessary to have some type of Callback method.
  2. Certain versions of Jersey and Android don't play well together.  See this post: http://stackoverflow.com/questions/9342506/jersey-client-on-android-nullpointerexception.  Basically you will have to provide your own Implementation of Jersey's ServiceIteratorProvider class - Android apparently cannot load the one in the META-INF folder of the Jersey Jar. The code to get around this is copied directly out of this post and works perfectly - it is attached below.
So - the very first thing I decided to do before creating my NetworkHandler class was to create a simple Callback interface as shown below.

package com.exposure101.example.shared;

public interface Callback<T> {

  void callback(T t); 
} 

Next I made 2 classes that extend Android's AsyncTask class to do the actual GET and POST calls.  I made a simple enum to wrap the MIME types for JSON.  The source code is given below

Here's the MIMETypes enum

package com.exposure101.example.shared;

public enum MIMETypes {

  APPLICATION_JSON("application/json");
  
  private final String name;
  
  private MIMETypes(String name) {
    this.name = name;
  }
  
  public String getName() {
    return name;
  }
}

Here's the Get Task

package com.exposure101.example.json;

import android.os.AsyncTask;

import com.exposure101.example.shared.Callback;
import com.exposure101.example.shared.MIMETypes;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.WebResource;

public class GetTask extends AsyncTask<String, String, String> {

  private final String url;
  private final Callback<String> callback;

  GetTask(String url, Callback<String> callback) {
    this.url = url;
    this.callback = callback;
  }

  @Override
  protected String doInBackground(String... params) {
    final Client client = Client.create();
    final WebResource resource = client.resource(url);
    final ClientResponse response = resource.accept(MIMETypes.APPLICATION_JSON.getName())
        .get(ClientResponse.class);
    return response.getEntity(String.class);
  }

  @Override
  protected void onPostExecute(String result) {
    callback.callback(result);
    super.onPostExecute(result);
  }
}

And here's the Post Task

package com.exposure101.example.json;

import android.os.AsyncTask;

import com.exposure101.example.shared.Callback;
import com.exposure101.example.shared.MIMETypes;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.WebResource;

public class PostTask extends AsyncTask<String, String, String> {

  private final String url;
  private final String requestBody;
  private final Callback<String> callback;

  PostTask(String url, String requestBody, Callback<String> callback) {
    this.url = url;
    this.requestBody = requestBody;
    this.callback = callback;
  }

  @Override
  protected String doInBackground(String... params) {
    final Client client = Client.create();
    final WebResource resource = client.resource(url);
    final ClientResponse response = resource.type(MIMETypes.APPLICATION_JSON.getName())
        .post(ClientResponse.class, requestBody);
    if (response.getStatus() != 201 && response.getStatus() != 200) {
      throw new RuntimeException("failed: http error code = " + response.getStatus());
    }
    final String responseEntity = response.getEntity(String.class).replaceAll("\\\\", "");
    return responseEntity.substring(1, responseEntity.length() - 1);
  }

  @Override
  protected void onPostExecute(String result) {
    callback.callback(result);
    super.onPostExecute(result);
  }
}

With this I kind of cheated the system using the POST request - honestly I have no idea if you should do it like this.  On the server side, as soon as the POST is received I query the datastore to see if a User with the given email address exists.  If it's not already in the datastore I create one, either way I return it back across the wire as JSON with the google datastore id in it.  There's 2 things that I found happen.
  1. The entire response is broken up by \ marks. An example would be {\"id"\:\1001\,\"emailAddress"\: ... \}
  2. The entire JSON string is wrapped in quotation marks - as a quick hack to get around this I simply took a substring.
Now that I have some simple tasks up and ready I made a really simple NetworkHandler interface to read and write requests. 

package com.exposure101.example.json;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import com.exposure101.example.jersey.AndroidServiceIteratorProvider;
import com.exposure101.example.shared.Callback;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.sun.jersey.spi.service.ServiceFinder;

public class NetworkHandler {

  private static NetworkHandler instance;

  public static synchronized NetworkHandler getInstance() {
    if (instance == null) {
      instance = new NetworkHandler();
    }
    return instance;
  }

  @SuppressWarnings("rawtypes")
  private NetworkHandler() {
    ServiceFinder.setIteratorProvider(new AndroidServiceIteratorProvider());
  }

  public <T> void read(final String url, final Class<T> clazz, final Callback<T> callback) {
    new GetTask(url, new Callback<String>() {

      @Override
      public void callback(String result) {
        callback.callback(new GsonBuilder().create().fromJson(result, clazz));
      }
    }).execute();
  }

  public <T> void readList(final String url, final Class<T[]> clazz, final Callback<List<T>> callback) {
    new GetTask(url, new Callback<String>() {

      @Override
      public void callback(String result) {
        final T[] array = new GsonBuilder().create().fromJson(result, clazz);
        callback.callback(new ArrayList<T>(Arrays.asList(array)));
      }
    }).execute();
  }

  public <T> void write(final String url, final Class<T> clazz, final T t, final Callback<T> callback) {
    final Gson gson = new GsonBuilder().create();
    new PostTask(url, gson.toJson(t), new Callback<String>() {

      @Override
      public void callback(String result) {
        callback.callback(gson.fromJson(result, clazz));
      }
    }).execute();
  }
}


That's pretty much it for the Network code.

All that's left is to create an activity to show the user accounts and send it to the server.

Basically there's only 2 things I want to happen here:
  1. Have all the accounts on the phone come up in a ListView on the top part of the screen.  (Note: the accounts on your phone aren't unique - there may be multiple accounts with the same email - we should only show unique email addresses)
  2. Have a button on the bottom to send the selected email address to the server.  If we receive a response we can then launch a new Intent to switch the Activity. (Note: while the server is processing we should indicate that to the user with a loading icon)
The xml layout for this is as follows:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <Button
        android:id="@+id/select_user_account_button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:text="@string/login" />

    <ListView
        android:id="@+id/user_accounts_list_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_above="@id/select_user_account_button"
        android:layout_alignParentTop="true" />

</RelativeLayout>

and the actual Android Activity code is as follows:


package com.exposure101.example.activities;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;

import android.accounts.Account;
import android.accounts.AccountManager;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Patterns;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.Window;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;

import com.exposure101.example.R;
import com.exposure101.example.json.NetworkHandler;
import com.exposure101.example.json.UserServiceURLs;
import com.exposure101.example.shared.Callback;
import com.exposure101.example.shared.entity.User;

public class UserAccountSelectActivity extends Activity {

  private ListView userAccountsListView;

  private Button selectUserAccountButton;

  private String selectedEmailAddress;

  /**
   * 
   */
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
    setContentView(R.layout.activity_user_account_select);
    userAccountsListView = (ListView) findViewById(R.id.user_accounts_list_view);
    selectUserAccountButton = (Button) findViewById(R.id.select_user_account_button);
    final Account[] accounts = initializeAccounts();
    final String[] emailAddresses = initializeEmailAddresses(accounts);
    initializeView(emailAddresses);
  }

  /**
   * get the accounts from android's account manager
   * 
   * @return
   */
  private Account[] initializeAccounts() {
    final Pattern emailPattern = Patterns.EMAIL_ADDRESS;
    final List<Account> accounts = new ArrayList<Account>(Arrays.asList(AccountManager.get(this)
        .getAccounts()));
    for (final Iterator<Account> it = accounts.iterator(); it.hasNext();) {
      final Account account = it.next();
      if (emailPattern.matcher(account.name).matches() == false) {
        it.remove();
      }
    }
    return accounts.toArray(new Account[accounts.size()]);
  }

  /**
   * get the email addresses from the accounts
   * 
   * @param accounts
   * @return
   */
  private String[] initializeEmailAddresses(Account[] accounts) {
    final List<String> emailAddresses = new ArrayList<String>(accounts.length);
    for (final Account account : accounts) {
      emailAddresses.add(account.name);
    }
    final Set<String> uniqueEmailAddresses = new HashSet<String>(emailAddresses);
    return uniqueEmailAddresses.toArray(new String[uniqueEmailAddresses.size()]);
  }

  /**
   * 
   * @param accounts
   */
  private void initializeView(String[] emailAddresses) {
    selectUserAccountButton.setEnabled(false);
    userAccountsListView.setAdapter(new ArrayAdapter<String>(this,
        android.R.layout.simple_list_item_single_choice, emailAddresses));
    userAccountsListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
    userAccountsListView.setOnItemClickListener(new OnItemClickListener() {

      @Override
      public void onItemClick(AdapterView<?> adapter, View view, int position, long arg3) {
        selectedEmailAddress = (String) userAccountsListView.getItemAtPosition(position);
        selectUserAccountButton.setEnabled(true);
      }
    });

    selectUserAccountButton.setOnClickListener(new OnClickListener() {

      @Override
      public void onClick(View v) {
        login();
      }
    });
  }

  /**
   * 
   * @param emailAddress
   */
  private void login() {
    final NetworkHandler networkHandler = NetworkHandler.getInstance();
    final String url = UserServiceURLs.POST.getURL();
    networkHandler.write(url, User.class, new User(selectedEmailAddress), new Callback<User>() {

      @Override
      public void callback(User user) {
        handleLogin();
      }
    });
  }

  /**
   * your call was successful
   */
  private void handleLogin() {
    setProgressBarIndeterminateVisibility(false);
    selectUserAccountButton.setEnabled(true);
    /*
     * startActivity(new Intent(UserAccountSelectActivity.this, TheNextExampleActivity.class));
     */
  }
}
 

And that's about it. 

Here's all the required libraries:  (The jersey-json one was a little hard to track down)

gson
jersey-client-1.2
jersey-core-1.2
json-jar-1.2

And here's the AndroidServiceIteratorProvider class.

package com.exposure101.example.jersey;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;

import android.util.Log;

import com.sun.jersey.spi.service.ServiceFinder.ServiceIteratorProvider;

public class AndroidServiceIteratorProvider<T> extends ServiceIteratorProvider<T> {

  private static final String TAG = AndroidServiceIteratorProvider.class.getSimpleName();
  private static final String MESSAGE = "Unable to load provider";

  private static final HashMap<String, String[]> SERVICES = new HashMap<String, String[]>();

  private static final String[] com_sun_jersey_spi_HeaderDelegateProvider = {
      "com.sun.jersey.core.impl.provider.header.MediaTypeProvider",
      "com.sun.jersey.core.impl.provider.header.StringProvider" };

  private static final String[] com_sun_jersey_spi_inject_InjectableProvider = {};

  private static final String[] javax_ws_rs_ext_MessageBodyReader = {
      "com.sun.jersey.core.impl.provider.entity.StringProvider",
      "com.sun.jersey.core.impl.provider.entity.ReaderProvider" };

  private static final String[] javax_ws_rs_ext_MessageBodyWriter = {
      "com.sun.jersey.core.impl.provider.entity.StringProvider",
      "com.sun.jersey.core.impl.provider.entity.ReaderProvider" };

  static {
    SERVICES.put("com.sun.jersey.spi.HeaderDelegateProvider",
        com_sun_jersey_spi_HeaderDelegateProvider);
    SERVICES.put("com.sun.jersey.spi.inject.InjectableProvider",
        com_sun_jersey_spi_inject_InjectableProvider);
    SERVICES.put("javax.ws.rs.ext.MessageBodyReader", javax_ws_rs_ext_MessageBodyReader);
    SERVICES.put("javax.ws.rs.ext.MessageBodyWriter", javax_ws_rs_ext_MessageBodyWriter);
    SERVICES.put("jersey-client-components", new String[] {});
    SERVICES.put("com.sun.jersey.client.proxy.ViewProxyProvider", new String[] {});
  }

  @SuppressWarnings("unchecked")
  @Override
  public Iterator<Class<T>> createClassIterator(Class<T> service, String serviceName,
      ClassLoader loader, boolean ignoreOnClassNotFound) {

    String[] classesNames = SERVICES.get(serviceName);
    int length = classesNames.length;
    ArrayList<Class<T>> classes = new ArrayList<Class<T>>(length);
    for (int i = 0; i < length; i++) {
      try {
        classes.add((Class<T>) Class.forName(classesNames[i]));
      } catch (ClassNotFoundException e) {
        Log.v(TAG, MESSAGE, e);
      }
    }
    return classes.iterator();
  }

  @Override
  public Iterator<T> createIterator(Class<T> service, String serviceName, ClassLoader loader,
      boolean ignoreOnClassNotFound) {

    String[] classesNames = SERVICES.get(serviceName);
    int length = classesNames.length;
    ArrayList<T> classes = new ArrayList<T>(length);
    for (int i = 0; i < length; i++) {
      try {
        classes.add(service.cast(Class.forName(classesNames[i]).newInstance()));
      } catch (IllegalAccessException e) {
        Log.v(TAG, MESSAGE, e);
      } catch (InstantiationException e) {
        Log.v(TAG, MESSAGE, e);
      } catch (ClassNotFoundException e) {
        Log.v(TAG, MESSAGE, e);
      }
    }

    return classes.iterator();
  }
}



Hope this helps.