Monday Apr 29, 2013

UK Oracle Users Group hosts ADF Mobile event

The UK Oracle Users Group is hosting an ADF Mobile specific SIG on May 21st in the London city office.  Topics include device integration, security, web service development for mobile and user experience.

Check this link for more information!

Seats are limited so register soon!

Wednesday Mar 06, 2013

Invoking custom Javascript from AMX pages

In this edition of the ADF Mobile blog I'll tackle a topic that many have asked about.  How to invoke a custom Javascript method from an AMX page.  We'll also cover how to call back to Java from the same Javascript method.


Adding the custom Javascript to your AMX

To include a javascript file to an AMX feature, you need to go to Content tab of the adfmf-feature.xml.  This lets you include a javascript or CSS file.  In this case, we'll be including our custom javacript file.


Invoking the Javascript from a Java handler

From any Java code, you can use a built in method of the AdfmfContainerUtilities utility class to invoke a Javascript method.  Here's an example:

  AdfmfContainerUtilities.invokeContainerJavaScriptFunction("feature1", 
        "doAlert", new Object[] {});

The above call invokes the "doAlert" method in the feature with featureid="feature1".  The last parameter is an array of parameters that will be sent to the method.   Here's an example of passing in 3 parameters:

  AdfmfContainerUtilities.invokeContainerJavaScriptFunction("feature1", 
         "doAlert", new Object[] {"arg1", new Integer(123), "arg3"});


Handling Parameters in your Javascript methods

You can access parameters in your Javascript method by simply using the "arguments" variable.  In the example we call the same doAlert function with different numbers of parameters to show this.

     // This method shows you how to use variable args 
    doAlert = function () {
        var args = arguments;
        var str = "doAlert, argCount:" + args.length + ", arguments:";

        for (x = 0;x < args.length;x++) {
            if (x > 0) {
                str += ", ";
            }
            str += arguments[x];
        }
        alert(str);
    };


Calling back to Java

You can invoke Java methods by using the adf.mf.api.invokeMethod call:

        adf.mf.api.invokeMethod("mobile.MyClass", "FetchCallback", URI, 
            onInvokeSuccess, onFail);

In this method, the first parameter is the fully qualified Java class with the package name.  The second parameter is the method to invoke.  The second to last parameter is the javascript callback to be invoked on success and the last parameter is the javascript callback to be invoked when the function fails.  Any parameters added between the method and success callback are passes as the parameters to the java method.  Here are the signatures for the success and failure callbacks:

    function onSuccess(request, response) {
       // Process any return values that comes back in the "response" parameter 
    }; 
  
    function onFail(request, response)) {
    }; 
  

This shouldn't be confused with the success and failure callbacks from PhoneGap method calls.  Those have a different signature and you should consult the PhoneGap documentation for details.

If you wanted to just set a value of an EL expression, you can use the following method call:

        adf.mf.el.setValue( { "name": "#{pageFlowScope.myVariable}", "value": "some value" }, 
                onSuccess, onFail); 
  

With this method, you can simply set the value of the EL expression.  Note that the first parameter is a complex parameter that contains a JSON snippet that has a name/value pair.  It has the usual success and failure callbacks. 


The full example is available here.


Tuesday Feb 19, 2013

Tablet Layouts

There are some popular patterns for tablets that many developers are looking to implement.  First we'll describe a few of these and show you how to achieve them.

Flowing Layouts

Flowing layouts are the easiest to achieve.  You can simply add enough content so that i won't fit onto the page and the panelPage will automatically make the content scrollable so you can view it all.  Typically you would achieve this by using a tableLayout with many rowLayout components. 

Stretch Layouts

Sometimes it is desired to just separate your layout into multiple areas that are proportional to the width of the display and size dynamically based on orientation.  Many times you don't want these areas to flow but rather to resize to the size of the application size.  Let's take an example where you want to device the screen into two vertical areas, each 50% width but 100% of the height of the page.  You can achieve it like this:

