Screencast 9 – ADF Mobile and WebCenter Content Simple Example

Screencast 9 – ADF Mobile and WebCenter Content Simple Example

Any discussion around user interface capability inherently entails a discussion around mobile enablement. Organizations are making concerted efforts to understand how to develop and extend user experiences to a wide range of mobile devices.

Oracle’s ADF Mobile allows companies to develop leverage the power of declarative development and data binding to enable mobile experiences for new and existing systems.

In this episode of the Enterprise 2.0 Workbench we take a look if using ADF Mobile to create an interface on top of WebCenter Content. We do this using a web service data control that is being generated on top of a Java Bean connecting to Content Server. Our Java Bean uses RIDC to connect to content server and although it is not necessarily best practice to layer a web service on top of a bean using RIDC (RIDC uses services to call into Content Server – creating additional service traffic), it gives us a sense of how we can use existing services or services that are quickly enabled through a plain old job object to create our mobile interfaces.

Simple Example Scenario

In our example to create a mobile interface on top of the sales library to enable our sales people in the field to get the most recent, approved content items to help them do their job.

For organizations looking for true production mobile capabilities for WebCenter Content should review

Let’s dive in and review the various components within our simple example solution.

WebCenter Content

The basic sales library that we will expose via our mobile interface is supplied using WebCenter Content to secure and categorize the sales assets.

AMX Mobile Pages

Our solution only consists of two pages. These pages are created using ADF Mobile’s AMX tags. The DocView.amx will recursively call itself if a user chooses to drill into a folder within the interface. If a user selects a document the view will be routed to DocView.amx.

Document List Views – amx:setPropertyListener and amx:listView
For each selection we will store the relevant ID code in a managed bean associated with our application. This is done by including an amx:setPropertyListener within each list item to set the selected node. Once the row is selected, the managed bean’s GetCollection method will be called to refresh the data bound to 2 amx:listView components.

DocView.amx

<?xml version="1.0" encoding="UTF-8" ?>
<amx:view xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:amx="http://xmlns.oracle.com/adf/mf/amx"
          xmlns:dvtm="http://xmlns.oracle.com/adf/mf/amx/dvt">
  <amx:panelPage id="pp1">
    <amx:listView var="row" value="#{bindings.viewFolders.collectionModel}"
                  fetchSize="#{bindings.viewFolders.rangeSize}" id="lv1">
      <amx:listItem id="li1" actionListener="#{pageFlowScope.DemoBean.GetCollection}" action="showDocs">
        <amx:setPropertyListener from="#{row.collectionID}" to="#{pageFlowScope.DemoBean.selectedNode}" type="action"/>
        <amx:tableLayout id="tl2">
          <amx:rowLayout id="rl3">
            <amx:cellFormat id="cf4" width="40px">
              <amx:image id="i1" source="/Images/folder_32.png"/>
            </amx:cellFormat>
            <amx:cellFormat id="cf5">
              <amx:outputText value="#{row.collectionName}" id="ot4"/>
            </amx:cellFormat>
          </amx:rowLayout>
        </amx:tableLayout>
      </amx:listItem>
    </amx:listView>
    <amx:listView var="row" value="#{bindings.viewItems.collectionModel}" fetchSize="#{bindings.viewItems.rangeSize}"
                  id="lv2">
      <amx:listItem id="li2" action="showDetail">
        <amx:setPropertyListener from="#{row.URL}" to="#{pageFlowScope.DemoBean.docURL}" type="action"/>
        <amx:setPropertyListener from="#{row.title}" to="#{pageFlowScope.DemoBean.docName}"/>
        <amx:tableLayout width="100%" id="tl1">
          <amx:rowLayout id="rl1">
            <amx:cellFormat width="40px" rowSpan="2" id="cf2">
              <amx:image id="i2" source="/Images/page_32.png"/>
            </amx:cellFormat>
            <amx:cellFormat width="100%" height="28px" id="cf1">
              <amx:outputText value="#{row.title}" id="ot5"/>
            </amx:cellFormat>
          </amx:rowLayout>
          <amx:rowLayout id="rl2">
            <amx:cellFormat width="100%" height="12px" id="cf3">
              <amx:outputText value="#{row.author}" styleClass="adfmf-listItem-captionText" id="ot6"/>
            </amx:cellFormat>
          </amx:rowLayout>
        </amx:tableLayout>
      </amx:listItem>
    </amx:listView>
    <amx:facet name="header">
      <amx:outputText value="Browse" id="ot1"/>
    </amx:facet>
    <amx:facet name="primary">
      <amx:commandButton text="Home" id="cb3" actionListener="#{pageFlowScope.DemoBean.GoHome}" action="showDocs"/>
    </amx:facet>
    <amx:facet name="secondary">
    </amx:facet>
  </amx:panelPage>
