Browse Reddit with AeroGear Part 2 paging.

In December, I described a demonstration of using Aerogear to browse Reddit (link). With the release of Aerogear Android M2, I wanted to take the time to update the demo with the new paging features.

Paging introduced several new interfaces: PagedList, PageResultExtractor, and ParameterProvider as well as PageConfig and default implementations for the new interfaces.

Adding the paging code to the Reddit application was very straight forward.

First let’s look at a response from Reddit:

[sourcecode language=”javascript”]
{
"data": {
"after": "t3_17i1lt",
"before": null,
"children": [
{
"data": {
/*snip*/
}
}
],
"modhash": ""
},
"kind": "Listing"
}
[/sourcecode]

“data.before” and “data.after” are information about the paging state. By default, Aeorgear can support WebLink headers or headers and body parameters which are URIs. Since Reddit uses neither, we need to implement a PageResultExtractor to consume this into AeroGear. This class will parse the data from HeaderAndBody into something usable by the framework.

[sourcecode language=”java”]
public class PageConsumer implements PageResultExtractor<PageConfig>{

public ReadFilter getNextFilter(HeaderAndBody result, PageConfig config) {
ReadFilter filter = new ReadFilter();
JsonParser parser =new JsonParser();
JsonElement element = parser.parse(new String(result.getBody()));
String next = getFromJSON(element, config.getNextIdentifier());
if (next != null) {
filter.setLinkUri(URI.create("?count=25&after=" + next));
}
return filter;
}

public ReadFilter getPreviousFilter(HeaderAndBody result, PageConfig config) {
ReadFilter filter = new ReadFilter();
JsonParser parser =new JsonParser();
JsonElement element = parser.parse(new String(result.getBody()));
String previous = getFromJSON(element, config.getPreviousIdentifier());
if (previous != null) {
filter.setLinkUri(URI.create("?count=26&before=" + previous));
}
return filter;
}

private String getFromJSON(JsonElement element, String nextIdentifier) {
String[] identifiers = nextIdentifier.split("\\.");
for( String identifier:identifiers) {
element = element.getAsJsonObject().get(identifier);
}
if (element.isJsonNull()) {
return null;
}

return element.getAsString();
}

}
[/sourcecode]

Now that we can handle Reddit’s responses, we can create a PageConfig object and pass it to our PipeConfig.

[sourcecode language=”java”]
//In StoryListApplication.java
PageConfig pageConfig = new PageConfig();
pageConfig.setLimitValue(25);
pageConfig.setMetadataLocation(PageConfig.MetadataLocations.BODY);
pageConfig.setNextIdentifier("data.after");
pageConfig.setPreviousIdentifier("data.before");
pageConfig.setPageHeaderParser(new PageConsumer());

config.setPageConfig(pageConfig);//config is an instance of PipeConfig
[/sourcecode]

When you have a PageConfig object, Aerogear will wrap the List result from a “read” operation in a PagedList. In StoryList we have created two new fields, a Callback readCallback and a PagedList listings. The callback will assign the result to “listings” and update the UI.

[sourcecode language=”java”]
private PagedList<Listing> listings;

private final Callback<List<Listing>> readCallback = new Callback<List<Listing>>() {

public void onSuccess(List<Listing> data) {
listings = (PagedList<Listing>) data;
setListAdapter(new ArrayAdapter<T3>(getActivity(),
android.R.layout.simple_list_item_activated_1,
android.R.id.text1,
data.get(0).getData().getChildren()));
}

public void onFailure(Exception e) {
Log.d("Reddt", "failure", e);
if (e instanceof HttpException) {
HttpException httpException = (HttpException) e;
Log.d("Reddit", new String(httpException.getData()));
}
}
};

[/sourcecode]

And now we add methods which call the “next” and “previous” methods on listings.

[sourcecode language=”java”]
public void next() {
listings.next(readCallback);
}

public void previous() {
listings.previous(readCallback);
}
[/sourcecode]

And now AGReddit can page results. The full code can be found on the GitHub page for AGReddit.