<amx:tableLayout id="tl1" width="100%" shortDesc="Detail Table">
   <amx:rowLayout id="rl2">
      <amx:cellFormat id="cf2" height="#{deviceScope.hardware.screen.availableHeight-44}" 
               inlineStyle="background-color:yellow" valign="top" width="50%"  
               shortDesc="Column1 Cell">
         <amx:outputText value="Column 1 content goes here" id="ot6" 
               inline/>
      </amx:cellFormat>
      <amx:cellFormat id="cf3" inlineStyle="background-color:Aqua;" valign="top" 
               width="50%" shortDesc="Column2 Cell">
         <amx:outputText value="Column 2 content goes here" id="ot7" 
               inline/>
      </amx:cellFormat>
   </amx:rowLayout>
</amx:tableLayout> 

Note that the height attribute uses the availableHeight attribute of the deviceScope and the 44px is the height of the header (in a future release we may have some kind of constant so you don't need to hardcode this).  The availableHeight will change dynamically if the orientation changes.  The width of each cell can be set with percentages.  It is not possible to set the height using percentages with the tableLayout.  

Swim Lanes

This type of layout allows row of data to scroll horizontally independently of each other.  The lanes themselves can also be moved up and down so many different lanes can appear.  Below is an example.  You could replace the multiple hardcoded panelGroupLayout's with an iterator component to stamp out components based on a data collection. 

 <amx:tableLayout id="tl1" width="100%" shortDesc="Detail Table" cellPadding="0" 
      cellSpacing="0">
    <amx:rowLayout id="rlh1">
        <amx:cellFormat id="cfh1" valign="middle" height="30" 
                inlineStyle="background-color:blue" width="100%" 
                shortDesc="Content1 Header Cell" header="true">
            <amx:outputText value="Content1 Header" id="oth1"/>
        </amx:cellFormat>
    </amx:rowLayout>
    <amx:rowLayout id="rlc1">
        <amx:cellFormat id="cfc1" inlineStyle="background-color:yellow" width="100%" 
            shortDesc="Content1 Cell">
            <amx:panelGroupLayout id="panelGroupLayout5" inlineStyle="width:100%" 
                layout="horizontal">
                <amx:panelGroupLayout id="pgl11" styleClass="adfmf-panelGroupLayout-groupBox"
                                      inlineStyle="width:100px;height:100px">
                    <amx:outputText value="Lane1 Item1" id="otr11"/>
                </amx:panelGroupLayout>
                <amx:panelGroupLayout id="pgl12" styleClass="adfmf-panelGroupLayout-groupBox"
                                      inlineStyle="width:100px;height:100px">
                    <amx:outputText value="Lane1 Item2" id="otr12"/>
                </amx:panelGroupLayout>
                <amx:panelGroupLayout id="pgl13" styleClass="adfmf-panelGroupLayout-groupBox"
                                      inlineStyle="width:100px;height:100px">
                    <amx:outputText value="Lane1 Item3" id="otr13"/>
                </amx:panelGroupLayout>
                <amx:panelGroupLayout id="pgl14" styleClass="adfmf-panelGroupLayout-groupBox"
                                      inlineStyle="width:100px;height:100px">
                    <amx:outputText value="Lane1 Item4" id="otr14"/>
                </amx:panelGroupLayout>
                <amx:panelGroupLayout id="pgl15" styleClass="adfmf-panelGroupLayout-groupBox"
                                      inlineStyle="width:100px;height:100px">
                    <amx:outputText value="Lane1 Item5" id="otr15"/>
                </amx:panelGroupLayout>
                <amx:panelGroupLayout id="pgl16" styleClass="adfmf-panelGroupLayout-groupBox"
                                      inlineStyle="width:100px;height:100px">
                    <amx:outputText value="Lane1 Item6" id="otr16"/>
                </amx:panelGroupLayout>
                <amx:panelGroupLayout id="pgl17" styleClass="adfmf-panelGroupLayout-groupBox"
                                      inlineStyle="width:100px;height:100px">
                    <amx:outputText value="Lane1 Item7" id="otr17"/>
                </amx:panelGroupLayout>
                <amx:panelGroupLayout id="pgl18" styleClass="adfmf-panelGroupLayout-groupBox"
                                      inlineStyle="width:100px;height:100px">
                    <amx:outputText value="Lane1 Item8" id="otr18"/>
                </amx:panelGroupLayout>
                <amx:panelGroupLayout id="pgl19" styleClass="adfmf-panelGroupLayout-groupBox"
                                      inlineStyle="width:100px;height:100px">
                    <amx:outputText value="Lane1 Item9" id="otr19"/>
                </amx:panelGroupLayout>
                <amx:panelGroupLayout id="pgl110" styleClass="adfmf-panelGroupLayout-groupBox"
                                      inlineStyle="width:100px;height:100px">
                    <amx:outputText value="Lane1 Item10" id="otr110"/>
                </amx:panelGroupLayout>
            </amx:panelGroupLayout>
        </amx:cellFormat>
    </amx:rowLayout>
 </amx:tableLayout> 

Tiled Layouts

A popular layout is to have a horizontal layout of "tiled" objects that will move horizontally across the page until no more will fit and then wrap to the next line and continue until all are completed.  The entire page may scroll to enable the user to see all tiled objects.  Note that currently you need to apply the "display:inline-block" CSS attribute added to the child components that you want to wrap.  In a later release, this may be done automatically for you but until then you'll need to add this yourself.   This can be achieved by the following:

<amx:tableLayout id="tl1" width="100%" shortDesc="Detail Table">
    <amx:rowLayout id="rl1">
          <amx:cellFormat id="cfh2" valign="middle" height="30" inlineStyle="background-color:blue" 
                      width="100%" shortDesc="Content Header Cell" header="true">
              <amx:outputText value="Content Header" id="ot3" inline/>
        </amx:cellFormat>
    </amx:rowLayout>
    <amx:rowLayout id="rl2">
          <amx:cellFormat id="cf2" height="#{deviceScope.hardware.screen.availableHeight-44-30}"
                      inlineStyle="background-color:yellow" valign="top" width="50%" 
                      shortDesc="Column1 Cell">
              <amx:panelGroupLayout id="pgl1"
                      inlineStyle="height:100%">
                  <amx:panelGroupLayout id="pglw" layout="wrap">
                    <amx:panelGroupLayout id="pgl11" styleClass="adfmf-panelGroupLayout-groupBox"
                                            inlineStyle="width:100px;height:100px;display:inline-block">
                        <amx:outputText value="Lane1 Item1" id="otr11"/>
                      </amx:panelGroupLayout>
                    <amx:panelGroupLayout id="pgl12" styleClass="adfmf-panelGroupLayout-groupBox"
                                            inlineStyle="width:100px;height:100px;display:inline-block">
                        <amx:outputText value="Lane1 Item2" id="otr12"/>
                    </amx:panelGroupLayout>
                    <amx:panelGroupLayout id="pgl13" styleClass="adfmf-panelGroupLayout-groupBox"
                                            inlineStyle="width:100px;height:100px;display:inline-block">
                        <amx:outputText value="Lane1 Item3" id="otr13"/>
                    </amx:panelGroupLayout>
                    <amx:panelGroupLayout id="pgl14" styleClass="adfmf-panelGroupLayout-groupBox"
                                            inlineStyle="width:100px;height:100px;display:inline-block">
                        <amx:outputText value="Lane1 Item4" id="otr14"/>
                    </amx:panelGroupLayout>
                    <amx:panelGroupLayout id="pgl15" styleClass="adfmf-panelGroupLayout-groupBox"
                                            inlineStyle="width:100px;height:100px;display:inline-block">
                        <amx:outputText value="Lane1 Item5" id="otr15"/>
                    </amx:panelGroupLayout>
                    <amx:panelGroupLayout id="pgl16" styleClass="adfmf-panelGroupLayout-groupBox"
                                            inlineStyle="width:100px;height:100px;display:inline-block">
                        <amx:outputText value="Lane1 Item6" id="otr16"/>
                    </amx:panelGroupLayout>
                    <amx:panelGroupLayout id="pgl17" styleClass="adfmf-panelGroupLayout-groupBox"  
                                          inlineStyle="width:100px;height:100px;display:inline-block">
                        <amx:outputText value="Lane1 Item7" id="otr17"/>
                    </amx:panelGroupLayout>
                    <amx:panelGroupLayout id="pgl18" styleClass="adfmf-panelGroupLayout-groupBox" 
                                          inlineStyle="width:100px;height:100px;display:inline-block">
                        <amx:outputText value="Lane1 Item8" id="otr18"/> 
                    </amx:panelGroupLayout>
                    <amx:panelGroupLayout id="pgl19" styleClass="adfmf-panelGroupLayout-groupBox" 
                                          inlineStyle="width:100px;height:100px;display:inline-block">
                        <amx:outputText value="Lane1 Item9" id="otr19"/> 
                    </amx:panelGroupLayout>
                    <amx:panelGroupLayout id="pgl110" styleClass="adfmf-panelGroupLayout-groupBox" 
                                            inlineStyle="width:100px;height:100px;display:inline-block">
                      <amx:outputText value="Lane1 Item10" id="otr110"/> 
                    </amx:panelGroupLayout>
                    <amx:panelGroupLayout id="pgl111" styleClass="adfmf-panelGroupLayout-groupBox" 
                                            inlineStyle="width:100px;height:100px;display:inline-block">
                      <amx:outputText value="Lane1 Item11" id="otr111"/>
                    </amx:panelGroupLayout> 
                  </amx:panelGroupLayout>
            </amx:panelGroupLayout> 
          </amx:cellFormat>
    </amx:rowLayout> 
  </amx:tableLayout>

Using panelSplitter

The panelSplitter component is the most common method to achieve some simple layouts for tablets.  This provides a "navigation" facet that would typically contain a list or some kind of flowing component.  You can have multiple panelItems within the panelSplitter.  When the panelSplitter is displayed in portrait mode, the navigation facet is automatically collapsed into a popup to provide more space for the panelItems.  When in landscape mode, the navigator will appear normally.  The "position" attribute is used to define the size of the navigator facet, in either pixels or percentage.  Any of the above layout formats can be used within the panelItem so you can create a variety of master-detail layouts.  Here's an example using the stretch example above.

<amx:panelSplitter id="ps1">
    <amx:facet name="navigator">
        <amx:listView var="row" id="lv1">
            <amx:listItem id="li1" shortDesc="List Item 1">
                <amx:outputText value="Item 1" id="otli1"/>
                <amx:setPropertyListener from="Item1" to="#{pageFlowScope.item}"/>
            </amx:listItem>
            <amx:listItem id="li2" shortDesc="List Item 2">
                <amx:outputText value="Item 2" id="otli2"/>
                <amx:setPropertyListener from="Item2" to="#{pageFlowScope.item}"/>
            </amx:listItem>
            <amx:listItem id="li3" shortDesc="List Item 3">
                <amx:outputText value="Item 3" id="otli3"/>
                <amx:setPropertyListener from="Item3" to="#{pageFlowScope.item}"/>
            </amx:listItem>
            <amx:listItem id="li4" shortDesc="List Item 4">
                <amx:outputText value="Item 4" id="otli4"/>
                <amx:setPropertyListener from="Item4" to="#{pageFlowScope.item}"/>
            </amx:listItem>
            <amx:listItem id="li5" shortDesc="List Item 5">
                <amx:outputText value="Item 5" id="otli5"/>
                <amx:setPropertyListener from="Item5" to="#{pageFlowScope.item}"/>
            </amx:listItem>
            <amx:listItem id="li6" shortDesc="List Item 6">
                <amx:outputText value="Item 6" id="otli6"/>
                <amx:setPropertyListener from="Item6" to="#{pageFlowScope.item}"/>
            </amx:listItem>
            <amx:listItem id="li7" shortDesc="List Item 7">
                <amx:outputText value="Item 7" id="otli7"/>
                <amx:setPropertyListener from="Item7" to="#{pageFlowScope.item}"/>
            </amx:listItem>
            <amx:listItem id="li8" shortDesc="List Item 8">
                <amx:outputText value="Item 8" id="otli8"/>
                <amx:setPropertyListener from="Item8" to="#{pageFlowScope.item}"/>
            </amx:listItem>
            <amx:listItem id="li9" shortDesc="List Item 9">
                <amx:outputText value="Item 9" id="otli9"/>
                <amx:setPropertyListener from="Item9" to="#{pageFlowScope.item}"/>
            </amx:listItem>
            <amx:listItem id="li10" shortDesc="List Item 10">
                <amx:outputText value="Item 10" id="otli10"/>
                <amx:setPropertyListener from="Item10" to="#{pageFlowScope.item}"/>
            </amx:listItem>
        </amx:listView>
    </amx:facet>
    <amx:panelItem id="pi1">
        <amx:tableLayout id="tl1" width="100%" shortDesc="Detail Table">
            <amx:rowLayout id="rl1">
                <amx:cellFormat id="cfh2" valign="middle" height="30" inlineStyle="background-color:blue"
                                width="100%" columnSpan="2" shortDesc="Content Header Cell" header="true">
                    <amx:outputText value="Content Header" id="ot3" inlineStyle="color:White;"/>
                </amx:cellFormat>
            </amx:rowLayout>
            <amx:rowLayout id="rl2">
                <amx:cellFormat id="cf2" height="#{deviceScope.hardware.screen.availableHeight-44-30}"
                                inlineStyle="background-color:yellow" valign="top" width="50%"
                                shortDesc="Column1 Cell">
                    <amx:outputText value="Column 1 : #{pageFlowScope.item} content goes here" id="ot6"
                                    inlineStyle="color:Black;"/>
                </amx:cellFormat>
                <amx:cellFormat id="cf3" inlineStyle="background-color:Aqua;" valign="top" width="50%"
                                shortDesc="Column2 Cell">
                    <amx:outputText value="Column 2 : #{pageFlowScope.item} content goes here" id="ot7"
                                    inlineStyle="color:Black;"/>
                </amx:cellFormat>
            </amx:rowLayout>
        </amx:tableLayout>
    </amx:panelItem>
</amx:panelSplitter>


Friday Jan 11, 2013

Using Hi DPI Images in your Mobile App

Getting Started with ADF Mobile Sample Apps

Whether you are developing an ADF Mobile application or a classic ADF Server application, it is becoming more and more important to support hi-DPI screens (high resolution or "retina" displays). There is no easier way to make your application look out-dated than to use grainy, unprofessional image assets or use them improperly.

In HTML, the "px" unit corresponds to the same amount of space regardless of the DPI of your device (however, the number of device pixels may vary). The original iPhone models did not have hi-DPI displays. Each point of color on those screens corresponds to one HTML CSS "px" unit. Newer iPhone models introduced a hi-DPI (or "retina") display that has 4 device pixels in the same amount of space that 1 device pixel used to take up (2 device pixels wide by 2 device pixels tall); on these new devices, the width and height "px" values use twice the amount of device pixels.

Why is this a common problem? Since ADF Mobile uses HTML, displaying an image is not as simple as just specifying the source path and magically hoping the browser will know that you are using a high resolution image. You must specify dimensions to go along with the source path.

Let's work with an example. You have an image named "image-64.png". This image has a size of 64 by 64 individual dots of color (individual points of color information). If you coded your page like the following, the image will be shown with a width of 64px and a height of 64px (one color dot per "px"):

   <amx:image id="i1" source="images/image-64.png"/>

This would look just fine on a classic low-DPI display. However, on a hi-DPI display, it still takes up the same space but since there are more device pixels, the image will look very grainy.

In order to look crisp and professional, you need to set a size so that each dot of color corresponds to at least one device pixel. For a hi-DPI display, this means your image needs a width and a height specified such that you use 2 dots of image color information per HTML CSS "px" unit (e.g. a 64 by 64 sized image should be specified to use a width of 32px and a height of 32px. In code, your page should look like this:

   <amx:image id="i1" inlineStyle="width:32px;height:32px" source="images/image-64.png"/>

Even if you still want to support legacy devices for your application, this same image (with the same specified width and height) will look beautiful on low-DPI screens because of how images are processed modern browsers.

If for some reason you really needed or wanted to specify alternate images for each kind of device, you have the option to use a device properties EL variable to toggle the rendered state of alternate amx:image components or simply use that EL to alter the inlineStyle and the source path as desired.

Thursday Jan 10, 2013

ADF Mobile REST JSON/XML example

This article discusses and demonstrates the use of REST web services in your ADF Mobile applications.  

First you should download the RESTDemo example application and install it and run it and take a look.  The general usage of the app is that it takes a domain or IP address and returns you the geo-location of it.  The app provides this in a form and then you can view it on a map as well.  This demo uses a public web service provided by FreeGeoIP and it's author, Alexandre Fiori.  NOTE:  This service has a throttling mechanism of 1000 queries an hour so if you get a 403, you should wait until the next hour before you test again.

REST XML

There are two flavors of REST supported by ADF Mobile.  The first we'll talk about is REST XML.  The nice thing about REST XML is that it's structure is defined by an XSD and thus the design-time knows that the format should be and can provide you with a shape for the data when building your app.  You use the URL Data Control wizard from the new gallery to define a Data Control backed by a REST XML service.  You need to supply an XSD that defines what the structure will be.  Note: if you have a service that returns XML but you don't have an XSD, there are many free utilities that will generate the XSD for you.  When specifying the endpoint, you should typically keep this to the minimum needed for all methods of your web service.  You then specify the HTTP method for the method of your service you want to invoke.  Note that if you have a single service that supports multiple methods, you should be selecting the same endpoint and just keep adding methods to it, DO NOT generate separate Data Controls for each method.  Any extra info you need for specific methods, including parameters should be put into the Source field. 

Once your DC is created, it can be used just like any other Data Control.  You can optionally decide to invoke these Data Control methods from Java and follow the techniques outlines in the Web Service example #2 in this blog if you wish.

The REST XML Data Control for the Geo IP is used directly in the REST-XML feature and shows you how to declaratively use the DC without a bean.  

REST JSON

Because of the loosely structured nature of REST JSON services, there is no declarative model available for most because they do not provide a consistent way to describe the shape of the data model.  The way to use REST JSON services with ADF Mobile is to execute these services with our helper classes and then use the supplied JSON parsing classes to create beans from the data and bind your UI to those beans. 

In the second feature, REST-JSON, we have a managed bean created called RESTJSONBean.  This bean maintains the parameter that we send to the service method along with the JSON response that is returned. 

Steps to invoke and parse the REST JSON web service:

  1. The bean method loadData first declares an instance of the RestServiceAdapter class.  This is a helper class that lets you invoke REST web services and simply returns the results in string format. 
  2. The RestServiceAdapter lets you set the connection name (from connection.xml), the request type (GET/POST/etc), what the retry limits are and then you can specify anything else you want added to the URI. 
  3. The send method of the RestServiceAdapter is invoked and the service is called and the JSON string is returned.
  4. To easily parse the JSON string into a bean, we provide the JSONBeanSerializationHelper class.  This class has a fromJSON method where you can specify the destination class type and the string to parse and it will return an object of the class you specified.  In our case, we have defined a RESTJSONResponse class to hold our GeoIP information returned from the service.

Now that the service is invoked and the results are parsed into the bean, the UI is updated because it is already bound to the bean DC.  With the REST JSON version of this you don't need a Data Control for the service, but you do need a connection in your connection.xml to store the URL connection.   You can optionally bind the data directly to your bean or create a Data Control out of the Bean and bind that to the UI.  Normally it's easier to do the latter once you understand the binding framework.

What's next?

This pretty much completes this example.  I've added the map just as a nice UI representation of geo-coordinate supplied by this service.  In the future you will see some more structured REST JSON web services coming out of Oracle that have methods that provide schema information.  This will allow us to use them declaratively within ADF Mobile.

Good luck and happy coding!




About

This blog is is dedicated to tips and tricks for developing, integrating, securing, and managing mobile applications using Oracle Mobile Platform. It is created and maintained by the Mobile Suite/Oracle ADF Mobile product development team.

Archive of past entries

Even More Mobile Development Blogs

Oracle A-Team Site - Mobile Related Entries

Code samples from the Community

Fusion Middleware Blogs

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today