</amx:view>

DetailView.amx

<?xml version="1.0" encoding="UTF-8" ?>
<amx:view xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:amx="http://xmlns.oracle.com/adf/mf/amx"
          xmlns:dvtm="http://xmlns.oracle.com/adf/mf/amx/dvt">
  <amx:panelPage id="pp1" inlineStyle="background-color:White;">
    <amx:facet name="header">
      <amx:outputText value="#{pageFlowScope.DemoBean.docName}" id="ot1"/>
    </amx:facet>
    <amx:facet name="primary">
      <amx:commandButton id="cb1" text="Back" action="__back"/>
    </amx:facet>
    <amx:facet name="secondary">
    </amx:facet>
    <amx:commandButton text="View Content" id="cb2"
                       actionListener="#{pageFlowScope.DemoBean.callFramedDocumentJS}"/>
 
    <amx:verbatim id="vb1"/>
  </amx:panelPage>
</amx:view>

Data Controls used to Create amx:listView

The 2 amx:listView components in the DocView.amx were declaratively based on the following controls that were dragged and dropped into the AMX page. The data controls were created by creating a new “Web Service Data Control” in our project based on a WSDL from our service layer on top of Content Server.

Managed Bean in ADF Mobile

Our managed bean does the heavy lifting in our example. It contains a series of methods that are called from our AMX page using actionListener attributes of our components.
Managed Bean DocBean.java

package mobile;

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

import javax.el.ValueExpression;

import oracle.adfmf.amx.event.ActionEvent;
import oracle.adfmf.framework.api.AdfmfContainerUtilities;
import oracle.adfmf.framework.api.AdfmfJavaUtilities;

import oracle.adfmf.framework.exception.AdfInvocationException;
import oracle.adfmf.java.beans.PropertyChangeListener;
import oracle.adfmf.java.beans.PropertyChangeSupport;

public class DocBean {

    // Start node for the document viewer (dCollectionID)
    private String rootCollectionNode = "727456639941000002";

    // Track the currently selected node
    private String selectedNode = rootCollectionNode;
    
    // Link to converted content
    private String docURL = "";

    // Name of content item that has been selected
    private String docName = "";