Aerogear Android M2

Aerogear Android M2

The Aeorgear Android library has gotten some new features since our last release in December.

First and foremost is an API for interacting with paged data from REST providers. Now you can simply add a PageConfig to a PipeConfig and begin using paged resources on an Aerogear Controller powered site. Alternatively, you can provide PageResultExtractor and DefaultParameterProvider to interact with paged data sources on other services such as Reddit, Github, or Twitter.

Secondly we have included a persistent SQLite backed datastore, SQLStore. It functions similarly to MemoryStore but instead uses Android’s native database to provide CRUD operations.

Pagination

API Review

PageConfig
A bean which is used to set configuration options for a Pipe to describe pagination.
PageResultExtractor
An interface which defines how to parse a read response into Pagining information. Default Implementations are URIBodyPageParser and URIPageHeaderParser
PagedList
A List extension which wraps the results of a read or readWithFilter operation and has the methods next and previous which call a Pipe and pass ReadFilters which return a PagedList for the next or previous datasets. The default implementation is WrappingPagedList.
ParameterProvider
An interface which is responsible for consuming ReadFilters and emitting URIs which can be consumed by the server with the correct paging implementation. The default implementation is DefaultParameterProvider.

Examples

see AGReddit Post

SQLStore

API Review

StoreTypes
An enum class with options MEMORY and SQL.
SQLStore
A Store implementation which writes to a backing SQLLite impl.

Examples

Creating a SQLStore

To create a SQLStore we can use a StoreConfig bean and DataManager. A SQL store needs to be opened using the open method. Open takes a Callback which is called after the database is ready for writing.

StoreConfig sqlStoreConfig = new StoreConfig();  
sqlStoreConfig.setContext(Robolectric.application.getApplicationContext());  
sqlStoreConfig.setType(SQL);  
sqlStoreConfig.setKlass(Data.class);  
Store store = dataManager.store("sqlStore", sqlStoreConfig);  
store.open(/*callback*/);  

Saving Data

Please note, open has already been called on store.

Data data = new Data(10, "name", "description");//Id, name, description  
store.save(data);  
Data readData = store.read(10);  

Clearing the data store

store.reset();  
Data readData = store.read(10);  
Assert.assertNull(readData);  

Reading All Data

store.save(new Data(1, "name", "description"));  
store.save(new Data((2, "name", "description"));  
store.save(new Data((3, "name2", "description"));  
store.save(new Data((4, "name2", "description"));  
store.save(new Data((5, "name", "description2"));  
store.save(new Data((6, "name2", "description2"));

List<data> allData = new ArrayList</data><data>(store.readAll());  
Collections.sort(allData);  
Assert.assertEquals(6, allData.size());  
Assert.assertEquals("name", allData.get(0).getName());  
Assert.assertEquals("name2", allData.get(5).getName());  

Deleting Data

loadBulkData(); //Loads 6 "Data" Elements
store.remove(1); //Deletes the element with ID 1  

Searching Data

Note: Filtering only supports exact matches

loadBulkData(); //Loads 6 "Data" Elements

result = store.readWithFilter(null);  
Assert.assertEquals(6, result.size());

filter = new ReadFilter();  
where = new JSONObject();  
where.put("name", "name2");  
filter.setWhere(where);  
result = store.readWithFilter(filter);  
Assert.assertEquals(3, result.size());

filter = new ReadFilter();  
where = new JSONObject();  
where.put("name", "name2");  
where.put("description", "description");  
filter.setWhere(where);  
result = store.readWithFilter(filter);  
Assert.assertEquals(2, result.size());  

2013 Technology Resolutions

For 2013 I would like to learn several new languages, read some books, and work on more projects.

Languages

  • Ruby
  • Scala
  • F#
  • Haskell
  • Coljure
  • Smalltalk
  • Objective C

Books

  • Engineering a Compiler
  • Hacking the JVM

Projects

  • jrecruiter
  • devnexus
  • update my website
  • Complete my Android game
  • ruboto