The flickr-browser module is an example of how to build a custom content app. The module installs an app that allows you to browse photos and albums on Flickr. We use a POJO as ItemId, which is more versatile than the String used in the simpler example. Using an object as ItemId has the advantage that we can tell the difference between Flickr photos and albums. We also make fewer Flickr API calls which makes the app perform better. In this example the tree view and the thumbnail view use different Containers.
On this example we will implement the following custom classes:
ItemId
Item
ContentConnector
Tree Container
ImageProvider
Presenter
We also add two views to the workbench:
Tree view
Thumbnail view
Using a POJO as ItemId makes hierarchy possible
The module needs a tree view to show both albums and photos. We need an ItemId that carries sufficient information about its corresponding Item. The ItemId should know things such as:
isForAlbum: Is the item an album or a photo?
parentId: Parent album of a photo. A photo can be in more than one album. However, within the tree container it should know its parent.
photoId: Photo ID. An album also carries information about a photo. Each album has a cover photo. The image provider should display the cover photo when the album is selected in the container.
albumId: Album ID. If the item is a photo it carries the albumId of its parent album.
uuid: A String representation of the ItemId to use for URL fragments. The UUID should carry sufficient information to instantiate the ItemId again by the given UUID.
snippet of info.magnolia.flickr.browser.app.item.FlickrItemExpand source
public interface FlickrItem extends BasicFlickrItem {
public static String PROPERTY_UUID = "uuid";
public static String PROPERTY_NUMBEROFPHOTOS = "numberOfPhotos";
public static String PROPERTY_FLICKR_ITEM_TYPE = "flickrItemType";
public static String FLICKRITEMTYPE_ALBUM = "album";
public static String FLICKRITEMTYPE_PHOTO = "photo";
public String getUuid();
public class IDs {
private static Map<String, Class> properties = BasicFlickrItem.IDs.getIDs();
static {
properties.put(PROPERTY_FLICKR_ITEM_TYPE, String.class);
properties.put(PROPERTY_NUMBEROFPHOTOS, Integer.class);
properties.put(PROPERTY_UUID, String.class);
}
public static Map<String, Class> getIDs() {
return properties;
}
}
}
Property IDs:
uuid: A String representation of the ItemId to use for URL fragments. The same as used in the FlickrItemId. This will be helpful when an ItemId must be constructed by a given item.
numberOfPhotos: The number of photos in an album.
flickrItemType: Whether the item has been derived from a photo or from an album.
In addition, we inherit property IDs title, description and photoId from
Similar to the SimpleFlickrItem interface, the FlickrItem interface created here also has a static class that provides a map of all the properties and their types:
public class FlickrPropertysetItem extends PropertysetItem implements FlickrItem {
protected FlickrPropertysetItem(Photo photo, String uuid){
String photoId = photo.getId();
String title = StringUtils.isNotBlank(photo.getTitle()) ? photo.getTitle() : photoId;
String description = StringUtils.isNotBlank(photo.getDescription()) ? photo.getDescription() : "";
addItemProperty(PROPERTY_UUID, new ObjectProperty(uuid));
addItemProperty(PROPERTY_PHOTOID, new ObjectProperty(photoId));
addItemProperty(PROPERTY_FLICKR_ITEM_TYPE, new ObjectProperty(FLICKRITEMTYPE_PHOTO));
addItemProperty(PROPERTY_TITLE, new ObjectProperty(title));
addItemProperty(PROPERTY_DESCRIPTION, new ObjectProperty(description));
}
protected FlickrPropertysetItem(Photo photo){
String photoId = photo.getId();
String title = StringUtils.isNotBlank(photo.getTitle()) ? photo.getTitle() : photoId;
String description = StringUtils.isNotBlank(photo.getDescription()) ? photo.getDescription() : "";
addItemProperty(PROPERTY_PHOTOID, new ObjectProperty(photoId));
addItemProperty(PROPERTY_FLICKR_ITEM_TYPE, new ObjectProperty(FLICKRITEMTYPE_PHOTO));
addItemProperty(PROPERTY_TITLE, new ObjectProperty(title));
addItemProperty(PROPERTY_DESCRIPTION, new ObjectProperty(description));
}
protected FlickrPropertysetItem(Photoset photoset){
String photoId = photoset.getPrimaryPhoto().getId();
String uuid = FlickrItemId.UuidParser.createUuid(photoset);
String title = StringUtils.isNotBlank(photoset.getTitle()) ? photoset.getTitle() : uuid;
String description = StringUtils.isNotBlank(photoset.getDescription()) ? photoset.getDescription() : "";
addItemProperty(PROPERTY_UUID, new ObjectProperty(uuid));
addItemProperty(PROPERTY_PHOTOID, new ObjectProperty(photoId));
addItemProperty(PROPERTY_FLICKR_ITEM_TYPE, new ObjectProperty(FLICKRITEMTYPE_ALBUM));
addItemProperty(PROPERTY_TITLE, new ObjectProperty(title));
addItemProperty(PROPERTY_DESCRIPTION, new ObjectProperty(description));
addItemProperty(PROPERTY_NUMBEROFPHOTOS, new ObjectProperty(new Integer(photoset.getPhotoCount())));
}
public String getUuid() {
return (String) getItemProperty(PROPERTY_UUID).getValue();
}
}
Item utility
We use two different containers for the tree and thumbnail views. For this reason we cannot couple the container and the content connector as we did in SimpleFlickrFlatContainer. We need methods to fetch an Item by ItemId and vice versa, in both the container and the content connector. Here we implement a utility class which provides methods to use in both.
In this example we implement a lazy loading tree container. At first, the view should render only albums as top level items. When a user clicks an album the view expands the subtree and loads the photos of the album. To achieve this, our custom container implements Collapsible and Container.Indexed as well as Container, Container.Hierarchical and Container.Ordered which are superinterfaces. We also implement
List<FlickrItemId> preorderItemKeys is used in many methods. It represents the list of IDs that are currently visible.
When a user clicks a folder (album), it expands or collapses. This triggers #setCollapsed via an event. preorderItemKeys is set to null and must be assigned the next time #getPreorder is used.
Methods throwing UnsupportedOperationException are not shown above for better readability.
public class FlickrBrowserTreePresenterDefinition extends TreePresenterDefinition {
public FlickrBrowserTreePresenterDefinition() {
setImplementationClass(FlickrBrowserTreePresenter.class);
}
}
public class FlickrBrowserTreePresenter extends TreePresenter {
private final FlickrService flickrService;
private UiContext uiContext;
@Inject
public FlickrBrowserTreePresenter(TreeView view, ComponentProvider componentProvider, FlickrService flickrService, UiContext uiContext) {
super(view, componentProvider);
this.flickrService = flickrService;
this.uiContext = uiContext;
}
@Override
protected Container initializeContainer() {
return new FlickrCustomTreeContainer(flickrService, uiContext);
}
@Override
public String getIcon(Item item) {
if (item instanceof FlickrItem) {
if (FlickrItem.FLICKRITEMTYPE_ALBUM.equals(item.getItemProperty(FlickrItem.PROPERTY_FLICKR_ITEM_TYPE).getValue())) {
return "icon-folder-l";
} else {
return "icon-file-image";
}
}
return super.getIcon(item);
}
}
As we already know from others containers, #initializeContainer must be implemented to set the container. What is new compared to containers in previous examples, #getIcon sets the icon depending on the underlying Item.
. Image provider is a component that renders images used in apps. It generates the portrait image at the bottom of the action bar and the thumbnails for the thumbnail view.
is different from SimpleFlickrBrowserPreviewImageProvider used the simple example. In the #getThumbnailResource(Object itemId, String generator) method, FlickrItemId already knows the thumbnail URL. This way we do not need to call the Flickr API again and can save time and network resources.
public class FlickrThumbnailContainer extends ThumbnailContainer {
public FlickrThumbnailContainer(ImageProvider imageProvider, FlickrService flickrService, UiContext uiContext) {
super(imageProvider, new FlickrFlatIdProvider(flickrService));
}
}
public class FlickrThumbnailPresenterDefinition extends ThumbnailPresenterDefinition {
public FlickrThumbnailPresenterDefinition() {
setImplementationClass(FlickrThumbnailPresenter.class);
}
}