    private transient PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);

    public DocBean() {
        super();
    }

    // Store the seclected node for a given folder in the UI
    public void setSelectedNode(String selectedNode) {
        String oldSelectedNode = this.selectedNode;
        this.selectedNode = selectedNode;
        propertyChangeSupport.firePropertyChange("selectedNode", oldSelectedNode, selectedNode);
    }

    public void addPropertyChangeListener(PropertyChangeListener l) {
        propertyChangeSupport.addPropertyChangeListener(l);
    }

    public void removePropertyChangeListener(PropertyChangeListener l) {
        propertyChangeSupport.removePropertyChangeListener(l);
    }

    public String getSelectedNode() {
        return selectedNode;
    }

    // Set selected node to the root of the content systems as set above
    public void GoHome(ActionEvent actionEvent) {
        // Change the bean attribute of selectedNode to the root
        ValueExpression ve =
            AdfmfJavaUtilities.getValueExpression("#{pageFlowScope.DemoBean.selectedNode}", String.class);
        String selectedNode =
            String.valueOf(rootCollectionNode);
        ve.setValue(AdfmfJavaUtilities.getAdfELContext(), selectedNode);
    }

    // Set the current folder that we will query
    public void GetCollection(ActionEvent actionEvent) {
        ValueExpression ve =
            AdfmfJavaUtilities.getValueExpression("#{pageFlowScope.DemoBean.selectedNode}", String.class);
        String selectedNode = (String)ve.getValue(AdfmfJavaUtilities.getAdfELContext());
        ve.setValue(AdfmfJavaUtilities.getAdfELContext(), selectedNode);
    }

    // Call custom Javascript to inject an iFrame
    public void callFramedDocumentJS(ActionEvent actionEvent) {
        AdfmfContainerUtilities.invokeContainerJavaScriptFunction("DocB", "showFramedDocument",
                                                                  new Object[] { "" + docURL + "" });
    }

    // Set the URL for the web viewable version of a document / content item
    public void setDocURL(String docURL) {
        String oldDocURL = this.docURL;
        this.docURL = docURL;
        propertyChangeSupport.firePropertyChange("docURL", oldDocURL, docURL);
    }

    public String getDocURL() {
        return docURL;
    }

    public void setDocName(String docName) {
        this.docName = docName;
    }

    public String getDocName() {
        return docName;
    }
}

Custom Javascript to Open Iframe

For complete details of our custom Javascript that opens the iframe to our content check out our prior post ADF Mobile Custom Javascript – iFrame Injection.

Sample Application Download

ADF Mobile Development Resources

During this exercise the following resources proved extremely helpful.

11 comments
gokhalenr
gokhalenr

Hello John,


Nice blog. we are trying to develop ADF mobile application where we need to include RIDC jar in mobile application itself instead of webservice . the issue is RIDC need jre1.6 while adf mobile supports Java 1.4 so when we deploy application on mobile it gives unsupported class error. can you suggest any workaround for this


Nikhil

ruchika
ruchika

Hi, please throw some light on calling a webservice from java code. how can we do that. Whatever methods I have tried don't work. I have multiple inputs in my webservice and an array of outputs corresponding to that. I have tried AdfmfJavaUtilities.invokeDataControlMethod but without success. Please help

angelohannes
angelohannes

Thank you very much! This is an excellent video you posted.

I came across this blog because I have trouble using the <amx:verbatim> tag, which, I thought, does have the exact purpose of embedding pure HTML markup in an amx page. I see, you used the <amx:verbatim> tag to in your DetailView.amx.

Did you have problems, too? And if not, why did you decided to inject your markup that way?

John Brunswick
John Brunswick moderator

@gokhalenr  I have not tried that, but would be cautious with trying to use the JAR functionality within the app without extensive testing.  I know that some others have created some more extensive samples, but am pretty sure that they also call out to remote services using traditional routes.  Hope that helps!

John Brunswick
John Brunswick moderator

 @JRSim_UIX Thanks John!  Definitely keep me posted on any other updates.  Would be more than happy to showcase any new versions / platforms.

John Brunswick
John Brunswick moderator

 @angelohannes Thanks for the kind words on the video!

 

Regarding the amx:verbatim tag - you are correct in that it is used to embed HTML markup in an AMX page.  I found some challenges when 1. Trying to get the layout of the page to incorporate the Verbatim wrapped markup 2. Getting dynamic values within the Verbatim area based on what it being viewed 3. Having markup that the parser would not reject based on tag structure.

 

I have used a variety of mobile technologies in the past (JQM & Sencha Touch), so I found it pretty natural to look at tweaking the DOM to include the dynamic markup based on what I could pass along to the JS function via the Bean.  I am sure there are some other avenues, I just happened to get this pattern to work in the scenario above.

 

Hope that this explanation helps.  Good luck of you ADFM project!

John Brunswick
John Brunswick moderator

 @angelohannes  @ruchika Thanks guys - yes - definitely check out Shay's posts.  In general, if you can leverage the ADF Data Binding, that is the big benefit of using something like ADF Mobile.  So to leverage services some research around the ADF Data Binding capability in general will be a good start.  Shay's videos are an excellent resource.