15 Using Bindings and Creating Data Controls in MAF AMX

This chapter describes how to use data bindings, data controls, and the data binding expression language (EL) within a MAF AMX application feature. In addition, object scope lifecycles, managed beans, UI hints, validation, and data change events are also discussed.

This chapter includes the following sections:

15.1 Introduction to Bindings and Data Controls

Data controls use standard metadata interfaces to describe the operations and data collections of a business service while declarative bindings contain information about accessing data from data controls. The model layer reads relevant XML files for information about data controls and bindings, and connects the user interface with the business service.

Mobile Application Framework implements two concepts that enable the decoupling of the user interface (UI) technology from the business service implementation: data controls and declarative bindings. Data controls abstract the implementation technology of a business service by using standard metadata interfaces to describe the operations and data collections of the service, including information about the properties, methods, and types involved. Using JDeveloper, you can view that information as icons that you can drag and drop onto a page. Declarative bindings abstract the details of accessing data from data collections in a data control and invoking its operations. At runtime, the model layer reads the information describing the data controls and bindings from the appropriate XML files and then implements the two-way connection between the user interface and the business service.

The group of bindings supporting the user interface components on a page are described in a page-specific XML file called the page definition file. The model layer uses this file at runtime to instantiate the bindings of the page. These bindings are held in a request-scoped map called the binding container, accessible during each page request using the EL expression #{bindings}. This expression always evaluates to the binding container for the current page. You can design a databound user interface by dragging an item from the Data Controls panel and dropping it on a page as a specific UI component. When you use data controls to create a UI component, JDeveloper automatically creates the code and objects needed to bind the component to the data control you selected.

The Mobile Application Framework comes with two out-of-the box data controls: the DeviceFeatures data control and the ApplicationFeatures data control. The DeviceFeatures data control appears within the Data Controls panel in JDeveloper, enabling you to drag and drop the primary data attributes of data controls to your application as (text) fields, and the operations of data controls as command objects (buttons). These drag and drop actions will generate EL bindings in your application and the appropriate properties for the controls that are created. The bindings are represented in a DataControls.dcx file, which points at the data control source, and the page bindings link the reference of the specific page to the data control. For information about the ApplicationFeatures data control, see What You May Need to Know About Custom Springboard Application Features with MAF AMX Content.

For more information about data controls and bindings, see the following:

15.2 About Object Scope Lifecycles

A scope persists those objects in memory that an application may access at runtime using EL expressions. Scopes determine the lifespan and availability of an object, such as an application or a task flow.

At runtime, you pass data to pages by storing the needed data in an object scope where the page can access it. The scope determines the lifespan of an object. Once you place an object in a scope, it can be accessed from the scope using an EL expression. For example, you might create a managed bean named foo, and define the bean to live in the view scope. To access that bean, you would use the expression #{viewScope.foo}.

Mobile Application Framework variables and managed bean references are defined within different object scopes that determine the lifetime and visibility of the variable. MAF supports the following scopes, listed in order of decreasing visibility:

  • Application scope—The object is available for the duration of the application (across features).

  • Page flow scope—The object is available for the duration of a feature (single feature boundary) or task flow, depending on where the page flow-scoped managed bean is defined. If the bean is defined in an unbounded task flow, its scope is the feature. If the bean is defined in a bounded task flow, its scope is limited to the task flow.

  • View scope—The object is available for the duration of the view (single page of a feature).

Object scopes are analogous to global and local variable scopes in programming languages. The wider the scope, the higher the availability of an object. During their lifespan, these objects may expose certain interfaces, hold information, or pass variables and parameters to other objects. For example, a managed bean defined in application scope will be available for use during multiple page requests for the duration of the application. However, a managed bean defined in view scope will be available only for the duration of one page request within a feature.

EL expressions defined in the application scope namespace are available for the life of the application, across feature boundaries. You can define an application scope in one view of an application, and then reference it in another. EL expressions defined in the page flow scope namespace are available for the duration of a feature, within the bounds of a single feature. EL expressions defined in the view scope namespace are available for the duration of the view, within the bounds of a single page of a feature. In addition to these variable-containing scopes, MAF defines scopes that can expose information about device properties and application preferences. These scopes have application-level lifetime and visibility. See About the Managed Beans Category and About the Mobile Application Framework Objects Category.

When determining what scope to register a managed bean with or to store a value in, always try to use the narrowest scope possible. Use the application scope only for information that is relevant to the whole application, such as user or context information. Avoid using the application scope to pass values from one page to another.

Note:

Every object you put in a memory scope is serialized to a JSON DataChangeEvent, and objects returned by any getter method inside this object are also serialized. This can lead to deeply nested object trees that are serialized, which will decrease performance. To avoid serialization of a chain of nested objects, you should define them as transient. See What You May Need to Know About Serialization of Bean Class Variables.

15.2.1 What You May Need to Know About Object Scopes and Task Flows

Use page flow scope to pass data values between activities within a task flow, and use view scope for variables that are only needed within the current view activity.

When determining what scope to use for variables within a task flow, you must only use view or page flow scopes. The application scope persists objects in memory beyond the life of the task flow and therefore compromises the encapsulation and reusable aspects of a task flow. In addition, application scope may keep objects in memory longer than needed, causing unneeded overhead.

When you need to pass data values between activities within a task flow, you must use page flow scope. View scope must be used for variables that are needed only within the current view activity, not across view activities.

15.3 Creating EL Expressions

EL expressions are used in MAF applications to bind attributes to object values determined at runtime. Build EL expressions using the page definition files to automate access to individual objects and their properties without employing code.

You use EL expressions in MAF applications to bind attributes to object values determined at runtime. For example, #{UserList.selectedUsers} might reference a set of selected users, #{user.name} might reference a particular user's name, while #{user.role == 'manager'} would evaluate whether a user is a manager or not. At runtime, a generic expression evaluator returns the List, String, and boolean values of these respective expressions, automating access to the individual objects and their properties without requiring code.

Expressions are not evaluated until they are needed for rendering a value. Because MAF AMX supports only deferred evaluation, an expression using the immediate construction expression ("${}") still parses, but behaves the same as a deferred expression ("#{}"). At runtime, the value of certain UI components (such as an inputText component or an outputText component) is determined by its value attribute. While a component can have static text as its value, typically the value attribute will contain an EL expression that the runtime infrastructure evaluates to determine what data to display. For example, an outputText component that displays the name of the currently logged-in user might have its value attribute set to the expression #{UserInfo.name}. Since any attribute of a component (and not just the value attribute) can be assigned a value using an EL expression, it is easy to build dynamic, data-driven user interfaces. For example, you could hide a component when a set of objects you need to display is empty by using a boolean-valued expression like #{not empty UserList.selectedUsers} in the rendered attribute of the UI component. If the list of selected users in the object named UserList is empty, the rendered attribute evaluates to false and the component disappears from the page.

In a typical application, you would create objects like UserList as a managed bean. The runtime manages instantiating these beans on demand when any EL expression references them for the first time. When displaying a value, the runtime evaluates the EL expression and pulls the value from the managed bean to populate the component with data when the page is displayed. If the user updates data in the UI component, the runtime pushes the value back into the corresponding managed bean based on the same EL expression. See Creating and Using Managed Beans. For more information about EL expressions, see the Java EE tutorial at http://www.oracle.com/technetwork/java/index.html.

Note:

When using an EL expression for the value attribute of an editable component, you must have a corresponding set method for that component, or else the EL expression will evaluate to read-only, and no updates to the value will be allowed.

For example, say you have an inputText component (whose ID is it1) on a page, and you have its value set to #{myBean.inputValue}. The myBean managed bean would have to have get and set methods as follows, in order for the inputText value to be updated:

    public void setIt1(RichInputText it1) {
        this.it1 = it1;
    }
 
    public RichInputText getIt1() {
        return it1;
    }

15.3.1 About Data Binding EL Expressions

MAF builds data binding expressions when you use the Data Controls panel to create UI components. You can also manually add or edit MAF data binding expressions.

When you use the Data Controls panel to create a component, the MAF data binding expressions are created for you. The expressions are added to every component attribute that will either display data from or reference properties of a binding object. Each prebuilt expression references the appropriate binding objects defined in the page definition file. You can edit these binding expressions or create your own, as long as you adhere to the basic MAF binding expression syntax. MAF data binding expressions can be added to any component attribute that you want to populate with data from a binding object, if the attribute supports EL.

A typical MAF data binding EL expression uses the following syntax to reference any of the different types of binding objects in the binding container:

#{bindings.BindingObject.propertyName}

where:

  • bindings is a variable that identifies that the binding object being referenced by the expression is located in the binding container of the current page. All MAF data binding EL expressions must start with the bindings variable.

  • BindingObject is the ID, or for attributes the name, of the binding object as it is defined in the page definition file. The binding objectID or name is unique to that page definition file. An EL expression can reference any binding object in the page definition file, including parameters, executables, or value bindings.

  • propertyName is a variable that determines the default display characteristics of each databound UI component and sets properties for the binding object at runtime. There are different binding properties for each type of binding object. See What You May Need to Know About MAF Binding Properties.

For example, in the following expression:

#{bindings.ProductName.inputValue}

the bindings variable references a bound value in the binding container of the current page. The binding object being referenced is ProductName, which is an attribute binding object. The binding property is inputValue, which returns the value of the first ProductName attribute.

Tip:

While the binding expressions in the page definition file can use either a dollar sign ($) or hash sign (#) prefix, the EL expressions in MAF pages can only use the hash sign (#) prefix.

As stated previously, when you use the Data Controls panel to create UI components, these expressions are built for you. However, you can also manually create them if you need to. The JDeveloper Expression Builder is a dialog that helps you build EL expressions by providing lists of binding objects defined in the page definition files, as well as other valid objects to which a UI component may be bound. It is particularly useful when creating or editing MAF databound expressions because it provides a hierarchical list of MAF binding objects and their most commonly used properties. See What You May Need to Know About MAF Binding Properties.

15.3.2 How to Create an EL Expression

Use the procedure to build expressions, and add logical or mathematical operators to them using the Expression Builder.

You can create EL expressions declaratively using the JDeveloper Expression Builder. You can access the Expression Builder from the Properties window.

Before you begin:

It may be helpful to have an understanding of EL expressions. See Creating EL Expressions.

To use the Expression Builder:

  1. In the Properties window, locate the attribute you wish to modify and use the right-most drop-down menu to select Expression Builder.
  2. Create expressions using the following features:
    • Use the Variables drop-down to select items that you want to include in the expression. These items are displayed in a tree that is a hierarchical representation of the binding objects. Each icon in the tree represents various types of binding objects that you can use in an expression.

      To narrow down the tree, you can either use the drop-down filter or enter search criteria in the search field. The EL accessible objects exposed by MAF are located under the Mobile Application Framework Objects node, which is under the ADF Managed Beans node.

      Tip:

      For more information about these objects, see the MAF Javadoc. See also About the Categories in the Expression Builder.

      Selecting an item in the tree causes it to be moved to the Expression box within an EL expression. You can also type the expression directly in the Expression box.

    • Use the operator buttons to add logical or mathematical operators to the expression.

    Figure 15-1 shows an example of how to create an EL expression from the ADF Managed Beans category. However, you can create EL expressions from any of the categories described in About the Categories in the Expression Builder.

    Figure 15-1 The Expression Builder Dialog

    This image is described in the surrounding text

    Tip:

    For information about using proper syntax to create EL expressions, see the Java EE tutorial at http://www.oracle.com/technetwork/java/index.html.

    Table 15-1 Icons Under the Bindings Node of the Expression Builder

    Icon Description
    This image is described in the surrounding text

    Represents the bindings container variable, which references the binding container of the current page. Opening the bindings node exposes all the binding objects for the current page.

    This image is described in the surrounding text

    Represents the data binding variable, which references the entire binding context (created from all the.cpx files in the application). Opening the data node exposes all the page definition files in the application.

    This image is described in the surrounding text

    Represents an action binding object. Opening a node that uses this icon exposes a list of valid action binding properties.

    This image is described in the surrounding text

    Represents an iterator binding object. Opening a node that uses this icon exposes a list of valid iterator binding properties.

    This image is described in the surrounding text

    Represents an attribute binding object. Opening a node that uses this icon exposes a list of valid attribute binding properties.

    This image is described in the surrounding text

    Represents a list binding object. Opening a node that uses this icon exposes a list of valid list binding properties.

    This image is described in the surrounding text

    Represents a table or tree binding object. Opening a node that uses this icon exposes a list of valid table and tree binding properties.

    This image is described in the surrounding text

    Represents a MAF binding object property. See What You May Need to Know About MAF Binding Properties.

    This image is described in the surrounding text

    Represents a parameter binding object.

    This image is described in the surrounding text

    Represents a bean class.

    This image is described in the surrounding text

    Represents a method.

15.3.2.1 About the Method Expression Builder

The Method Expression Builder in the Properties window is akin to the Expression Builder except that the Method Expression Builder filters the managed beans depending on the selected property.

Table 15-2 shows properties that have the Method Expression Builder option available in the Properties window instead of the Expression Builder option. The only difference between them is that the Method Expression Builder filters out the managed beans depending on the selected property.

Table 15-2 Properties for the Method Expression Builder

Property Element

action

amx:commandButton

action

amx:commandLink

action

amx:listItem

action

amx:navigationDragBehavior

action

dvtm:chartDataItem

action

dvtm:ieDataItem

action

dvtm:timelineItem

action

dvtm:area

action

dvtm:marker

actionListener

amx:listItem

actionListener

amx:commandButton

actionListener

amx:commandLink

binding

amx:actionListener

mapBoundsChangeListener

dvtm:geographicMap

mapInputListener

dvtm:geographicMap

moveListener

amx:listView

rangeChangeListener

amx:listView

selectionListener

amx:listView

selectionListener

amx:filmStrip

selectionListener

dvtm:areaDataLayer

selectionListener

dvtm:pointDataLayer

selectionListener

dvtm:treemap

selectionListener

dvtm:sunburst

selectionListener

dvtm:timelineSeries

selectionListener

dvtm:nBox

selectionListener

dvtm:areaChart

selectionListener

dvtm:barChart

selectionListener

dvtm:bubbleChart

selectionListener

dvtm:comboChart

selectionListener

dvtm:horizontalBarChart

selectionListener

dvtm:lineChart

selectionListener

dvtm:funnelChart

selectionListener

dvtm:pieChart

selectionListener

dvtm:scatterChart

valueChangeListener

amx:inputDate

valueChangeListener

amx:inputNumberSlider

valueChangeListener

amx:inputText

valueChangeListener

amx:selectBooleanCheckbox

valueChangeListener

amx:selectBooleanSwitch

valueChangeListener

amx:selectManyCheckbox

valueChangeListener

amx:selectManyChoice

valueChangeListener

amx:selectOneButton

valueChangeListener

amx:selectOneChoice

valueChangeListener

amx:selectOneRadio

valueChangeListener

dvtm:statusMeterGauge

valueChangeListener

dvtm:dialGauge

valueChangeListener

dvtm:ratingGauge

viewportChangeListener

dvtm:areaChart

viewportChangeListener

dvtm:barChart

viewportChangeListener

dvtm:comboChart

viewportChangeListener

dvtm:horizontalBarChart

viewportChangeListener

dvtm:lineChart

15.3.2.2 About Non EL-Properties

Properties that are not EL-enabled do not have the EL Expression Builder option available in the Properties window.

Table 15-3 shows the properties that do not have the EL Expression Builder option available in the Properties window, because they are not EL-enabled.

Table 15-3 Non EL-Properties

Property Element

id

all elements

facetName

amx:facetRef

failSafeClientHandler

amx:loadingIndicatorBehavior

failSafeDuration

amx:loadingIndicatorBehavior

group

amx:validationBehavior

name

amx:attribute

name

amx:attributeList

name

amx:attributeListIterator

name

amx:facet

ref

amx:attributeList

type

dvtm:attributeGroups

var

amx:carousel

var

amx:filmStrip

var

amx:iterator

var

amx:listView

var

amx:loadBundle

var

dvtm:areaChart

var

dvtm:barChart

var

dvtm:bubbleChart

var

dvtm:comboChart

var

dvtm:funnelChart

var

dvtm:horizontalBarChart

var

dvtm:lineChart

var

dvtm:pieChart

var

dvtm:scatterChart

var

dvtm:sparkChart

var

dvtm:geographicMap

varStatus

amx:attributeListIterator

15.3.3 What You May Need to Know About MAF Binding Properties

When a databound component is created using the Expression Builder, the EL expression might reference specific MAF binding properties, which are defined by Oracle APIs.

When you create a databound component using the Expression Builder, the EL expression might reference specific MAF binding properties. At runtime, these binding properties can define such things as the default display characteristics of a databound UI component or specific parameters for iterator bindings. The binding properties are defined by Oracle APIs. For a full list of the available properties for each binding type, see Table 15-4

Values assigned to certain properties are defined in the page definition file. For example, iterator bindings have a property called RangeSize, which specifies the number of rows the iterator should display at one time. The value assigned to RangeSize is specified in the page definition file, as shown in the following example.

<iterator Binds="ItemsForOrder" RangeSize="25" 
              DataControl="BackOfficeAppModuleDataControl" 
              id="ItemsForOrderIterator" ChangeEventPolicy="ppr"/>

15.3.4 How to Enable Retention of Data Provider State Across Iterators

By specifying the same RSIName attribute on the top-level iterator of each page that is to access a data collection instance, a data control can have multiple instances of a data provider created for it.

To use this functionality, you specify the same RSIName attribute on the top-level iterator (iterator) of each page that is to access the same data collection instance.

The following example shows a hierarchy of iterators in a pageDef file. The last two accessor iterators enable iteration over a second collection of Employee objects as well as the phone numbers of those employees.

<iterator Binds="root"
          RangeSize="25"
          DataControl="BusinessManager"
          id="BusinessManagerIterator"
          RSIName="secondCollection" />
<accessorIterator id="companyIterator"/
                  MasterBinding="BusinessManagerIterator"
                  Binds="company"
                  RangeSize="25"
                  DataControl="BusinessManager"
                  BeanClass="mobile.Company"/>
<accessorIterator id="employeesIterator"
                  MasterBinding="companyIterator"
                  Binds="employees"
                  RangeSize="25"
                  DataControl="BusinessManager"
                  BeanClass="mobile.Employee"/>
<accessorIterator id="phoneNumbersIterator"
                  MasterBinding="employeesIterator"
                  Binds="phoneNumbers"
                  RangeSize="25"
                  DataControl="BusinessManager"
                  BeanClass="mobile.PhoneNumber"/>
<accessorIterator id="employeesIterator2"
                  MasterBinding="companyIterator"
                  Binds="employees"
                  RangeSize="25"
                  DataControl="BusinessManager"
                  BeanClass="mobile.Employee"
                  RSIName="secondCollection" />
<accessorIterator id="phoneNumbersIterator2"
                  MasterBinding="employeesIterator2"
                  Binds="phoneNumbers"
                  RangeSize="25"
                  DataControl="BusinessManager"
                  BeanClass="mobile.PhoneNumber" />

15.3.5 How to Reference Binding Containers

MAF uses different root EL expressions to reference the binding containers of an active screen and an inactive screen. Review the listed properties that can be used in EL expressions to access MAF AMX binding objects.

You can reference the binding container of the active screen by the root EL expression "#{bindings}" and you can reference binding container of another screen through the expression "#{data.PageDefName}". The MAF AMX binding objects are referenced by name from the binding container "#{bindings.Name}".

Table 15-4 shows a partial list of the properties that you can use in EL expressions to access values of the MAF AMX binding objects at runtime. The properties appear in alphabetical order.

Table 15-4 Runtime EL Properties of MAF Bindings

Runtime Property Description Iterator Action attributeValues Tree

class

Returns the Java class object for the runtime binding.

Yes

Yes

Yes

Yes

collectionModel

Exposes a collection of data. EL expressions used within a component that is bound to a collectionModel can be referenced with a row variable Foot 1, which will resolve the expression for each element in the collection.

No

No

No

Yes

collectionModel.makeCurrent

Causes the selected row to become the current row in the iterator for this binding.

No

No

No

Yes

collectionModel.selectedRow

Returns a reference to the selected row.

No

No

No

Yes

currentRow

Returns a reference to the current row or data object pointed to by the iterator (for example, built-in navigation actions).

Yes

No

No

No

currentRow.dataprovider

Returns a reference to the current row or data object pointed to by the iterator. (This is the same object returned by currentRow, just with a different syntax).

Yes

No

No

No

enabled

Returns true or false, depending on the state of the action binding. For example, the action binding may be enabled (true) or disabled (false) based on the currency (as determined, for example, when the user clicks the First, Next, Previous, or Last navigation buttons).

No

Yes

No

No

execute

Invokes the named action or methodAction binding when resolved.

No

Yes

No

No

format

This is a shortcut for hints.format.

No

No

Yes

Yes

hints

Returns a list of name-value pairs for UI hints for all display attributes to which the binding is associated.

No

No

Yes

Yes

inputValue

Returns the value of the first attribute to which the binding is associated.

No

No

Yes

No

items

Returns the list of values associated with the current list-enabled attribute.

No

No

Yes

No

label

Available as a child of hints or direct child of an attribute. Returns the label (if supplied by control hints) for the first attribute of the binding.

No

No

Yes

Yes

name

Returns the id of the binding as declared in the PageDef.xml file.

Yes

Yes

Yes

Yes

rangeSize

Returns the range size of the iterator binding's row set. This allows you to determine the number of data objects to bind from the data source.

Yes

No

No

Yes

result

Returns the result of a method that is bound and invoked by a method action binding.

No

Yes

No

No

updateable

Available as a child of hints or direct child of an attribute. Returns true if the first attribute to which the binding is associated is updateable. Otherwise, returns false.

No

No

Yes

Yes

viewable

Available as a child of Tree. Resolves at runtime whether this binding and the associated component should be rendered or not.

No

No

No

Yes

Footnote 1

The EL term row is used within the context of a collection component; row simply acts as an iteration variable over each element in the collection whose attributes can be accessed by a MAF AMX binding object when the collection is rendered. Attribute and list bindings can be accessed through the row variable. The syntax for such expressions will be the same as those used for accessing binding objects outside of a collection, with the row variable prepended as the first term: #{row.bindings.Name.property}.

15.3.6 About the Categories in the Expression Builder

The following categories are available in the Expression Builder for MAF AMX pages:

15.3.6.1 About the Bindings Category

You can configure the properties that are supported for different binding types. Review the supported bindings and properties in the Bindings category.

This section lists the options available under the Bindings category. The bindings and data nodes display the same set of supported bindings and properties. Table 15-5 lists available binding types along with the properties that are supported for each binding type. The securityContext node supports the following properties:

  • authenticated

  • userGrantedPrivilege

  • userInRole

  • userName

For example:

#{securityContext.authenticated}
#{securityContext.userGrantedPrivilege['submit_privilege']}
#{securityContext.userInRole[‘manager_role']}
#{securityContext.userName}

Table 15-5 Supported Binding Types

Binding Type Properties

accessorIterator

class

currentRow: dataProvider

name

rangeSize

action

class

enabled

execute

name

attributeValues

autoSubmit

category

class

controlType

displayHeight

displayHint

displayWidth

filedorder

format

hints: allows.read, allows.update, autoSubmit, category, controlType, displayHeight, displayHint, displayWidth, filedorder, format, label, mandatory, precision, tooltip, updateable

inputValue

items

iteratorBinding

label

mandatory

name

precision

tooltip

updateable

button

autoSubmit

category

class

controlType

displayHeight

displayHint

displayWidth

filedorder

format

hints: allows.read, allows.update, autoSubmit, category, controlType, displayHeight, displayHint, displayWidth, filedorder, format, label, mandatory, precision, tooltip, updateable

inputValue

items

iteratorBinding

label

mandatory

name

precision

tooltip

updateable

invokeAction

always

deferred

iterator

class

currentRow: dataProvider

name

rangeSize

list

autoSubmit

category

class

controlType

displayHeight

displayHint

displayWidth

filedorder

format

hints: format, allows.read, allows.update, autoSubmit, category, controlType, displayHeight, displayHint, displayWidth, filedorder, format, label, mandatory, precision, tooltip, updateable

inputValue

items

iteratorBinding

label

mandatory

name

precision

tooltip

updateable

methodAction

class

enabled

execute

name

operationEnabled

operationInfo

paramsMap

result

methodIterator

class

currentRow: dataProvider

name

rangeSize

searchAction

class

enabled

execute

name

operationEnabled

operationInfo

paramsMap

result

tree

category

class

collectionModel: bindings, makeCurrent, selectedRow, <AttrName>

displayHeight

displayHint

displayWidth

filedorder

format

hints: category, displayHeight, displayHint, displayWidth, filedorder, format, label, mandatory, precision, tooltip, updateable, <AttrName>

iteratorBinding

label

mandatory

name

precision

rangeSize

tooltip

updateable

viewable

variable

class

currentRow: dataProvider

name

variableIterator

class

currentRow: dataProvider

name

15.3.6.2 About the Managed Beans Category

Review the options available in the Managed Beans category.

This section lists the options available under the Managed Beans category.

  • applicationScope: Managed Beans > applicationScope node contains everything that is defined at the application level (for example, application-scoped managed beans).

  • pageFlowScope: Managed Beans > pageFlowScope node contains everything that is defined at the page flow level (for example, page flow-scoped managed beans).

  • viewScope: Managed Beans > viewScope node contains everything that is defined at the view level (for example, view-scoped managed beans).

The MAF runtime will register itself as a listener on managed bean property change notifications so that EL expressions bound to UI components that reference bean properties will update automatically if the value of the property changes. Sourcing these notifications requires some additional code in the property accessors of the beans. To automatically generate the necessary code to source notifications from the property accessors of the beans, select the Notify listeners when property changes checkbox in the Generate Accessors dialog (see Figure 15-2).

Figure 15-2 Notify Listeners When Property Changes

This image is described in the surrounding text

It is not necessary to add this code to simply reference bean methods or properties through EL, but it is necessary to keep the rendering of any EL expressions in the active form that depend on values stored in the bean current if those values change, especially if the change is indirect, such as a side effect of executing a bean method that changes one or more property values. For information about property changes and the PropertyChangeSupport class, see About Data Change Events.

The following example illustrates how to retrieve a value bound to another managed bean attribute programmatically.

public void someMethod() {
   Object value = AdfmfJavaUtilities.evaluateELExpression(
                       "#{applicationScope.MyManagedBean.someProperty}");
   ...
}

The following example illustrates how to execute bindings programmatically from a managed bean.

public void someMethod() {
   Object value = AdfmfJavaUtilities.evaluateELExpression(
                     "#{bindings.someDataControlMethod.execute}");
 ...
}

Note:

If you declare a managed bean within the applicationScope of a feature but then try to reference that bean through EL in another feature at design time, you will see a warning in the design time about invalid EL. This warning is due to the fact that the design time cannot find a reference in the current project for that bean. You can reference that bean at runtime only if you first visit the initial feature where you declared the bean and the bean is instantiated before you access it through EL in another feature. This is not the case for the PreferenceValue element as it uses the Name attribute value as the node label.

15.3.6.3 About the Mobile Application Framework Objects Category

The Mobile Application Framework Objects category includes those objects that are defined in MAF, and can be referenced using EL.

The Mobile Application Framework Objects category lists various objects defined in MAF that can be referenced using EL, such as object scopes.

MAF variables and managed bean references are defined within different object scopes that determine the lifetime and visibility of the variable. In order of decreasing visibility, they are application scope, page flow scope, and view scope. See About Object Scope Lifecycles.

In addition to these variable-containing scopes, MAF defines scopes that can expose information about device properties and application preferences. These scopes have application-level lifetime and visibility.

The following are available under the Mobile Application Framework Objects category:

  • applicationScope: The applicationScope node contains everything that is defined at the application level (for example, application-scoped managed beans). EL variables defined in the application scope are available for the life of the application, across feature boundaries.

  • deviceScope: The deviceScope node exposes information about device properties. The deviceScope has application-level lifetime and visibility.

  • feature: The feature node exposes feature-level data. The feature object exposes the dataControlContextDepthand maximumDataControlContextDepthproperties. You can obtain values for these properties using #{feature.dataControlContextDepth}and #{feature.maximumDataControlContextDepth}. These two properties are read only.

  • pageFlowScope: The pageFlowScope node contains everything that is defined at the page flow level (for example, page flow-scoped managed beans). EL variables defined in the page flow scope namespace are available for the duration of a feature, within the bounds of a single feature.

  • preferenceScope: The preferenceScope node contains all the application and feature preferences.

    Preference elements use the Id attribute value as the node label in the Expression Builder, except for the PreferenceValue element. The PreferenceValue element uses the Name attribute value as the node label in the Expression Builder.

    Note:

    Where string tokens in EL expressions contain a dot (".") or any special character, or a reserved word like default, the Expression Builder surrounds such string tokens with a single quote and bracket. When the feature ID or preference component ID contains a dot, the Expression Builder displays each part of the ID that is separated by a dot as a separate property in the preferenceScope hierarchy. The expression generated also takes each part of the ID separated by a dot as a separate property.

    Following are some sample preferenceScope EL expressions:

    "#{preferenceScope.feature.oracle.hello.SampleGroup1.label}"
    
    "#{preferenceScope.application.OracleMobileApp.Edition['default']}"
    
  • viewScope: This node contains everything that is defined at the view level (for example, view-scoped managed beans). EL variables defined in the view scope namespace are available for the duration of the view, within the bounds of a single page of a feature.

  • row: The row object is an intermediate variable that is a shortcut to a single provider in the collectionModel. Its name is the value of the var attribute of the parent component (such as List View or Carousel).

    Note:

    It is not possible to evaluate #{row} or properties of row using AdfmfJavaUtilities.evaluateELExpression. These expressions will return a null value.

  • viewControllerBundle

    This is the name of the resource bundle variable that points to a resource bundle defined at the project level. This node is shown only after the amx:loadBundle element has been dropped and a resource bundle has been created. The name of this node will vary as it depends on the variable name of amx:loadBundle. This node will display all strings declared in the bundle.

    The following example shows an example of AMX code for viewControllerBundle.

    <amx:loadBundle basename="mobile.ViewControllerBundle" var="viewcontrollerBundle"/>
    

15.3.7 About EL Events

EL events determine how the MAF AMX UI functions because they synchronize the updating of expressions with common terms. Any change in the value of an underlying expression generates an event to all listeners for that value.

EL events play a significant role in the functioning of the MAF AMX UI, enabling expressions with common terms to update in sync with each other.

EL expressions can refer to values in various contexts. The following example shows the creation of two Input Number Slider components, with each component tied to an applicationScope value. The output text then uses EL to display a simple addition equation along with the calculated results. When the framework parses the EL expression in the output text labels, it determines that the expression contains references to two values and creates event listeners (see Using Event Listeners) for the output text on those two values. When the value of the underlying expression changes, an event is generated to all listeners for that value.

Note:

If you are referencing properties on a managed bean (as opposed to scope objects) you have to add the listeners. See About the Managed Beans Category.

<amx:inputNumberSlider id="slider1" label="X" value="#{applicationScope.X}"/>
<amx:inputNumberSlider id="slider2" label="Y" value="#{applicationScope.Y}"/>
<amx:outputText id="ot1" value="#{applicationScope.X} + 
       #{applicationScope.Y} = #{applicationScope.X + applicationScope.Y}"/>

In the example above, two components are updating one value each, and one component is consuming both values. The following example shows that the behavior would be identical if a third Input Number Slider component is added that references one of the existing values.

<amx:inputNumberSlider id="slider1" label="X" value="#{applicationScope.X}"/>
<amx:inputNumberSlider id="slider2" label="Y" value="#{applicationScope.Y}"/>
<amx:outputText id="ot1" value="#{applicationScope.X} + 
       #{applicationScope.Y} = #{applicationScope.X + applicationScope.Y}"/>
<amx:inputNumberSlider id="slider3" label="X" value="#{applicationScope.X}"/>

In the example above, when either Input Number Slider component updates #{applicationScope.X}, the other is automatically updated along with the Output Text.

15.3.8 How to Use EL Expressions Within Managed Beans

Use the EL expressions that JDeveloper provides. Create new EL expressions, access, set, or invoke EL expressions within a managed bean.

While JDeveloper creates many needed EL expressions for you, and you can use the Expression Builder to create those not built for you, there may be times when you need to access, set, or invoke EL expressions within a managed bean.

The following example shows how you can get a reference to an EL expression and return (or create) the matching object.

public static Object resolveExpression(String expression) {
  return AdfmfJavaUtilities.evaluateELExpression(expression);
}

The following example shows how you can resolve a method expression.

public static Object resloveMethodExpression(String expression,
                                             Class returnType,
                                             Class[] argTypes,
                                             Object[] argValues) {
  MethodExpression methodExpression = AdfmfJavaUtilities.getMethodExpression(expression,
                                                                             returnType,
                                                                             argTypes);
  return methodExpression.invoke(AdfmfJavaUtilities.getAdfELContext(), argValues);
}

The following example shows how you can set a new object on a managed bean.

public static void setObject(String expression, Object newValue) {
  AdfmfJavaUtilities.setELValue(expression, newValue);
}

15.4 Creating and Using Managed Beans

Managed beans are Java classes registered with applications by means of configuration files, which when parsed, provide access to the properties and methods of the beans.

Managed beans are Java classes that you register with the application using various configuration files. When the MAF application starts up, it parses these configuration files and the beans are made available and can be referenced in an EL expression, allowing access to the properties and methods of the beans. Whenever a managed bean is referenced for the first time and it does not already exist, the Managed Bean Creation Facility instantiates the bean by calling the default constructor method on the bean. If any properties are also declared, they are populated with the declared default values.

Often, managed beans handle events or some manipulation of data that is best handled at the front end. For a more complete description of how to use managed beans, see the Java EE tutorial at http://www.oracle.com/technetwork/java/index.html.

Best Practice:

Use managed beans to store only bookkeeping information, for example the current user. All application data and processing should be handled by logic in the business layer of the application.

Note:

EL expressions must explicitly include the scope to reference the bean. For example, to reference the MyBean managed bean from the pageFlowScope scope, your expression would be #{pageFlowScope.MyBean}.

15.4.1 How to Create a Managed Bean in JDeveloper

Use the procedure to create a managed bean, and register it with the MAF application.

You can create a managed bean and register it with the MAF application at the same time using the Overview editor for the adfc-mobile-config.xml file.

Before you begin:

It may be helpful to have an understanding of managed beans. See Creating and Using Managed Beans.

To create and register a managed bean:

  1. In the Applications window, double-click adfc-mobile-config.xml.
  2. In the Editor window, click the Overview tab.
  3. In the Overview editor, click the Managed Beans navigation tab.

    Figure 15-3 shows the editor for the adfc-mobile-config.xml file.

    Figure 15-3 Managed Beans in the adfc-mobile-config.xml File

    This image is described in the surrounding text
  4. Click the Add icon to add a row to the Managed Bean table.
  5. In the Create Managed Bean dialog, enter values. Click Help for more information about using the dialog. Select the Generate Class If It Does Not Exist option if you want JDeveloper to create the class file for you. You can also open the Create Managed Bean dialog from the Properties window, by selecting one of the listener properties and clicking the Edit button. From there you can create a new managed bean and corresponding method.

    Note:

    When determining what scope to register a managed bean with or to store a value in, always try to use the narrowest scope possible. See About Object Scope Lifecycles.

  6. You can optionally add managed properties for the bean. When the bean is instantiated, any managed properties will be set with the provided value. With the bean selected in the Managed Bean table, click the New icon to add a row to the Managed Properties table. In the Properties window, enter a property name (other fields are optional).

    Note:

    While you can declare managed properties using this editor, the corresponding code is not generated on the Java class. You must add that code by creating private member fields of the appropriate type, and then by choosing the Generate Accessors menu item on the context menu of the code editor to generate the corresponding get and set methods for these bean properties.

15.4.2 What Happens When You Use JDeveloper to Create a Managed Bean

Creating a managed bean and generating the Java file in JDeveloper causes the IDE to create a stub class. Add logic to the page so that an EL expression can reference it using the given name of the stub class.

When you create a managed bean and elect to generate the Java file, JDeveloper creates a stub class with the given name and a default constructor. The following example shows the code added to the MyBean class stored in the view package.

package view;
 
public class MyBean {
    public MyBean() {
    }
}

You now must add the logic required by your page. You can then refer to that logic using an EL expression that refers to the managed-bean-name given to the managed bean. For example, to access the myInfo property on the my_bean managed bean, the EL expression would be:

#{my_bean.myInfo}

JDeveloper also adds a managed-bean element to the adfc-mobile-config.xml file (or to the task flow file that is being edited). The following example shows the managed-bean element created for the MyBean class.

<managed-bean>
  <managed-bean-name>my_bean</managed-bean-name>
  <managed-bean-class>view.MyBean</managed-bean-class>
  <managed-bean-scope>application</managed-bean-scope>
</managed-bean>

15.5 Exposing Business Services with Data Controls

Create data controls to declaratively bind UI components to business services. Use the Create Data Control menu option to generate data controls.

Once you have the services of the application in place, use JDeveloper to create data controls that provide the information needed to declaratively bind UI components to those services.

You generate data controls with the Create Data Control menu item. Data controls consist of one or more XML metadata files that define the capabilities of the services that the bindings can work with at runtime. The data controls work in conjunction with the underlying services.

15.5.1 How to Create Data Controls

Create an application workspace, add the business services, and then use the procedure to create a data control. You can also use the context menu of the class or object on which the data control is based to create a data control.

You create adapter-based data controls from within the Applications window of JDeveloper.

Before you begin:

It may be helpful to have a general understanding of using data controls. See Exposing Business Services with Data Controls.

You will need to complete this task:

  • Create an application workspace and add the business services on which you want to base your data control. For information on creating an application workspace, see Creating a MAF Application.

To create a data control:

  1. Right-click the top-level node for the data model project in the application workspace and select New and then From Gallery.
  2. In the New Gallery, expand Business Tier, select Data Controls, select the type of data control that you want to create, and click OK.
  3. Complete the remaining steps of the wizard.

Note:

In some cases, you can create a data control by right-clicking the class or object on which the data control will be based and selecting Create Data Control.

15.5.2 What Happens in Your Project When You Create a Data Control

JDeveloper creates the definition file for the data control which opens in the overview editor, and the hierarchy of the file is displayed in the Data Controls panel.

When you create a data control, JDeveloper creates the data control definition file (DataControls.dcx), opens the file in the overview editor, and displays the hierarchy of the file in the Data Controls panel. This file enables the data control to work directly with the services and the bindings.

You can see the code from the corresponding XML file by clicking the Source tab in the editor window.

15.5.2.1 DataControls.dcx Overview Editor

View the hierarchy of data control objects and the exposed methods of a data model in the overview editor for the DataControls.dcx file. Use the editor to edit the settings for a data control object.

The overview editor for the DataControls.dcx file provides a view of the hierarchies of data control objects and exposed methods of your data model.

See Table 15-6 for a description of the icons that are used in the overview editor and Data Controls panel.

You can change the settings for a data control object by selecting the object and clicking the Edit icon. See How to Edit a Data Control.

Figure 15-4 shows the DataControls.dcx file in the overview editor.

Figure 15-4 DataControls.dcx File in the Overview Editor

This image is described in the surrounding text

15.5.2.2 Data Controls Panel

The Applications window displays the Data Controls panel after a data control has been created. Drag nodes from the Data Controls panel to create databound UI components for a page.

The Data Controls panel serves as a palette, from which you can create databound UI components by dragging nodes from the Data Controls panel to the design editor for a page. The Data Controls panel appears in the Applications window once you have created a data control. Figure 15-5 shows the Data Controls panel for a sample application.

Figure 15-5 Data Controls Panel

This image is described in the surrounding text

15.5.3 Data Control Built-in Operations

The data control framework defines a standard set of operations for data controls that are implemented by business services. The type of the data control and the functionality of the business service differ in the available operations.

The data control framework defines a standard set of operations for data controls. These operations are implemented using functionality of the underlying business service. At runtime, when one of these data collection operations is invoked by name by the data binding layer, the data control delegates the call to an appropriate service method to handle the built-in functionality. For example, in bean data controls, the Next operation relies on the iterator of the bean collection.

Most of the built-in operations affect the current row. However, the execute operation refreshes the data control itself.

The operations available vary by data control type and the functionality of the underlying business service. Here is the full list of built-in operations:

  • Create: Creates a new row that becomes the current row. This new row is also added to the row set.

  • CreateInsert: Creates a new row that becomes the current row and inserts it into the row set.

  • Create With Parameters: Uses named parameters to create a new row that becomes the current row and inserts it into the row set.

  • Delete: Deletes the current row.

  • Execute: Refreshes the data collection by executing or reexecuting the accessor method.

    ExecuteWithParams: Refreshes the data collection by first assigning new values to variables that passed as parameters, then executing or reexecuting the associated query. This operation is only available for data control collection objects that are based on parameterized queries.

  • First: Sets the first row in the row set to be the current row.

  • Last: Sets the last row in the row set to be the current row.

  • Next: Sets the next row in the row set to be the current row.

  • Next Set: Navigates forward one full set of rows.

  • Previous: Sets the previous row in the row set to be the current row.

  • Previous Set: Navigates backward one full set of rows.

  • removeRowWithKey: Tries to find a row using the serialized string representation of the row key passed as a parameter. If found, the row is removed.

  • setCurrentRowWithKey: Tries to find a row using the serialized string representation of the row key passed as a parameter. If found, that row becomes the current row.

  • setCurrentRowWithKeyValue: Tries to find a row using the primary key attribute value passed as a parameter. If found, that row becomes the current row.

15.5.3.1 addXXX and removeXXX Methods of Data Control

Although most built-in operations operate automatically, built-in operations such as Create and Delete that use the addXXX and removeXXX methods require the addition of some method handlers to work. The addXXX and removeXXX methods automatically refresh the collection and trigger data change events.

Most of the built-in operations operate on the collection automatically. There are several operations that require the developer to write some method handlers in order to have these operations work. In order to use the Create operation, it is necessary for the developer to write method handler for addXXX. The Create operation also does the operation of CreateInsert because it inserts the record into the current collection. The CreateInsert operation is not used for MAF collections. Similarly, for Delete operation, the developer will have to write method handler for removeXXX.

The addXXX and removeXXX methods automatically refresh the collection and fire data change events, and the developer does not have to exclusively refresh the provider. The data objects are used by the built-in operations of the data control, such as addXXX and removeXXX methods, which are used by the Create and Delete built-in operations.

  • addXXX: This method returns the unique Id to the Data Control framework. For example,
    public void addDeptBean(DeptBean dept)
    {
    deptCollection.add(dept);
    }
    
    where DeptBean is the class name and dept is the object.
  • removeXXX: This method removes the object. For example,
    public void removeDeptBean(DeptBean dept)
    {
    deptCollection.remove(dept);
    }
    
    where dept is the object of class DeptBean.

The CRUDDemo sample application is located in the PublicSamples.zip file within the jdev_install/jdeveloper/jdev/extensions/oracle.maf/Samples directory on your development computer.

15.6 Creating Databound UI Components from the Data Controls Panel

Drag and drop a data control object from the Data Controls panel onto the editor for a page to create a specific UI component. JDeveloper automatically creates the code and objects needed to bind the component to the data control that you selected.

In the Data Controls panel, each data control object is represented by a specific icon. Table 15-6 describes what each icon represents, where it appears in the Data Controls panel hierarchy, and what components it can be used to create.

Table 15-6 Data Controls Panel Icons and Object Hierarchy

Icon Name Description Used to Create...
This image is described in the surrounding text

Data Control

Represents a data control.

Serves as a container for the other objects and is not used to create anything.

This image is described in the surrounding text

Collection

Represents a named data collection returned by an accessor method or operation.

Forms, tables, graphs, trees, range navigation components, master-detail components, and selection list components

This image is described in the surrounding text

Structured Attribute

Represents a returned object that is neither a Java primitive type (represented as an attribute) nor a collection of any type.

Forms, label, text field, date, list of values, and selection list components.

This image is described in the surrounding text

Attribute

Represents a discrete data element in an object (for example, an attribute in a row).

Label, text field, date, list of values, and selection list components.

This image is described in the surrounding text

Key Attribute

Represents an object attribute that has been declared as a primary key attribute, either in data control structure file or in the business service itself.

Label, text field, date, list of values, and selection list components.

This image is described in the surrounding text

Method

Represents a method or operation in the data control or one of its exposed structures that may accept parameters, perform some business logic and optionally return single value, a structure, or a collection.

Command components.

For methods that accept parameters: command components and parameterized forms.

This image is described in the surrounding text

Method Return

Represents an object that is returned by a method or other operation. The returned object can be a single value or a collection.

A method return appears as a child under the method that returns it. The objects that appear as children under a method return can be attributes of the collection, other methods that perform actions related to the parent collection, or operations that can be performed on the parent collection.

For single values: text fields and selection lists.

For collections: forms, tables, trees, and range navigation components.

When a single-value method return is dropped, the method is not invoked automatically by the framework. To invoke the method, you can drop the corresponding method as a button. If the form is part of a task flow, you can create a method activity to invoke the method.

This image is described in the surrounding text

Operation

Represents a built-in data control operation that performs actions on the parent object.

UI command components, such as buttons and links.

This image is described in the surrounding text

Parameter

Represents a parameter value that is declared by the method or operation under which it appears.

Label, text, and selection list components.

15.6.1 How to Use the Data Controls Panel

The Data Controls Panel provides a predefined set of UI components to build an application. Use the procedure to create UI components using the context menu that is displayed when you select a data collection from the panel.

JDeveloper provides you with a predefined set of UI components from which to select for each data control item you can drop.

Before you begin:

It may be helpful to have an understanding of the different objects in the Data Controls panel. See Creating Databound UI Components from the Data Controls Panel.

You will need to complete these tasks:

To use the Data Controls panel to create UI components:

  1. Select an item in the Data Controls panel and drag it onto the visual editor for your page. For a definition of each item in the panel, see Table 15-6.
  2. From the ensuing context menu, select a UI component.

    When you drag an item from the Data Controls panel and drop it on a page, JDeveloper displays a context menu of all the default UI components available for the item you dropped. The components displayed are based on the libraries in your project.

    Figure 15-6 shows the context menu displayed when a data collection from the Data Controls panel is dropped on a page.

    Figure 15-6 Dropping Component From Data Controls Panel

    This image is described in the surrounding text

    Depending on the component you select from the context menu, JDeveloper may display a dialog that enables you to define how you want the component to look. For example, if you select Form from the context menu, the Edit Form Fields dialog opens. Once you select a component, JDeveloper inserts the UI component on the page in the visual editor.

    The UI components selected by default are determined first by any UI hints set on the corresponding business object. If no UI hints have been set, then JDeveloper uses input components for standard forms and tables, and output components for read-only forms and tables. Components for lists are determined based on the type of list you chose when dropping the data control object.

    By default, the UI components created when you use the Data Controls are bound to attributes in the MAF data control and may have built-in features, such as:

    • Databound labels

    • Tooltips

    • Formatting

    • Basic navigation buttons

    • Validation, if validation rules are attached to a particular attribute.

    The default components are fully functional without any further modifications. However, you can modify them to suit your particular needs.

    Tip:

    If you want to change the type of MAF databound component used on a page, the easiest method is to use either the visual editor or the structure window to delete the component, and then drag and drop a new one from the Data Controls panel. When you use the visual editor or the structure window to delete a databound component from a page, if the related binding objects in the page definition file are not referenced by any other component, JDeveloper automatically deletes those binding objects for you (automatic deletion of binding objects will not happen if you use the source editor).

15.6.2 What Happens When You Use the Data Controls Panel

JDeveloper creates files to define the binding context for the application, a registry for the bindings file, and the binding container for every page in the application. Also added are components of the page, libraries, files, and configuration elements required by the UI components.

When an application is built using the Data Controls panel, JDeveloper does the following:

  • Creates a DataBindings.cpx file in the default package for the project (if one does not already exist), and adds an entry for the page.

    A DataBindings.cpx files defines the binding context for the application. The binding context is a container object that holds a list of available data controls and data binding objects. The DataBindings.cpx file maps individual pages to the binding definitions in the page definition file and registers the data controls used by those pages. See What You May Need to Know About Generated Drag and Drop Artifacts.

  • Creates the adfm.xml file in the META-INF directory. This file creates a registry for the DataBindings.cpx file, which allows the application to locate it at runtime so that the binding context can be created.

  • Adds a page definition file (if one does not already exist for the page) to the page definition subpackage. The default subpackage is mobile.pageDefs in the adfmsrc directory.

    Tip:

    You can set the package configuration (such as name and location) in the ADF Model settings page of the Project Properties dialog (accessible by double-clicking the project node).

    The page definition file (pageNamePageDef.xml) defines the binding container for each page in the view layer of an application. The binding container provides runtime access to all the binding objects for a page. For more information about the page definition file, see What You May Need to Know About Generated Drag and Drop Artifacts.

    Tip:

    The current binding container is also available from AdfContext for programmatic access.

  • Configures the page definition file, which includes adding definitions of the binding objects referenced by the page.

  • Adds the given component to the page.

    These prebuilt components include the data binding expression language (EL) expressions that reference the binding objects in the page definition file. See About Data Binding EL Expressions.

  • Adds all the libraries, files, and configuration elements required by the UI components. For more information on the artifacts required for databound components, see What Happens When You Create a MAF Application.

15.7 What Happens at Runtime: How the Binding Context Works

At runtime, a binding context manages the interactions between MAF bindings on a page and the business services. Created from the DataBindings.cpx file and the page definition files, the binding context is a map that contains references for data control or binding container objects on demand.

When a page contains MAF bindings, at runtime the interaction with the business services initiated from the client or controller is managed by the application through a single object known as the binding context. The binding context is a runtime map (named data and accessible through the EL expression #{data}) of all data controls and page definitions within the application.

The MAF creates the binding context from the application, DataBindings.cpx, and page definition files, as shown in Figure 15-7. The union of all the DataControls.dcx files and any application modules in the workspace define the available data controls at design time, but the DataBindings.cpx file defines what data controls are available to the application at runtime. The DataBindings.cpx file lists all the data controls that are being used by pages in the application and maps the binding containers, which contain the binding objects defined in the page definition files, to web page URLs. The page definition files define the binding objects used by the application pages. There is one page definition file for each page.

The binding context does not contain live instances of these objects. Instead, it is a map that contains references that become data control or binding container objects on demand. When the object (such as a page definition) is released from the application (for example when a task flow ends or when the binding container or data control is released at the end of the request), data controls and binding containers turn back into reference objects. For more information about the DataBindings.cpx file, see What You May Need to Know About Generated Drag and Drop Artifacts.

Figure 15-7 File Binding Context Runtime Usage

This image is described in the surrounding text

Note:

Carefully consider the binding styles you use when configuring components. More specifically, combining standard bindings with managed bean bindings will frequently result in misunderstood behaviors because the class instances are unlikely to be the same between the binding infrastructure and the managed bean infrastructure. If you mix bindings, you may end up calling behavior on an instance that is not directly linked to the UI.

For more information on working with bindings in MAF, see the following:

15.8 Configuring Data Controls

Create a data control, and then create and modify data control structure files that contain its elements. Use the overview editor for the.dcx file to generate a data control structure file.

When you create a data control, a standard set of values and behaviors are assumed for the data control. For example, the data control determines how the label for an attribute will display in a client. You can configure these values and behaviors by creating and modifying data control structure files that correspond to the elements of the data control. You first generate a data control structure file using the overview editor for the.dcx file.

15.8.1 How to Edit a Data Control

Create a data control, and then use the procedure to edit the data control.

You can make a data control configurable by using the overview editor for the DataControls.dcx file to create data control structure files that correspond to objects encompassed by the data control. You can then edit the individual data control structure files.

Before you begin:

It may be helpful to have a general understanding of data control configuration. See Configuring Data Controls.

You will need to complete this task:

To edit a data control:

  1. In the Applications window, double-click DataControls.dcx.
  2. In the overview editor, select the object that you would like to configure and click the Edit icon to generate a data control structure file, as shown in Figure 15-8.

    Figure 15-8 Edit Button in Data Controls Registry

    This image is described in the surrounding text
  3. In the overview editor of the data control structure file, make the desired modifications.

15.8.2 What Happens When You Edit a Data Control

When a data control is edited, JDeveloper creates a data control structure file for the relevant data collection, and opens the file in the overview editor. You can then add or edit configurations for the data control.

When you edit a data control, JDeveloper creates a data control structure file that contains metadata for the affected collection and opens that file in the overview editor. This file stores configuration data for the data control that is specific to that collection, such as any UI hints or validators that you have specified for the data object.

A data control structure file has the same base name as the data object with which it corresponds. For example, if you click the Edit icon when you have a collection node selected that corresponds with the Customer.java entity bean, the data control structure file is named Customer.xml. The data control structure file is generated in a package that corresponds to the package of the bean class, but with persdef prepended to the package name. For example, if the Customer.java bean is in the model package, the Customer.xml data control definition file is generated in the persdef.model package. Once a data control structure file has been generated, you can use the overview editor for that file to make further configurations.

A data control structure file contains the following information:

  • Attributes: Describes all of the attributes on the service. For example, for entity beans, there is an attribute for each bean property that is mapped to a database column. You can also add transient attributes. You can set UI hints that define how these attributes will display in the UI. You can also set other properties, such as whether the attribute value is required, whether it must be unique, and whether it is visible. For more information, see Working with Attributes.

    You can also set validation for an attribute and create custom properties. For more information on validation, see Validating Attributes.

  • Accessors: Describes data control elements that return result sets.

  • Operations: Describes methods on the data object that are used by the built-in operations of the data control, such as add and remove methods, which are used by the Create and Delete built-in operations, respectively.

Figure 15-9 shows the data control structure file for the Item bean.

Figure 15-9 Data Control Structure File in the Overview Editor

This image is described in the surrounding text

Note:

The overview editor of a data control structure file shows all of the attributes, accessors, and operations that are associated with the data object. However, the XML source of the data control structure file only contains definitions for elements that you have edited. The base elements are introspected from the data object. Also, when you make changes to the underlying data object, the data control inherits those changes.

15.8.3 What You May Need to Know About MDS Customization of Data Controls

Data control structure files are necessary for objects within a data control if those objects are to be made available to Oracle Metadata Services customization. Edit and add metadata to every data control object so that a data control structure file is generated for it.

If you wish for all of the objects that are encompassed by the data control to be available for Oracle Metadata Services (MDS) customization, the packaged application must contain data control structure files for those objects.

When you create a data control based on the adapter framework, data control structure files are not generated by default, since they are not needed by the data control if you do not add metadata to a given object. Typically, a data control structure file is only generated for a data control object once you edit the data control to add declarative metadata (such as UI hints or validators) to that object, as described in How to Edit a Data Control. To create data control structure files for each data control object, you need to repeat that procedure for each data control object.

See Customizing MAF Application Artifacts with MDS .

15.9 Working with Attributes

When a data control is created and a data control structure file generated for objects, you can configure the functionality of the persistent attributes of the data objects. Use the Attributes page of the overview editor of the data control structure file to configure properties.

When you create a data control for your business services, you can create a data control structure file for an individual data object in which you can declaratively augment the functionality of the persistent attributes of the data object. For example, you can create validation rules and set UI hints to control the default presentation of attributes in UI components.

You set these properties on the Attributes page of the overview editor of the data control structure file. For information on creating a data control structure file, see How to Edit a Data Control.

15.9.1 How to Designate an Attribute as Primary Key

In the data control structure file of a data object, you can specify an attribute as a primary key for the data object. Use the procedure to set an attribute as a primary key for a data object that has not inherited a set primary key.

In the overview editor for the data control structure file of a data object, you can designate an attribute as a primary key for that data object if you have not already done so in the underlying class of the data object.

Before you begin:

It may be helpful to have an understanding of how you set attribute properties. See Working with Attributes.

You will need to complete this task:

To set an attribute as a primary key:

  1. In the Applications window, double-click the desired data control structure file.
  2. In the overview editor, click the Attributes navigation tab.
  3. On the Attributes page, select the attribute you want to designate as the primary key and then click the Details tab.
  4. On the Details page, set the Key Attribute property.

Note:

If the attribute has already been designated as the primary key in the class, the data control inherits that setting and the Key Attribute checkbox will be selected. However, in this case, you can not deselect the Key Attribute option.

15.9.2 How to Define a Static Default Value for an Attribute

In the overview editor of the data control structure file for a data object, you can specify a static default value for an attribute. Use the procedure to set the value type to Literal, and define a static default value for an attribute.

The Value field in the Details section allows you to specify a static default value for the attribute when the value type is set to Literal. For example, you might set the default value of a Status attribute of a ServiceRequest entity bean to Open, or set the default value of a UserRole attribute of a User bean to user.

Before you begin:

It may be helpful to have an understanding of how you set attribute properties. See Working with Attributes.

To define a static default value for an attribute:

  1. In the Applications window, double-click the desired data control structure file.
  2. In the overview editor, click the Attributes navigation tab.
  3. On the Attributes page, select the attribute you want to edit, and then click the Details tab.
  4. On the Details page, select the Literal option.
  5. In the text field below the Literal option, enter the default value for the attribute.

15.9.3 How to Set UI Hints on Attributes

Setting UI hints on attributes displays and labels them consistently, and makes the attributes more usable and localizable for the UI components that use them. Use the procedure to set a UI hint using the Attributes option in the Applications window.

You can set UI hints on attributes so that those attributes are displayed and labeled in a consistent and localizable way by any UI components that use those attributes. UI hints determine things such as the type of UI component to use to display the attribute, the label, the tooltip, and whether the field should be automatically submitted. You can also determine whether a given attribute is displayed or hidden. To create UI hints for attributes, use the overview editor for the data control structure file of the data object, which is accessible from the Applications window.

Before you begin:

It may be helpful to have an understanding of how you set attribute properties. See Working with Attributes.

You will need to complete this task:

To set a UI hint:

  1. In the Applications window, double-click the desired data control structure file.
  2. In the overview editor, click the Attributes navigation tab.
  3. On the Attributes page, select the attribute you want to edit, and then click the UI Hints tab.
  4. In the UI Hints section, set the desired UI hints.

15.9.4 What Happens When You Set UI Hints on Attributes

UI hints on an attribute are stored as properties to which tags are added. Values for properties are stored in a resource bundle file, which is generated if it is not found.

When you set UI hints on an attribute, those hints are stored as properties. Tags for the properties are added to the data control structure file of the data object and the values for the properties are stored in a resource bundle file. If the resource bundle file does not already exist, it is generated in the package of the data control and named according to the project name when you first set a UI hint.

The following example shows the code for the price attribute in the Item.xml data control structure file, including tags for the Label and Format Type hints which have been set for the attribute.

<PDefAttribute
  Name="price">
  <Properties>
    <SchemaBasedProperties>
      <LABEL
        ResId="${adfBundle['model.ModelBundle']['model.Item.price_LABEL']}"/>
      <FMT_FORMATTER ResId="${adfBundle['model.ModelBundle']
                                       ['model.Item.price_FMT_FORMATTER']}"/>
    </SchemaBasedProperties>
  </Properties>
</PDefAttribute>

The following example shows the corresponding entries for the Label and Format Type hints in the ModelBundle.properties resource bundle file, which contains the values for all of the localizable properties of the project.

model.Item.price_LABEL=Price
. . .
model.Item.price_FMT_FORMATTER=oracle.jbo.format.DefaultCurrencyFormatter

15.9.5 How to Access UI Hints Using EL Expressions

Use EL expressions to display hint values as data on a page so that you can access UI hints. Drop databound components onto a page to create binding instances that provide access to UI hints.

You can access UI hints using EL expressions to display the hint values as data in a page. You access UI hints through the binding instances that you create after dropping databound components onto your pages.

The following example was produced using the DeviceFeatures data control. It shows the EL expression that is produced by dragging and dropping Contact as a MAF form and only keeping the displayName and nickname fields. The labels in bold are examples of the retrieval of UI hints using EL.

<amx:panelFormLayout id="pfl2">
      <amx:inputText value="#{row.bindings.displayName.inputValue}"
                     label="#{bindings.Contact.hints.displayName.label}" id="it9"/>
      <amx:inputText value="#{row.bindings.nickname.inputValue}"
                     label="#{bindings.Contact.hints.nickname.label}"
                     id="it10"/>
</amx:panelFormLayout>af:panelHeader id="ph1"

15.10 Creating and Using Bean Data Controls

A bean data control serves as a metadata wrapper for a bean class, and exposes the code elements of the bean as data control objects that are used to bind code elements to UI components. Create a Java bean data control by using the Create Data Control option in the Applications window.

A bean data control serves as a metadata wrapper for a bean class and exposes the code elements of the bean as data control objects, which can then be used to bind those code elements to UI components. Java bean data controls obtain their data structure from POJOs (plain old Java objects). To create a Java bean data control, right-click a Java class file (in the Applications window), and select Create Data Control. You create Java bean data controls from within the Applications window of JDeveloper.

Before you begin:

It may be helpful to have a general understanding of data controls. See How to Create Data Controls.

Note:

If the Java bean is using a background thread to update data in the UI, you need to manually call oracle.adfmf.framework.api.AdfmfJavaUtilities.flushDataChangeEvent. For information about the flushDataChangeEvent method, see About Data Change Events.

For a sample of to build CRUD operations using the local SQLite database and Java bean data controls, see the MAF sample application called CRUDDemo located in the PublicSamples.zip file within the jdev_install/jdeveloper/jdev/extensions/oracle.maf/Samples directory on your development computer.

15.10.1 What You May Need to Know About Serialization of Bean Class Variables

Define a chain of nested objects as transient to prevent serialization and the creation of cyclic objects from object nesting. You can serialize and deserialize Java objects into JSON objects by using the JSONBeanSerializationHelper class.

MAF does not serialize to JavaScript Object Notation (JSON) data bean class variables that are declared as transient. To avoid serialization of a chain of nested objects, you should define them as transient. This strategy also helps to prevent the creation of cyclic objects due to object nesting.

Consider the following scenario: you have an Employee object that has a child Employee object representing the employee's manager. If you do not declare the child object transient, a chain of serialized nested objects will be created when you attempt to calculate the child Employee object at runtime.

To serialize and deserialize Java objects into JSON objects, use the JSONBeanSerializationHelper class. The JSONBeanSerializationHelper class enables you to implement your own custom JSON serialization and deserialization, and it provides a hook to alter the JSON object after the JSON serialization (and deserialization) process. See the oracle.adfmf.framework.api.JSONBeanSerializationHelper class in Java API Reference for Oracle Mobile Application Framework.

MAF does not support serializing objects of the GregorianCalendar class. The JSONBeanSerializationHelper class cannot serialize objects of the GregorianCalendar class because the GregorianCalendar class has cyclical references in it. Instead, use java.util.Date or java.sql.Date for date manipulation. The following example shows how to convert a GregorianCalendar object using java.util.Date:

Calendar calDate = new GregorianCalendar();
calDate.set(1985, 12, 1); // "January 1, 1986"
Date date = calDate.getTime();

15.11 Sharing Instances of Data Controls Across Application Features

You can share an instance of a data control across application features so that a data control accessed by multiple application features is loaded into memory once for all application features that access the shared instance.

Doing this improves the performance of your MAF application. Shared instance data controls are loaded once into memory rather than for each individual application feature that accesses the data control.

MAF propagates data changes that end users make in one application feature that uses a shared instance data control to other application features that use the shared instance data control.

By default, data controls are not shared across application features in your MAF application. You must add each data control that you want to share to the Shared Instance DC page of the overview editor for the maf-application.xml file. Figure 15-10 shows the Shared Instance DC page where the HRService data control has been designated as a shared instance data control.

By default, MAF only permits access to the shared instance data control from application features that have security enabled. You can disable this restriction and allow application features used by unauthenticated users to access a shared instance data control by setting the Enable Security property to false.

Figure 15-10 Shared Instance Data Control Page in maf-application.xml’s Overview Editor

The surrounding text describes this image.

Data controls that your MAF application accesses from a task flow with a data-control-context value of isolated will not be shared, even if you have designated the data control as a shared instance data control.

15.11.1 How to Share Instances of Data Controls Across Application Features

You share instances of data controls by adding each data control that you want to share to the Shared Instance DC page of the overview editor for the maf-application.xml file.

To share instances of data controls across application features:
  1. In the Applications window, expand the Application Resources panel.
  2. In the Application Resources panel, expand Descriptors and then ADF META-INF.
  3. Double-click the maf-application.xml file and in the overview editor that appears, click the Shared Instance DC navigation tab.
  4. In the Shared Instance DC page, click the Add icon.
  5. In the Insert sharedInstanceDataControl dialog, select the ID of the data control from the dropdown list.
  6. Click OK.

15.11.2 What Happens When You Share Instances of Data Controls Across Application Features

At runtime, one instance of a data provider is created for a data control that you designate as a shared instance data control. This means, for example, that a Java class is instantiated one time only when the associated shared instance data control is first referenced by a feature.

All application features that reference the shared instance data control share this instance of the Java class. Updates to the shared instance data control are delivered to all application features if the security setting permits it. If, for example, the shared instance data control is only available to secured application features (the default setting), updates in the form of data change or data provider events will be sent to secured application features and MAF throws an error when an unsecured application feature attempts to access the shared instance data control.

One exception to the just-described behavior is where your MAF application accesses the shared instance data control from a task flow with a data-control-context value of isolated. In this scenario, more than one instance of a data provider is created.

JDeveloper adds entries to the maf-application.xml file that identifies each data control that you designated as shared. The following example shows entries for two shared instance data controls. The first shared instance data control (HRService) can only be accessed by application features that have security enabled (the default setting) while the second (CustomerService) can be accessed by unsecured application features.

Example 15-1 Shared Instance Data Controls Defined in maf-application.xml File

<adfmf:application ...>
  ...
  <adfmf:sharedInstanceDataControls>
    <adfmf:sharedInstanceDataControl dataControlId="HRService" id="sidc1"/>
    <adfmf:sharedInstanceDataControl dataControlId="CustomerService" id="sidc2" securityEnabled="false"/>
  </adfmf:sharedInstanceDataControls>
</adfmf:application>

15.12 Using the DeviceFeatures Data Control

When you create a new MAF application, MAF presents theDeviceFeatures data control in the Data Controls panel. Use the available operations in the data control on a MAF AMX page to manage user contacts on a device, use email and SMS text messages, ascertain the location of a device, use a device camera, or retrieve the images stored on a device.

MAF exposes device-specific features that you can use in your application through the DeviceFeatures data control, a component that appears in the Data Controls panel when you create a new MAF application. The Cordova Java API is abstracted through this data control, enabling the application features implemented as MAF AMX to access various services embedded on a device. By dragging and dropping the operations provided by the DeviceFeatures data control into a MAF AMX page, you can add functions to manage the user contacts stored on a device, create and send both email and SMS text messages, ascertain the location of a device, use a device camera, and retrieve the images stored in a file system of a device. The following sections describe each of these operations in detail, including how to use them declaratively and how to implement them with Java code and JavaScript.

Figure 15-11 MAF DeviceFeatures Data Control in the Overview Editor

This image is described in the surrounding text

The DeviceFeatures data control appears in the Data Controls panel automatically when you create an application using the MAF application template. Figure 15-11 shows the DeviceFeatures data control in the overview editor. The following methods are available:

  • addLocalNotification

  • cancelLocalNotification

  • createContact

  • displayFile

  • findContacts

  • getPicture

  • removeContact

  • sendEmail

  • sendSMS

  • startLocationMonitor

  • updateContact

After you create a page, you can drag DeviceFeatures data control methods (or other objects nested within those methods) from the Data Controls panel to a MAF AMX view to create command buttons and other components that are bound to the associated functionality. You can accept the default bindings or modify the bindings using EL. You can also use JavaScript or Java to implement or configure functionality. See How to Add Data Controls to a MAF AMX Page.

The DeviceManager is the object that enables you to access device functionality. You can get a handle on this object by calling DeviceManagerFactory.getDeviceManager. The following sections describe how you can invoke methods like getPicture or createContact using the DeviceManager object.

[[ dev ER 19938192 addressed below ]]

With the exception of network access, access to all of the Apache Cordova-enabled device capabilities is not enabled by default for MAF applications. The operations that the DeviceFeatures data control expose require that the associated plugin be enabled in the MAF application for the operation to function correctly at runtime. If, for example, you want to use the sendSMS operation from the DeviceFeatures data control, you must enable the SMS plugin in the MAF application. You can enable plugins manually or you can select the appropriate option in the dialog that JDeveloper displays when you drag and drop an operation that does not have the associated plugin enabled in the MAF application. For example, JDeveloper displays the dialog in Figure 15-12 when you drag and drop the sendSMS operation to a MAF AMX page in a MAF application that has yet to enable the SMS plugin.

Figure 15-12 Enabling Plugin for a DeviceFeatures Data Control Operation

This image is described in the surrounding text

If the plugin that an operation requires is not enabled, a warning message appears in the source file of the MAF AMX page. Assume, for example, that the MAF application does not enable the SMS plugin. The warning message shown in Figure 15-13 appears in MAF AMX pages where the application attempts to invoke the sendSMS operation. You resolve this issue by manually enabling the plugin, as described in Using Plugins in MAF Applications .

Figure 15-13 DeviceFeatures Data Control Operation Requires Plugin

The surrounding text describes this image.

15.12.1 How to Use the getPicture Method to Enable Taking Pictures

The DeviceFeatures data control provides the getPicture method to take a photo with the device camera or retrieve an existing image. Use the procedure to customize a getPicture operation using parameters such as sourceType, destinationType, quality, and allowEdit.

The DeviceFeatures data control includes the getPicture method, which enables MAF applications to leverage a device camera and photo library so end users can take a photo or retrieve an existing image. At the end of this section there are examples that show:

  • JavaScript code that enables an end user to take a picture with a device camera. Java code that allows the user to take a picture with a device camera.

  • Java code that allows the user to retrieve a previously-saved image.

For information about the getPicture method, see the DeviceDataControl class in the MAF Javadoc and refer to the Cordova documentation (http://cordova.apache.org/).

The following parameters control where the image is taken from and how it is returned:

Note:

If you do not specify a targetWidth, targetHeight, and quality for the picture being taken, the default values used are maximum values, and this can cause memory failures.

  • quality: Set the quality of the saved image. Range is 0 to 100, inclusive. A higher number indicates higher quality, but also increases the file size. Only applicable to JPEG images (specified by encodingType).

  • destinationType: Choose the format of the return value:

    • DeviceManager.CAMERA_DESTINATIONTYPE_DATA_URL (0)—Returns the image as a Base64-encoded string. This value is also specified as an enum using DeviceManager.CAMERA_DESTINATION_DATA_URL when used programmatically. You need to prefix the value returned with "data:image/gif;base64," in order to see the image in an image component.

    • DeviceManager.CAMERA_DESTINATIONTYPE_FILE_URI (1)—Returns the image file path. This value is also specified as an enum using DeviceManager.CAMERA_DESTINATION_FILE_URI when used programmatically.

    Note:

    If a file URI is returned by the getPicture method, it should be stripped of any query parameters before being used to determine the size of the file. For example:

    String fileURI = ...getPicture(...);

    fileURI = fileURI.substring(0, result.lastIndexOf("?"));

  • sourceType: Set the source of the picture:

    • DeviceManager.CAMERA_SOURCETYPE_PHOTOLIBRARY (0)—Enables the user to choose from a previously saved image. This value is also specified as an enum using DeviceManager.CAMERA_SOURCETYPE_PHOTOLIBRARY when used programmatically.

    • DeviceManager.CAMERA_SOURCETYPE_CAMERA (1)—Enables the user to take a picture with the device camera. This value is also specified as an enum using DeviceManager.CAMERA_SOURCETYPE_CAMERA when used programmatically.

    • DeviceManager.CAMERA_SOURCETYPE_SAVEDPHOTOALBUM (2)—Allows the user to choose from an existing photo album. This value is also specified as an enum using DeviceManager.CAMERA_SOURCETYPE_SAVEDPHOTOALBUM when used programmatically.

  • allowEdit: Choose whether to allow simple editing of the image before selection (boolean).

  • encodingType: Choose the encoding of the returned image file:

    • DeviceManager.CAMERA_ENCODINGTYPE_JPEG (0)—Encodes the returned image as a JPEG file. This value is also specified as an enum using DeviceManager.CAMERA_ENCODINGTYPE_JPEG when used programmatically.

    • DeviceManager.CAMERA_ENCODINGTYPE_PNG (1)—Encodes the returned image as a PNG file. This value is also specified as an enum using DeviceManager.CAMERA_ENCODINGTYPE_PNG when used programmatically.

  • targetWidth: Set the width in pixels to scale the image. Aspect ratio is maintained. A negative or zero value indicates that the original dimensions of the image will be used.

  • targetHeight: Set the height in pixels to scale the image. Aspect ratio is maintained. A negative or zero value indicates that the original dimensions of the image will be used.

To customize a getPicture operation using the DeviceFeatures data control:

  1. Drag the getPicture operation from the DeviceFeatures data control in the Data Controls panel and drop it on the page as a Button.

    If you want to provide more control to the user, drop the getPicture operation as a Parameter Form. This allows the end user to specify settings before taking a picture or choosing an existing image.

  2. In the Edit Action dialog, set the values for all parameters described above. Be sure to specify destinationType = 1 so that the image is returned as a filename.

  3. Drag the return value of getPicture and drop it on the page as an Output Text.

  4. From the Common Components panel, drag an Image from the Component Palette and drop it on the page.

  5. Set the source attribute of the Image to the return value of the getPicture operation. The bindings expression should be: #{bindings.Return.inputValue}.

Figure 15-14 shows the bindings for displaying an image from the user's photo library:

Figure 15-14 Bindings for Displaying an Image from the Photo Library at Design Time

This image is described in the surrounding text

When this application is run, the image chooser will automatically be displayed and the end user can select an image to display. The image chooser is displayed automatically because the Image control is bound to the return value of the getPicture operation, which in turn causes the getPicture operation to be invoked.

Note:

The timeout value for the getPicture method is set to 5 minutes. If the device operation takes longer than the timeout allowed, a timeout error is displayed.

Keep in mind the following platform-specific issues:

  • iOS

    • Set quality below 50 to avoid memory error on some devices.

    • When destinationType FILE_URI is used, photos are saved in the temporary directory of the application.

    • The contents of the temporary directory of the application are deleted when the application ends. You may also delete the contents of this directory using the navigator.fileMgr APIs if storage space is a concern.

    • targetWidth and targetHeight must both be specified to be used. If one or both parameters have a negative or zero value, the original dimensions of the image will be used.

  • Android

    • Ignores the allowEdit parameter.

    • Camera.PictureSourceType.PHOTOLIBRARY and Camera.PictureSourceType.SAVEDPHOTOALBUM both display the same photo album.

    • Camera.EncodingType is not supported. The parameter is ignored, and will always produce JPEG images.

    • targetWidth and targetHeight can be specified independently. If one parameter has a positive value and the other uses a negative or zero value to represent the original size, the positive value will be used for that dimension, and the other dimension will be scaled to maintain the original aspect ratio.

    • When destinationType DATA_URL is used, large images can exhaust available memory, producing an out-of-memory error, and will typically do so if the default image size is used. Set the targetWidth and targetHeight to constrain the image size.

The following example shows JavaScript code that allows the user to take a picture with a device camera. The result will be the full path to the saved image.

// The camera, like many other device-specific features, is accessed 
// from the global 'navigator' object in JavaScript.
// Note that in the Cordova JavaScript APIs, the parameters are passed
// in as a dictionary, so it is only necessary to provide key-value pairs
// for the parameters you want to specify.

navigator.camera.getPicture(onSuccess, onFail, { quality: 50 });

function onSuccess(imageURI) { 
    var image = document.getElementById('myImage');
    image.src = imageURI;
}

function onFail(message) {
    alert('Failed because: ' + message);
}

The following example shows Java code that allows the user to take a picture with a device camera. The result will be the full path to the saved image.

import oracle.adf.model.datacontrols.device; 

// Access device features in Java code by acquiring an instance of the  
// DeviceManager from the DeviceManagerFactory.
// Take a picture with the device's camera.  
// The result will be the full path to the saved PNG image.
String imageFilename = DeviceManagerFactory.getDeviceManager().getPicture(100,
                               DeviceManager.CAMERA_DESTINATIONTYPE_FILE_URI,
                               DeviceManager.CAMERA_SOURCETYPE_CAMERA, false,
                               DeviceManager.CAMERA_ENCODINGTYPE_PNG, 0, 0);

The following example shows Java code that allows the user to retrieve a previously-saved image. The result will be a base64-encoded JPEG.

import oracle.adf.model.datacontrols.device; 

// Retrieve a previously-saved image. The result will be a base64-encoded JPEG.
String imageData = DeviceManagerFactory.getDeviceManager().getPicture(100, 
  DeviceManager.CAMERA_DESTINATIONTYPE_FILE_URL,
  DeviceManager.CAMERA_SOURCETYPE__PHOTOLIBRARY, false,
  DeviceManager.CAMERA_ENCODINGTYPE_JPEG, 0, 0);

15.12.2 How to Use the SendSMS Method to Enable Text Messaging

Use the sendSMS method in the DeviceFeatures data control to send and receive SMS messages with the Short Message Service (SMS) text messaging interface of a device. The sendSMS operation is customizable.

The DeviceFeatures data control includes the sendSMS method, which enables MAF applications to leverage the Short Message Service (SMS) text messaging interface of a device so users can send and receive SMS messages. MAF enables you to display the SMS interface of a device and optionally pre-populate the following fields:

  • to: List recipients (comma-separated).

  • body: Add message body.

After the SMS text messaging interface is displayed, the user can select to either send the SMS or discard it. It is not possible to automatically send the SMS due to device and carrier restrictions; only the user can actually send the SMS.

The timeout value for the sendSMS method is set to 5 minutes. If the operation of the device takes longer than the timeout allowed, a timeout error appears.

In Android, if an user switches away from their application while editing an SMS message and then subsequently returns to it, they will no longer be in the SMS editing screen. Instead, that message will have been saved as a draft that can then manually be selected for continued editing.

MAF applications on the Universal Windows Platform do not support the use of SMS text messaging at present.

To customize a sendSMS operation using the DeviceFeatures data control:

To display an interactive form on the page for sending SMS, drag the sendSMS operation from the DeviceFeatures data control in the Data Controls panel and drop it on the page designer as a Parameter Form. You can then customize the form in the Edit Form Fields dialog. At runtime, an editable form will be displayed on the page, which enables the application user to enter values for the various fields described above. Below this form will be a button to display the SMS interface of the device, which will display an SMS that is ready to send with all of the specified fields pre-populated.

Figure 15-15 shows the bindings for sending an SMS using an editable form on the page.

Figure 15-15 Bindings for Sending an SMS Using an Editable Form at Design Time

This image is described in the surrounding text

The following examples show code examples that allow the user to send an SMS message with the text messaging interface of a device.

For information about the sendSMS method, see the DeviceDataControl class in the MAF Javadoc and refer to the Cordova documentation (http://cordova.apache.org/).

The following example shows JavaScript code that allows the user to send an SMS message:

adf.mf.api.sendSMS({to: "5551234567", body: "This is a test message"}); 

The following example shows Java code that allows the user to send an SMS message:

import oracle.adf.model.datacontrols.device.DeviceManagerFactory;

// Access device features in Java code by acquiring an instance of the  
// DeviceManager from the DeviceManagerFactory.
// Send an SMS to the phone number "5551234567"
DeviceManagerFactory.getDeviceManager().sendSMS("5551234567", "This is a test message");

15.12.3 How to Use the sendEmail Method to Enable Email

The DeviceFeatures data control provides the sendEmail method to send and receive email messages from a configured email account with the email messaging interface of a device. The sendEmail operation is customizable.

The DeviceFeatures data control includes the sendEmail method, which enables MAF applications to leverage the email messaging interface of a device so users can send and receive email messages. MAF enables you to display the email interface of a device and optionally pre-populate the following fields:

  • to: List recipients (comma-separated).

  • cc: List CC recipients (comma-separated).

  • subject: Add message subject.

  • body: Add message body.

  • bcc: List BCC recipients (comma-separated).

  • attachments: List file names to attach to the email (comma-separated).

  • mimeTypes: List MIME types to use for the attachments (comma-separated). Specify null to let MAF automatically determine the MIME types. It is also possible to specify only the MIME types for selected attachments as shown in the examples at the end of this section.

After the email interface of a device is displayed, the user can select to either send the email or discard it. It is not possible to automatically send the email due to device and carrier restrictions; only the user can actually send the email. The device must also have at least one email account configured to send email or an error will be displayed indicating that no email accounts could be found.

Note:

The timeout value for the sendEmail method is set to 5 minutes. If the operation of the device takes longer than the timeout allowed, a timeout error is displayed.

Note:

In Android, if an user switches away from their application while editing an email and then subsequently returns to it, they will no longer be in the email editing screen. Instead, the message will be saved as a draft that can then be manually selected for continued editing.

To customize a sendEmail operation using the DeviceFeatures data control:

In JDeveloper, drag the sendEmail operation from the DeviceFeatures data control in the Data Controls panel to the page designer and drop it as a Parameter Form. You can then customize the form in the Edit Form Fields dialog. At runtime, an editable form will be displayed on the page, which enables the application user to enter values for the various fields described above. Below this form will be a button to display the email interface of a device, which will display an email ready to send with all of the specified fields pre-populated.

Figure 15-16 shows the bindings for sending an email using an editable form on the page.

Figure 15-16 Bindings for Sending an Email Using an Editable Form at Design Time

This image is described in the surrounding text

Following are code examples that allow the user to send an email message with the email interface of the device

For information about the sendEmail method, see the DeviceDataControl class in the MAF Javadoc and refer to the Cordova documentation (http://cordova.apache.org/).

The following example shows JavaScript code that allows the user to send an email message:

// Populate an email to 'ann.li@example.com',  
// copy 'joe.jones@example.com', with the 
// subject 'Test message', and the body 'This is a test message'
// No BCC recipients or attachments
adf.mf.api.sendEmail({to: "ann.li@example.com",
                           cc: "joe.jones@example.com",
                           subject: "Test message",
                           body: "This is a test message"});

// Populate the same email as before, but this time, also BCC 
// 'john.smith@example.com' & 'jane.smith@example.com' and attach two files.
// By not specifying a value for the mimeTypes parameter, you are telling 
// ADFMobile to automatically determine the MIME type for each of the attachments.
adf.mf.api.sendEmail({to: "ann.li@example.com",
                           cc: "joe.jones@example.com",
                           subject: "Test message",
                           body: "This is a test message"});
                           bcc: "john.smith@example.com,jane.smith@example.com",
                           attachments: "path/to/file1.txt,path/to/file2.png"});

// For iOS only: Same as previous email, but this time, explicitly specify
// all the MIME types.
adf.mf.api.sendEmail({to: "ann.li@example.com",
                           cc: "joe.jones@example.com",
                           subject: "Test message",
                           body: "This is a test message"});
                           bcc: "john.smith@example.com,jane.smith@example.com",
                           attachments: "path/to/file1.txt,path/to/file2.png"});
                           mimeTypes: "text/plain,image/png"});

// For iOS only: Same as previous email, but this time, only specify  
// the MIME type for the second attachment and let the system determine
// the MIME type for the first one.
adf.mf.api.sendEmail({to: "ann.li@example.com",
                           cc: "joe.jones@example.com",
                           subject: "Test message",
                           body: "This is a test message"});
                           bcc: "john.smith@example.com,jane.smith@example.com",
                           attachments: "path/to/file1.txt,path/to/file2.png"});
                           mimeTypes: ",image/png"});

// For Android only: Same as previous e-mail, but this time, explicitly specify 
// the MIME type.
adf.mf.api.sendEmail({to: "ann.li@example.com",
                           cc: "joe.jones@example.com",
                           subject: "Test message",
                           body: "This is a test message"});
                           bcc: "john.smith@example.com,jane.smith@example.com",
                           attachments: "path/to/file1.txt,path/to/file2.png"});
                           mimeTypes: "image/*"}); 
// You can also use "plain/text" as the MIME type as it just determines the type
// of applications to be filtered in the application chooser dialog.

The following example shows Java code that allows the user to send an email message:

import oracle.adf.model.datacontrols.device.DeviceManagerFactory;

// Access device features in Java code by acquiring an instance of the  
// DeviceManager from the DeviceManagerFactory.
// Populate an email to 'ann.li@example.com', copy 'joe.jones@example.com', with the 
// subject 'Test message', and the body 'This is a test message'.
// No BCC recipients or attachments.
DeviceManagerFactory.getDeviceManager().sendEmail(
                                        "ann.li@example.com",
                                        "joe.jones@example.com",
                                        "Test message",
                                        "This is a test message",
                                        null,
                                        null,
                                        null);

// Populate the same email as before, but this time, also BCC 
// 'john.smith@example.com' & 'jane.smith@example.com' and attach two files.
// By specifying null for the mimeTypes parameter, you are telling 
// ADFMobile to automatically determine the MIME type for each of the attachments.
DeviceManagerFactory.getDeviceManager().sendEmail(
                                        "ann.li@example.com",
                                        "joe.jones@example.com",
                                        "Test message",
                                        "This is a test message",
                                        "john.smith@example.com,jane.smith@example.com",
                                        "path/to/file1.txt,path/to/file2.png",
                                        null);

// Same as previous email, but this time, explicitly specify all the MIME types.
DeviceManagerFactory.getDeviceManager().sendEmail(
                                        "ann.li@example.com",
                                        "joe.jones@example.com",
                                        "Test message",
                                        "This is a test message",
                                        "john.smith@example.com,jane.smith@example.com",
                                        "path/to/file1.txt,path/to/file2.png",
                                        "text/plain,image/png");


// Same as previous email, but this time, only specify the MIME type for the 
// second attachment and let the system determine the MIME type for the first one.
DeviceManagerFactory.getDeviceManager().sendEmail(
                                        "ann.li@example.com",
                                        "joe.jones@example.com",
                                        "Test message",
                                        "This is a test message",
                                        "john.smith@example.com,jane.smith@example.com",
                                        "path/to/file1.txt,path/to/file2.png",
                                        ",image/png");

15.12.4 How to Use the createContact Method to Enable Creating Contacts

The DeviceFeatures data control provides the createContact method to manage contacts that you can add and save in an address book. Use the procedure to customize a createContact operation.

The DeviceFeatures data control includes the createContact method, which enables MAF applications to leverage the interface of a device and file system for managing contacts so users can create new contacts to save in the address book of the device. MAF enables you to display the interface of the device and optionally pre-populate the Contact fields. The createContact method takes in a Contact object as a parameter and returns the created Contact object, as shown in the examples at the end of this section.

For more information about the createContact method and the Contact object, see the DeviceDataControl class in the MAF Javadoc and refer to the Cordova documentation (http://cordova.apache.org/). Also see How to Use the findContacts Method to Enable Finding Contacts for a description of Contact properties.

Note:

The timeout value for the createContact method is set to 1 minute. If the operation of the device takes longer than the timeout allowed, a timeout error is displayed.

Note:

If a null Contact object is passed in to the method, an exception is thrown.

To customize a createContact operation using the DeviceFeatures data control:

  1. In JDeveloper, drag the createContact operation from the DeviceFeatures data control in the Data Controls panel and drop it on the page designer as a Link or Button.

    Link or Button: You will be prompted with the Edit Action Binding dialog to enter the Contact object parameter to the createContact operation. This parameter must be an EL expression that refers to the property of a managed bean that is used to return the Contact from a Java bean class. Assuming a managed bean already exists with a getter for a Contact object, you can use the EL Expression Builder to set the value of the parameter. At runtime, a button or link will be displayed on the page, which will use the entered values to perform a createContact operation when pressed. The following code example shows an example of managed bean code for creating a Contact object.

  2. You can also drag a Contact return object from under the createContact operation in the Data Controls panel and drop it on to the page as a Form. You can then customize the form in the Edit Form Fields dialog. When the createContact operation is performed, the results will be displayed in this form.
private Contact contactToBeCreated;
 
public void setContactToBeCreated(Contact contactToBeCreated) {
   this.contactToBeCreated = contactToBeCreated;
}
 
public Contact getContactToBeCreated() {
   String givenName = "Mary";
   String familyName = "Jones";
   String note = "Just a Note";
   String phoneNumberType = "mobile";
   String phoneNumberValue = "650-555-0111";
   String phoneNumberNewValue = "650-555-0199";
   String emailType = "home";
   String emailTypeNew = "work";
   String emailValue = "Mary.Jones@example.com";
   String addressType = "home";
   String addressStreet = "500 Barnacle Pkwy";
   String addressLocality = "Redwood Shores";
   String addressCountry = "USA";
   String addressPostalCode = "94065";
   ContactField[] phoneNumbers = null;
   ContactField[] emails = null;
   ContactAddresses[] addresses = null;
 
   /*
    * Create contact
    */
   this.contactToBeCreated = new Contact();
 
   ContactName name = new ContactName();
   name.setFamilyName(familyName);
   name.setGivenName(givenName);
   this.contactToBeCreated.setName(name);
 
   ContactField phoneNumber = new ContactField();
   phoneNumber.setType(phoneNumberType);
   phoneNumber.setValue(phoneNumberValue);
 
   phoneNumbers = new ContactField[] { phoneNumber };
 
   ContactField email = new ContactField();
   email.setType(emailType);
   email.setValue(emailValue);
 
   emails = new ContactField[] { email };
 
   ContactAddresses address = new ContactAddresses();
   address.setType(addressType);
   address.setStreetAddress(addressStreet);
   address.setLocality(addressLocality);
   address.setCountry(addressCountry);
 
   addresses = new ContactAddresses[] { address };
 
   this.contactToBeCreated.setNote(note);
   this.contactToBeCreated.setPhoneNumbers(phoneNumbers);
   this.contactToBeCreated.setEmails(emails);
   this.contactToBeCreated.setAddresses(addresses);
 
   return this.contactToBeCreated;
}

The following examples show code examples that allow the user to create contacts on devices.

The following example shows JavaScript code for createContact.

// Contacts, like many other device-specific features, 
// are accessed from the global 'navigator' object in JavaScript
var contact = navigator.contacts.create();
 
var name = new ContactName();
name.givenName = "Mary";
name.familyName = "Jones";
 
contact.name = name;
 
// Store contact phone numbers in ContactField[]
var phoneNumbers = [1];
phoneNumbers[0] = new ContactField('home', '650-555-0123', true);
 
contact.phoneNumbers = phoneNumbers;
 
// Store contact email addresses in ContactField[]
var emails = [1];
emails[0] = new ContactField('work', 'Mary.Jones@example.com');
 
contact.emails = emails;
 
// Save
contact.save(onSuccess, onFailure);
 
function onSuccess()
{
  alert("Create Contact successful.");
}
 
function onFailure(Error)
{
  alert("Create Contact failed: " + Error.code);
}
     

The following example shows Java code for createContact.

import oracle.adf.model.datacontrols.device.DeviceManagerFactory; 

import oracle.adf.model.datacontrols.device.ContactAddresses;
import oracle.adf.model.datacontrols.device.ContactField;
import oracle.adf.model.datacontrols.device.ContactName;

String givenName = "Mary";
String familyName = "Jones";
String note = "Just a Note";
String phoneNumberType = "mobile";
String phoneNumberValue = "650-555-0111";
String phoneNumberNewValue = "650-555-0199";
String emailType = "home";
String emailTypeNew = "work";
String emailValue = "Mary.Jones@example.com";
String addressType = "home";
String addressStreet = "500 Barnacle Pkwy";
String addressLocality = "Redwood Shores";
String addressCountry = "USA";
String addressPostalCode = "91234";
ContactField[] phoneNumbers = null;
ContactField[] emails = null;
ContactAddresses[] addresses = null;
ContactField[] emails = null;
    
/*
* Create contact
*/
Contact aContact = new Contact();
     
ContactName name = new ContactName();
name.setFamilyName(familyName);
name.setGivenName(givenName);
aContact.setName(name);
     
ContactField phoneNumber = new ContactField();
phoneNumber.setType(phoneNumberType);
phoneNumber.setValue(phoneNumberValue);
     
phoneNumbers = new ContactField[] { phoneNumber };
     
ContactField email = new ContactField();
email.setType(emailType);
email.setValue(emailValue);
     
emails = new ContactField[] { email };
     
ContactAddresses address = new ContactAddresses();
address.setType(addressType);
address.setStreetAddress(addressStreet);
address.setLocality(addressLocality);
address.setCountry(addressCountry);
     
addresses = new ContactAddresses[] { address };
     
aContact.setNote(note);
aContact.setPhoneNumbers(phoneNumbers);
aContact.setEmails(emails);
aContact.setAddresses(addresses);
     
// Access device features in Java code by acquiring an instance of the  
// DeviceManager from the DeviceManagerFactory.
// Invoking the createContact method, using the DeviceDataControl object.
Contact createdContact = DeviceManagerFactory.getDeviceManager()
    .findContacts.createContact(aContact);

15.12.5 How to Use the findContacts Method to Enable Finding Contacts

The DeviceFeatures data control provides the findContacts method to help you locate contacts in an address book using properties such as id, name, birthday, photos, and address. Use the procedures to customize a findContacts operation.

The DeviceFeatures data control includes the findContacts method, which enables MAF applications to leverage the interface of a device and file system for managing contacts so users can find one or more contacts from the address book of the device . MAF enables you to display the interface of the device and optionally pre-populate the findContacts fields. The findContacts method takes in a filter string and a list of field names to look through (and return as part of the found contacts). The filter string can be anything to look for in the contacts. For more information about the findContacts method, see the DeviceDataControl class in the MAF Javadoc and refer to the Cordova documentation (http://cordova.apache.org/).

The findContacts operation takes the following arguments:

  • contactFields: Required parameter. Use this parameter to specify which fields should be included in the Contact objects resulting from a findContacts operation. Separate fields with a comma (spacing does not matter).

  • filter: The search string used to filter contacts. (String) (Default: "")

  • multiple: Determines if the findContacts operation should return multiple contacts. (Boolean) (Default: false)

Note:

Passing in a field name that is not in the following list may result in a null return value for the findContacts operation. Also, only the fields specified in the Contact fields argument will be returned as part of the Contact object.

The following list shows the possible Contact properties that can be passed in to look through and be returned as part of the found contacts:

  • id: A globally unique identifier

  • displayName: The name of this contact, suitable for display to end-users

  • name: An object containing all components of a person's name

  • nickname: A casual name for the contact. If you set this field to null, it will be stored as an empty string.

  • phoneNumbers: An array of all the contact's phone numbers

  • emails: An array of all the contact's email addresses

  • addresses: An array of all the contact's addresses

  • ims: An array of all the contact's instant messaging (IM) addresses (The ims property is not supported in this release.)

    Note:

    MAF does not support the Contact property ims in this release. If you create a contact with the ims property, MAF will save the contact without the ims property. As a result, if a user tries to perform a search based on ims, the user will not be able to find the contact. Also, if a user tries to enter ims in a search field, the ims will be returned as null.

  • organizations: An array of all the contact's organizations

  • birthday: The birthday of the contact. Although you cannot programmatically set a contact's birthday field and persist it to the address book, you can still use the address book application of the operating system to manually set this field.

  • note: A note about the contact. If you set this field to null, it will be stored as an empty string.

  • photos: An array of the contact's photos

  • categories: An array of all the contact's user-defined categories.

  • urls: An array of web pages associated to the contact

Note:

The timeout value for the findContacts method is set to 1 minute. If the operation of the device takes longer than the timeout allowed, a timeout error is displayed.

To customize a findContacts operation using the DeviceFeatures data control:

  1. In JDeveloper, drag the findContacts operation from the DeviceFeatures data control in the Data Controls panel and drop it on the page designer as a Link, Button, or Parameter Form.

    Link or Button: You will be prompted with the Edit Action Binding dialog to enter values for arguments to the findContacts operation. At runtime, a button or link will be displayed on the page, which will use the entered values to perform a findContacts operation when pressed.

    Parameter Form: Customize the form in the Edit Form Fields dialog. At runtime, an editable form will be displayed on the page, which enables the application user to enter values for the various Contact fields described above. Below this form will be a button, which will use the entered values to perform a findContacts operation when pressed.

  2. You can also drag a Contact return object from under the findContacts operation in the Data Controls panel and drop it on to the page as a Form. You can then customize the form in the Edit Form Fields dialog. When the findContacts operation is performed, the results will be displayed in this form.

The following example shows possible argument values for the findContacts method.

// This will return just one contact with only the ID field:
Contact[] foundContacts = DeviceManagerFactory.getDeviceManager().findContacts("", "", false);

// This will return all contacts with only ID fields:
Contact[] foundContacts = DeviceManagerFactory.getDeviceManager().findContacts("", "", true);

// This will return just one contact with all fields:
Contact[] foundContacts = DeviceManagerFactory.getDeviceManager().findContacts("*", "", false);

// This will return all contacts with all fields:
Contact[] foundContacts = DeviceManagerFactory.getDeviceManager().findContacts("*", "", true);

// These will throw an exception as contactFields is a required argument and cannot be null:
DeviceManagerFactory.getDeviceManager().findContacts(null, "", false);
DeviceManagerFactory.getDeviceManager().findContacts(null, "", true);

// These will throw an exception as the filter argument cannot be null:
DeviceManagerFactory.getDeviceManager().findContacts("", null, false);
DeviceManagerFactory.getDeviceManager().findContacts("", null, true);

Note:

The Contact fields passed are strings (containing the comma-delimited fields). If any arguments are passed as null to the method, an exception is thrown.

The following JavaScript example shows how to find a contact by family name and get the contact's name, phone numbers, email, addresses, and note.

var filter = ["name", "phoneNumbers", "emails", "addresses", "note"];
 
var options = new ContactFindOptions();
options.filter="FamilyName";
 
// Contacts, like many other device-specific features, are accessed from 
// the global 'navigator' object in JavaScript.
navigator.contacts.find(filter, onSuccess, onFail, options);
 
function onSuccess(contacts)
{
  alert ("Find Contact call succeeded! Number of contacts found = " + contacts.length);
}
 
function onFail(Error)
{
  alert("Find Contact failed: " + Error.code);
}

The following Java example shows how to find a contact by family name and get the contact's name, phone numbers, email, addresses, and note.

import oracle.adf.model.datacontrols.device.DeviceManagerFactory;

/*
 * Find Contact - Find contact by family name.
 *
 * See if we can find the contact that we just created.
 */

String familyName = "FamilyName"

// Access device features in Java code by acquiring an instance of the  
// DeviceManager from the DeviceManagerFactory.
Contact[] foundContacts = DeviceManagerFactory.getDeviceManager().findContacts(
    "name,phoneNumbers,emails,addresses,note", familyName, true); 

15.12.6 How to Use the updateContact Method to Enable Updating Contacts

The DeviceFeatures data control provides the updateContact method to help you add or update contact information such as phone number, email type, and postal code in the address book of the device. Use the procedures to customize an updateContact operation.

The DeviceFeatures data control includes the updateContact method, which enables MAF applications to leverage a device's interface and file system for managing contacts so users can update contacts in the address book of the device. MAF enables you to display the interface of the device and optionally pre-populate the updateContact fields. The updateContact method takes in a Contact object as a parameter and returns the updated Contact object, as shown in as shown in the example at the end of this section.

For more information about the updateContact method and the Contact object, see the DeviceDataControl class in the MAF Javadoc and refer to the Cordova documentation (http://cordova.apache.org/). Also see How to Use the findContacts Method to Enable Finding Contacts for a description of Contact properties.

Note:

The Contact object that is needed as the input parameter can be found using the findContacts method as described in How to Use the findContacts Method to Enable Finding Contacts. If a null Contact object is passed in to the method, an exception is thrown.

To customize an updateContact operation using the DeviceFeatures data control:

  1. In JDeveloper, drag the updateContact operation from the DeviceFeatures data control in the Data Controls panel and drop it on the page designer as a Link or Button.

    Link or Button: You will be prompted with the Edit Action Binding dialog to enter the Contact object parameter to the updateContact operation. This parameter must be an EL expression that refers to the property of a managed bean that is used to return the Contact from a Java bean class. Assuming a managed bean already exists with a getter for a Contact object, you can use the EL Expression Builder to set the value of the parameter. At runtime, a button or link will be displayed on the page, which will use the entered values to perform a updateContact operation when pressed. How to Use the createContact Method to Enable Creating Contacts shows an example of managed bean code for creating a Contact object.

  2. You can also drag a Contact return object from under the updateContact operation in the Data Controls panel and drop it on to the page as a Form. You can then customize the form in the Edit Form Fields dialog. When the updateContact operation is performed, the results will be displayed in this form.

The following examples show how to update and add a contact's phone number.

The following JavaScript example shows how to use updateContact.

function updateContact(contact)
{
  try
  {
    if (null != contact.phoneNumbers)
    {
      alert("Number of phone numbers = " + contact.phoneNumbers.length);
      var numPhoneNumbers = contact.phoneNumbers.length;
      for (var j = 0; j < numPhoneNumbers; j++)
      {
        alert("Type: " + contact.phoneNumbers[j].type + "\n" +
              "Value: "  + contact.phoneNumbers[j].value + "\n" +
              "Preferred: "  + contact.phoneNumbers[j].pref);
 
        contact.phoneNumbers[j].type = "mobile";
        contact.phoneNumbers[j].value = "408-555-0100";
      }
 
      // save
      contact.save(onSuccess, onFailure);
    }
    else
    {
      //alert ("No phone numbers found in the contact.");
    }
  }
  catch(e)
  {
    alert("updateContact - ERROR: " + e.description);
  }
}
 
function onSuccess()
{
  alert("Update Contact successful.");
}
 
function onFailure(Error)
{
  alert("Update Contact failed: " + Error.code);
}

The following JavaScript example shows how to use updateContact to add a phone number to existing phone numbers.

function updateContact(contact)
{
  try
  {
    var phoneNumbers = [1];
    phoneNumbers[0] = new ContactField('home', '650-555-0123', true);
    contact.phoneNumbers = phoneNumbers;
 
    // save
    contact.save(onSuccess, onFailure);
  }
  catch(e)
  {
    alert("updateContact - ERROR: " + e.description);
  }
}
 
function onSuccess()
{
  alert("Update Contact successful.");
}
 
function onFailure(Error)
{
  alert("Update Contact failed: " + Error.code);
}

The following Java code example shows how to use updateContact to update a contact's phone number, email type, and postal code.

import oracle.adf.model.datacontrols.device.DeviceManagerFactory;

/*
 * Update Contact - Updating phone number, email type, and adding address postal code
 */
String familyName = "FamilyName";
String phoneNumberNewValue = "650-555-0123";
String emailTypeNew = "work";
String addressPostalCode = "91234";

Contact[] foundContacts = DeviceManagerFactory.getDeviceManager().findContacts(
    "name,phoneNumbers,emails,addresses,note", familyName, true); 

// Assuming there was only one contact returned, we can use the first contact in the array.
// If more than one contact is returned then we have to filter more to find the exact contact 
// we need to update.

foundContacts[0].getPhoneNumbers()[0].setValue(phoneNumberNewValue);
foundContacts[0].getEmails()[0].setType(emailTypeNew);
foundContacts[0].getAddresses()[0].setPostalCode(addressPostalCode);

Contact updatedContact = DeviceManagerFactory.getDeviceManager().updateContact(foundContacts[0]);

The following Java example shows how to use updateContact to add a phone number to existing phone numbers.

import oracle.adf.model.datacontrols.device.DeviceManagerFactory;

String additionalPhoneNumberValue = "408-555-0123";
String additionalPhoneNumberType = "mobile";
// Create a new phoneNumber that will be appended to the previous one.
ContactField additionalPhoneNumber = new ContactField();
additionalPhoneNumber.setType(additionalPhoneNumberType);
additionalPhoneNumber.setValue(additionalPhoneNumberValue);

foundContacts[0].setPhoneNumbers(new ContactField[] { additionalPhoneNumber });

// Access device features in Java code by acquiring an instance of the DeviceManager 
// from the DeviceManagerFactory.
Contact updatedContact = DeviceManagerFactory.getDeviceManager().updateContact(foundContacts[0]);

Note:

The timeout value for the updateContact method is set to 1 minute. If the operation of the device takes longer than the timeout allowed, a timeout error is displayed.

15.12.7 How to Use the removeContact Method to Enable Removing Contacts

The DeviceFeatures data control provides the removeContact method to remove contacts, found using findContacts, from an address book on a device. Use the procedures to customize a removeContact operation.

The DeviceFeatures data control includes the removeContact method, which enables MAF applications to leverage the interface of a device and file system for managing contacts so users can remove contacts from the address book of the device. MAF enables you to display the interface of the device and optionally pre-populate the removeContact fields. The removeContact method takes in a Contact object as a parameter, as shown in the examples at the end of this section.

Note:

The Contact object that is needed as the input parameter can be found using the findContacts method as described in How to Use the findContacts Method to Enable Finding Contacts.

To customize a removeContact operation using the DeviceFeatures data control:

  1. In JDeveloper, drag the removeContact operation from the DeviceFeatures data control in the Data Controls panel and drop it on the page designer as a Link, Button, or Parameter Form.

    Link or Button: You will be prompted with the Edit Action Binding dialog to enter values for arguments to the removeContact operation. At runtime, a button or link will be displayed on the page, which will use the entered values to perform a removeContact operation when pressed.

    Parameter Form: Customize the form in the Edit Form Fields dialog. At runtime, an editable form will be displayed on the page, which enables the application user to enter values for the various Contact fields. Below this form will be a button, which will use the entered values to perform a removeContact operation when pressed.

  2. You can also drag a Contact return object from under the removeContact operation in the Data Controls panel and drop it on to the page as a Form. You can then customize the form in the Edit Form Fields dialog. When the removeContact operation is performed, the results will be displayed in this form.

The examples at the end of this section show you how to delete a contact that you found using findContacts. For information about the removeContact method and the Contact object, see the DeviceDataControl class in the MAF Javadoc and refer to the Cordova documentation (http://cordova.apache.org/).

Note:

In Android, the removeContact operation does not remove the contact fully. After a contact is removed by calling the removeContact method, a contact with the "(Unknown)" display name shows in the contacts list in the application.

The following JavaScript code example shows how to use removeContact.

// Remove the contact from the device
contact.remove(onSuccess,onError);
 
function onSuccess()
{
  alert("Removal Success");
}
 
function onError(contactError)'
{
  alert("Error = " + contactError.code);
}

The following Java code example shows how to use removeContact.

import oracle.adf.model.datacontrols.device.DeviceManagerFactory;

/*
 * Remove the contact from the device
 */
Contact[] foundContacts = DeviceManagerFactory.getDeviceManager().findContacts(
    "name,phoneNumbers,emails,addresses", familyName, true);

// Assuming there is only one contact returned, we can use the first contact in the array.
// If more than one contact is returned we will have to filter more to find the  
// exact contact we want to remove.

// Access device features in Java code by acquiring an instance of the DeviceManager 
// from the DeviceManagerFactory.
DeviceManagerFactory.getDeviceManager().removeContact(foundContacts[0]);

Note:

The timeout value for the removeContact method is set to 1 minute. If the operation of the device takes longer than the timeout allowed, a timeout error is displayed.

15.12.8 How to Use the startLocationMonitor Method to Enable Geolocation

The DeviceFeatures data control includes the startLocationMonitor method to track device location at a point in time, or periodically. Listen for changes in the location of a device using parameters such as enableHighAccuracy, updateInterval, and locationListener.

The DeviceFeatures data control includes the startLocationMonitor method, which enables MAF applications to use the geolocation services of a device in order to obtain and track the location of the device. MAF enables you to display the interface of a device and optionally pre-populate the startLocationMonitor fields.

MAF exposes APIs that enable you to acquire the current position of a device, allowing you to retrieve the current location of the device for one instant in time or to subscribe to it on a periodic basis. The examples at the end of this section show how to use geolocation to subscribe to changes in the location of a device and how to obtain the location of a device. For information about the startLocationMonitor method, see the DeviceDataControl class in Java API Reference for Oracle Mobile Application Framework and refer to the Cordova documentation (http://cordova.apache.org/).

Note:

The altitudeAccuracy property is not supported by Android devices.

Updates do not occur as frequently on the Android platform as on iOS.

To listen for changes in the location of a device using the DeviceFeatures data control:

In JDeveloper, drag the startLocationMonitor operation from the DeviceFeatures data control in the Data Controls panel to the page designer and drop it as a Link or Button. When prompted by the Edit Action Dialog, populate the fields with values for the parameters that the operation supports, as described in the following list or see the s startLocationMonitor method of the DeviceDataControl class in Java API Reference for Oracle Mobile Application Framework.

  • enableHighAccuracy: If true, use the most accurate possible method of obtaining a location fix. This is just a hint; the operating system may not respect it. Devices often have several different mechanisms for obtaining a location fix, including cell tower triangulation, Wi-Fi hotspot lookup, and true GPS. Specifying false indicates that you are willing to accept a less accurate location, which may result in a faster response or consume less power.

  • updateInterval: Defines how often, in milliseconds, to receive updates. Location updates may not be delivered as frequently as specified; the operating system may wait until a significant change in the position of the device has been detected before triggering another location update.

  • locationListener: EL expression that resolves to a bean method with the following signature:

    void methodName(Location newLocation)
    

    This EL expression will be evaluated every time a location update is received. For example, enter viewScope.LocationListenerBean.locationUpdated (without the surrounding#{}), then define a bean named LocationListenerBean in viewScope and implement a method with the following signature:

    public void locationUpdated(Location currentLocation) {
      System.out.println(currentLocation);
      // To stop subscribing to location updates, invoke the following:
      // DeviceManagerFactory.getDeviceManager().clearWatchPosition(
      //     currentLocation.getWatchId());
    }
    

    Note:

    Do not use the EL syntax #{LocationListenerBean.locationUpdate} to specify the locationListener, unless you truly want the result of evaluating that expression to be the name of the locationListener.

The example at the end of this section shows how to subscribe to changes in the location of the device using the DeviceManager.startUpdatingPosition method. For more information about the parameters that this method takes, see Java API Reference for Oracle Mobile Application Framework.

For an example of how to subscribe to changes in the position of the device using JavaScript, refer to the Cordova documentation (http://cordova.apache.org/).

Parameters returned in the callback function specified by the locationListener are as follows:

  • double getAccuracy—Accuracy level of the latitude and longitude coordinates in meters

  • double getAltitude—Height of the position in meters above the ellipsoid

  • double getLatitude—Latitude in decimal degrees

  • double getLongitude—Longitude in decimal degrees

  • double getAltitudeAccuracy—Accuracy level of the altitude coordinate in meters

  • double getHeading—Direction of travel, specified in degrees counting clockwise relative to the true north

  • double getSpeed—Current ground speed of the device, specified in meters per second

  • long getTimestamp—Creation of a timestamp in milliseconds since the Unix epoch

  • String getWatchId—Only used when subscribing to periodic location updates. A unique ID that can be subsequently used to stop subscribing to location updates

import oracle.adf.model.datacontrols.device.DeviceManagerFactory;
import oracle.adf.model.datacontrols.device.GeolocationCallback;
import oracle.adf.model.datacontrols.device.Location;

// Subscribe to location updates that will be delivered every 20 seconds, with high accuracy.
// As you can have multiple subscribers, let's identify this one as 'MyGPSSubscriptionID'.
// Notice that this call returns the watchID, which is usually the same as the watchID passed in.
// However, it may be different if the specified watchID conflicts with an existing watchID,
// so be sure to always use the returned watchID.
String watchID = DeviceManagerFactory.getDeviceManager().startUpdatingPosition(20000, true, "
       "MyGPSSubscriptionID", new GeolocationCallback() {
    public void locationUpdated(Location position) {
        System.out.println("Location updated to: " + position);
    }
});

// The previous call returns immediately so that you can continue processing. 
// When the device's location changes, the locationUpdated() method specified in  
// the previous call will be invoked in the context of the current feature.

// When you wish to stop being notified of location changes, call the following method:
DeviceManagerFactory().getDeviceManager().clearWatchPosition(watchID);

For more information about the startLocationMonitor and startHeadingMonitor methods, see the DeviceDataControl class in Java API Reference for Oracle Mobile Application Framework and refer to the Cordova documentation (http://cordova.apache.org/).

The following example shows how to get the current location of a device (one time) using the DeviceManager.getCurrentPosition. For information about the parameters that this method accepts, see Java API Reference for Oracle Mobile Application Framework.

import oracle.adf.model.datacontrols.device.DeviceManagerFactory;
import oracle.adf.model.datacontrols.device.Location;

// Get the device's current position, with highest accuracy, and accept a cached location that is 
// no older than 60 seconds.
Location currentPosition = DeviceManagerFactory.getDeviceManager().getCurrentPosition(60000, true);
System.out.println("The device's current location is: latitude=" + currentPosition.getLatitude() + 
    ", longitude=" + currentPosition.getLongitude());

15.12.9 How to Use the displayFile Method to Enable Displaying Files

The DeviceFeatures data control provides the displayFile method to display files that are local to the device, such as PDFs, image files, or Microsoft Office documents, using either the platform functionality on iOS devices, or using third-party applications on Android devices. The displayFile operation is customizable.

The DeviceFeatures data control includes the displayFile method, which enables MAF applications to display files that are local to the device. Depending on the platform, application users can view PDFs, image files, Microsoft Office documents, and various other file types. On iOS, the application user has the option to preview supported files within the MAF application. Users can also open those files with third-party applications, email them, or send them to a printer. On Android, all files are opened in third-party applications. In other words, the application user leaves the MAF application while viewing the file. The user may return to the MAF application by pressing the Android Back button. If the device does not have an application capable of opening the given file, an error is displayed. For an example of how the displayFile method opens files on both iOS- and Android-powered devices, see the DeviceDemo sample application. This application is available in the PublicSamples.zip file at the following location within the JDeveloper installation directory of your development computer:

jdev_install/jdeveloper/jdev/extensions/oracle.maf/Samples

The displayFile method is only able to display files that are local to the device. This means that remote files first have to be downloaded. Use the call AdfmfJavaUtilities.getDirectoryPathRoot(AdfmfJavaUtilities.DownloadDirectory) to return the directory root where downloaded files should be stored. Note that on iOS, this location is specific to the application, but on Android this location refers to the external storage directory. The external storage directory is publicly accessible and allows third-party applications to read files stored there.

Table 15-7 Supported File Types

iOS Android

For more information about supported file types, see the Quick Look preview controller documentation at the Apple iOS development site (http://developer.apple.com/library/ios/navigation/).

MAF will start the viewer associated with the given MIME type if it is installed on the device. There is no built-in framework for viewing specific file types. If the device does not have an application installed that handles the file type, the MAF application displays an error.

iWork documents

 

Microsoft Office documents (Office '97 and newer)

 

Rich Text Format (RTF) documents

 

PDF files

 

Images

 

Text files whose uniform type identifier (UTI) conforms to the public.text type

 

Comma-separated value (csv) files

 

To customize a displayFile operation using the DeviceFeatures data control:

  1. In JDeveloper, drag the displayFile operation from the DeviceFeatures data control in the Data Controls panel and drop it on the page designer as a Link, Button, or Parameter Form.

    Link or Button: You will be prompted with the Edit Action Binding dialog to enter values for arguments to the displayFile operation. At runtime, a button or link will be displayed on the page, which will use the entered values to perform a displayFile operation when pressed.

    Parameter Form: Customize the form in the Edit Form Fields dialog. At runtime, an editable form will be displayed on the page, which enables the application user to enter values for the various fields. Below this form will be a button, which will use the entered values to perform a displayFile operation when pressed.

The following example shows you how to view files using the displayFile method. For information about the displayFile method, see the DeviceDataControl class in the MAF Javadoc).

import oracle.adf.model.datacontrols.device.DeviceManagerFactory;

 URL remoteFileUrl;
        InputStream is;
        BufferedOutputStream fos;
        try {
           
            // Open connection to remote file; fileUrl here is a String containing the URL to the remote file.
            remoteFileUrl = new URL(fileUrl);
            URLConnection connection = remoteFileUrl.openConnection();
            is = new BufferedInputStream(connection.getInputStream());
            // Saving the file locally as 'previewTempFile.<extension>'
            String fileExt = fileUrl.substring(fileUrl.lastIndexOf('.'), fileUrl.length());
            String tempFile = "/previewTempFile" + fileExt;
            File localFile = null;
            // Save the file in the DownloadDirectory location
            localFile = new File(AdfmfJavaUtilities.getDirectoryPathRoot(AdfmfJavaUtilities.DownloadDirectory) + tempFile);
            if (localFile.exists()) {
                localFile.delete();
            }
            // Use buffered streams to download the file.
            fos = new BufferedOutputStream(new FileOutputStream(localFile));
            byte[] data = new byte[1024];
            int read = 0;
            while ((read = is.read(data)) != -1) {
                fos.write(data, 0, read);
            }
            is.close();
            fos.close();
 
            // displayFile takes a URL string which has to be encoded on iOS.
            // iOS does not handle "+" as an encoding for space (" ") but
            // expects "" instead.  Also, the leading slash MUST NOT be
            // encoded to "%2F".  We will revert it to a slash after the
            // URLEncoder converts it to "%2F".
            StringBuffer buffer = new StringBuffer();
            String path = URLEncoder.encode(localFile.getPath(), "UTF-8");
            // replace "+" with ""
            String replacedString = "+";
            String replacement = "";
            int index = 0, previousIndex = 0;
            index = path.indexOf(replacedString, index);
            while (index != -1) {
                buffer.append(path.substring(previousIndex, index)).append(replacement);
                previousIndex = index + 1;
                index = path.indexOf(replacedString, index + replacedString.length());
            }
            buffer.append(path.substring(previousIndex, path.length()));
            // Revert the leading encoded slash ("%2F") to a literal slash ("/").
            if (buffer.indexOf("%2F") == 0) {
                buffer.replace(0, 3, "/");
            }
 
            // Create URL and invoke displayFile with its String representation.
            URL localURL = null;
            if (Utility.getOSFamily() == Utility.OSFAMILY_ANDROID) {
                localURL = new URL("file", "localhost", localFile.getAbsolutePath());
            }
            else if (Utility.getOSFamily() == Utility.OSFAMILY_IOS)
            {
                localURL = new URL("file", "localhost", buffer.toString());
            }
            DeviceManagerFactory.getDeviceManager().displayFile(localURL.toString(), "remote file");
        } catch (Throwable t) {
            System.out.println("Exception caught: " + t.toString());
        }

15.12.10 How to Use the addLocalNotification and cancelLocalNotification Methods to Manage Local Notifications

The DeviceFeatures data control provides the addLocalNotification and cancelLocalNotification methods to schedule or cancel local notifications. Use the procedure to customize an addLocalNotification or cancelLocalNotification operation.

The DeviceFeatures data control includes the addLocalNotification and cancelLocalNotification methods, which enable MAF applications to leverage the interface of a device for managing notifications so users can schedule or cancel local notifications.

To customize an addLocalNotification or cancelLocalNotification operation using the DeviceFeatures data control:

  1. In JDeveloper, drag the addLocalNotification or cancelLocalNotification operation from the DeviceFeatures data control in the Data Controls panel and drop it on the page designer as a Button, Link, List Item, or Parameter Form.

    Button, Link, or List Item: You will be prompted with the Edit Action Binding dialog to enter values for arguments to the addLocalNotification or cancelLocalNotification operation. For more information on this dialog, see the online help for Oracle JDeveloper. At runtime, a button, link, or list item will be displayed on the page, which will use the entered values to perform the addLocalNotification or cancelLocalNotification operation when pressed.

    Parameter Form: Customize the form in the Edit Form Fields dialog. For more information on this dialog, see the online help for Oracle JDeveloper. At runtime, an editable form will be displayed on the page, which enables the application user to enter values for the various fields. Below this form will be a button, which will use the entered values to perform the addLocalNotification or cancelLocalNotification operation when pressed.

Figure 15-17 shows the Edit Action Binding dialog, which you use to configure the parameters of the selected operation. In this example, the notificationID of the cancelLocalNotification operation is bound to the result of the addLocalNotification operation.

Figure 15-17 Setting Bindings for Scheduling Local Notifications

This image is described in the surrounding text

Figure 15-18 shows how you can use the expression builder to bind the result of the addLocalNotification operation to the cancelLocalNotification operation.

Figure 15-18 Binding cancelLocalNotification to the result of addLocalNotification

This image is described in the surrounding text

For information about the addLocalNotification and cancelLocalNotification methods, see the DeviceDataControl class in the MAF Javadoc). See Managing Local Notifications. Also see Introduction to Notifications.

15.12.11 What You May Need to Know About Device Properties

MAF includes properties that are accessed from Java, JavaScript, and EL to support dynamic behavior in applications. Review the list of properties to know about querying them, the expected return values, and property changes during the application lifecycle.

There may be features of your application that rely on specific device characteristics or capabilities. For example, you may want to present a different user interface depending on the screen orientation of the device, or there may be a mapping feature that you want to enable only if the device supports geolocation. MAF provides a number of properties that you can access from Java, JavaScript, and EL in order to support this type of dynamic behavior. Table 15-8 lists these properties, along with information about how to query them, what values to expect in return, and whether the property can change during the lifecycle of the application. The example at the end of this section shows how you can access these properties using JavaScript.

Note:

The timeout value for device properties is set to 1 minute. If the operation of the device takes longer than the timeout allowed, a timeout error is displayed.

Table 15-8 Device Properties and Corresponding EL Expressions

Property Static/ Dynamic EL Expression Sample Value Java API

device.name

Static

#{deviceScope.device.name}

"iPhone Simulator", "Joe Smith's iPhone"

DeviceManager.getName()

device.platform

Static

#{deviceScope.device.platform}

"iPhone Simulator", "iPhone"

DeviceManager.getPlatform()

device.version

Static

#{deviceScope.device.version}

"4.3.2", "5.0.1"

DeviceManager.getVersion()

device.os

Static

#{deviceScope.device.os}

"iOS"

DeviceManager.getOs()

device.model

Static

#{deviceScope.device.model}

"x86_64", "i386", "iPhone3,1"

DeviceManager.getModel()

device.phonegap

Static

#{deviceScope.device.phonegap}

"1.0.0"

DeviceManager.getPhonegap()

hardware.hasCamera

Static

#{deviceScope.hardware.hasCamera}

"true", "false"

DeviceManager.hasCamera()

hardware.hasContacts

Static

#{deviceScope.hardware.hasContacts}

"true", "false"

DeviceManager.hasContacts()

hardware.hasTouchScreen

Static

#{deviceScope.hardware.hasTouchScreen}

"true", "false"

DeviceManager.hasTouchScreen()

hardware.hasGeolocation

Static

#{deviceScope.hardware.hasGeolocation}

"true", "false"

DeviceManager.hasGeolocation()

hardware.hasAccelerometer

Static

#{deviceScope.hardware.hasAccelerometer}

"true", "false"

DeviceManager.hasAccelerometer()

hardware.hasCompass

Static

#{deviceScope.hardware.hasCompass}

"true", "false"

DeviceManager.hasCompass()

hardware.hasFileAccess

Static

#{deviceScope.hardware.hasFileAccess}

"true", "false"

DeviceManager.hasFileAccess()

hardware.hasLocalStorage

Static

#{deviceScope.hardware.hasLocalStorage}

"true", "false"

DeviceManager.hasLocalStorage()

hardware.hasMediaPlayer

Static

#{deviceScope.hardware.hasMediaPlayer}

"true", "false"

DeviceManager.hasMediaPlayer()

hardware.hasMediaRecorder

Static

#{deviceScope.hardware.hasMediaRecorder}

"true", "false"

DeviceManager.hasMediaRecorder()

hardware.networkStatus

Dynamic

#{deviceScope.hardware.networkStatus}

"wifi", "2g", "unknown", "none"Foot 2

DeviceManager.getNetworkStatus()

hardware.screen.width

Dynamic

#{deviceScope.hardware.screen.width}

320, 480

DeviceManager.getScreenWidth()

hardware.screen.height

Dynamic

#{deviceScope.hardware.screen.height}

480, 320

DeviceManager.getScreenHeight()

hardware.availableWidth

Dynamic

#{deviceScope.hardware.screen.availableWidth}

<= 320, <= 480

DeviceManager.getAvailableScreenWidth()

hardware.availableHeight

Dynamic

#{deviceScope.hardware.screen.availableHeight}

<= 480, <= 320

DeviceManager.getAvailableScreenHeight()

hardware.screen.dpi

Static

#{deviceScope.hardware.screen.dpi}

160, 326

DeviceManager.getScreenDpi()

hardware.screen.diagonalSize

Static

#{deviceScope.hardware.screen.diagonalSize}

9.7, 6.78

DeviceManager.getScreenDiagonalSize()

hardware.screen.scaleFactor

Static

#{deviceScope.hardware.screen.scaleFactor}

1.0, 2.0

DeviceManager.getScreenScaleFactor()

Footnote 2

If both wifi and 2G are turned on, network status will be wifi, as wifi takes precedence over 2G.

The following example shows how you can access device properties using JavaScript.

<!DOCTYPE html>
<html>
  <head>
    <title>Device Properties Example</title>
 
    <script type="text/javascript" charset="utf-8" src="cordova-2.2.0.js"></script>
    <script type="text/javascript" charset="utf-8">

    // Wait for Cordova to load
    //
    //document.addEventListener("deviceready", onDeviceReady, false);
    document.addEventListener("showpagecomplete",onDeviceReady,false);
 
    // Cordova is ready
    //
    function onDeviceReady() {
        adf.mf.api.getDeviceProperties(properties_success, properties_fail);
    }
    
    function properties_success(response) {
      try {
        var element = document.getElementById('deviceProperties');
        var device = response.device;
        var hardware = response.hardware;
        element.innerHTML = 'Device Name:              ' + device.name            + '<br />' +
                            'Device Platform:          ' + device.platform        + '<br />' +
                            'Device Version:           ' + device.version         + '<br />' +
                            'Device OS:                ' + device.os              + '<br />' +
                            'Device Model:             ' + device.model           + '<br />' +
                            'Hardware Screen Width:    ' + hardware.screen.width  + '<br />' +
                            'Hardware Screen Height:   ' + hardware.screen.height + '<br />' +
      } catch (e) {alert("Exception: " + e);}
    }
    
    function properties_fail(error) {
        alert("getDeviceProperties failed");
    }

    </script>
  </head>
  <body>
    <p id="deviceProperties">Loading device properties...</p>
  </body>
</html>

Note:

You can declaratively bind a JavaScript function to the showpagecomplete event by adding an amx:clientListener tag as a direct child of <amx:view>, as in the following example:

<amx:clientListener type="showpagecomplete" method="myShowPageCompleteHandler"/>

For more information about the Client Listener (clientListener) component, see How to Use the Client Listener.

15.13 Validating Attributes

In MAF, validation rules are set on binding attributes, and validation occurs in the data control layer. You can define both validators for attributes exposed by the data controls, and validation message for attributes.

In the Mobile Application Framework, validation occurs in the data control layer, with validation rules set on binding attributes. Attribute validation takes place at a single point in the system, during the setValue operation on the bindings.

You can define the following validators for attributes exposed by the data controls:

  • Compare validator

  • Length validator

  • List validator

  • Range validator

All validators for a given attribute are executed, and nested exceptions are thrown for every validator that does not pass. You can define a validation message for attributes, which is displayed when a validation rule is fired at runtime. See Validating Input and How to Add Validation Rules.

Note:

Due to a JSON limitation, the value that a BigDecimal can hold is within the range of a Double, and the value that a BigInteger can hold is within the range of a Long. If you want to use numbers greater than those allowed, you can call toString on BigDecimal or BigInteger to (de)serialize values as String.

Table 15-9 lists supported validation combinations for the length validator.

Table 15-9 Length Validation

Compare type Byte Character

Equals

Supported

Supported

Not Equals

Supported

Supported

Less Than

Supported

Supported

Greater Than

Supported

Supported

Less Than Equal To

Supported

Supported

Greater Than Equal To

Supported

Supported

Between

Supported

Supported

Table 15-10 and Table 15-11 list supported validation combinations for the range validator.

Table 15-10 Range Validation

Compare type Byte Char Double Float Integer Long Short

Between

Supported

Supported

Supported

Supported

Supported

Supported

Supported

Not Between

Supported

Supported

Supported

Supported

Supported

Supported

Supported

Table 15-11 Range Validation - math, sql, and util Packages

Compare type java.math.BigDecimal java.math.BigInteger java.sql.Date java.sql.Time java.sql.Timestamp java.util.Date

Between

Supported

Supported

Not supported

Not supported

Not supported

Not supported

Not Between

Supported

Supported

Not supported

Not supported

Not supported

Not supported

Table 15-12 lists supported validation combinations for the list validator.

Table 15-12 List Validation

Compare type String

In

Supported

Not In

Supported

Table 15-13 and Table 15-14 lists supported validation combinations for the compare validator.

Table 15-13 Compare Validation

Compare type Byte Char Double Float Integer Long Short String

Equals

Supported

Supported

Supported

Supported

Supported

Supported

Supported

Supported

Not Equals

Supported

Supported

Supported

Supported

Supported

Supported

Supported

Supported

Less Than

Not supported

Supported

Supported

Supported

Supported

Supported

Supported

Not supported

Greater Than

Not supported

Supported

Supported

Supported

Supported

Supported

Supported

Not supported

Less Than Equal To

Not supported

Supported

Supported

Supported

Supported

Supported

Supported

Not supported

Greater Than Equal To

Not supported

Supported

Supported

Supported

Supported

Supported

Supported

Not supported

Table 15-14 Compare Validation - java.math, java.sql, and java.util Packages

Compare type java.math.BigDecimal java.math.BigInteger java.sql.Date java.sql.Time java.sql.Timestamp java.util.Date

Equals

Supported

Supported

Not supported

Not supported

Not supported

Not supported

Not Equals

Supported

Supported

Not supported

Not supported

Not supported

Not supported

Less Than

Supported

Supported

Not supported

Not supported

Not supported

Not supported

Greater Than

Supported

Supported

Not supported

Not supported

Not supported

Not supported

Less Than Equal To

Supported

Supported

Not supported

Not supported

Not supported

Not supported

Greater Than Equal To

Supported

Supported

Not supported

Not supported

Not supported

Not supported

15.13.1 How to Add Validation Rules

MAF users can define validation rules. Use the procedure to add a validation rule to a data controls object using the Overview Editor for Data Control Structure Files - Attributes Page.

You can define validation rules for a variety of use cases. To add a declarative validation rule to an entity object, use the Overview Editor for Data Control Structure Files - Attributes Page.

To add a validation rule:

  1. From the Data Controls panel, right-click on a data controls object and select Edit Definition.
  2. In the Overview Editor for Data Control Structure Files, select the Attributes page.
    This image is described in the surrounding text
  3. Select the Validation Rules tab in the lower part of the page and then click Add. In the resulting Add Validation Rule dialog, define the validation rule and the failure handling.
    This image is described in the surrounding text

15.13.2 What You May Need to Know About the Validator Metadata

The data control structure metadata XML files contain validator metadata.

The validator metadata is placed into the data control structure metadata XML files at design time. The following example shows a sample length validator.

<?xml version="1.0" encoding="windows-1252" ?>
<!DOCTYPE PDefViewObject SYSTEM "jbo_03_01.dtd">
<PDefViewObject
   xmlns="http://xmlns.oracle.com/bc4j"
   Name="Product"
   Version="12.1.1.61.36"
   xmlns:validation="http://xmlns.oracle.com/adfm/validation">
   <DesignTime>
      <Attr Name="_DCName" Value="DataControls.ProductListBean"/>
      <Attr Name="_SDName" Value="mobile.Product"/>
   </DesignTime>
   <PDefAttribute
      Name="name">
      <validation:LengthValidationBean
         Name="nameRule0"
         OnAttribute="name"
         CompareType="GREATERTHAN"
         DataType="BYTE"
         CompareLength="5"
         Inverse="false"/>
   </PDefAttribute>
</PDefViewObject>

15.14 Using Background Threads

Data model values can be updated with background Java threads. Update model objects from a background thread by using the MafExecutorService API.

A background thread may be useful when fetching data (say from a remote server) or computing values with a complex algorithm. You can also use background threads to fetch or compute data values, but you should not use them to update the application’s data model objects directly, because this could result in conflicts with the application’s user interface threads.

To update model objects from a background thread, use the MafExecutorService API to submit a Java Runnable that will perform the model updates. First, obtain the new or updated model values (fetched or computed) and then submit a Runnable to update the values in the application’s data model objects, as shown in the following example.

        //  First, fetch/compute new data values.
        fetchUpdatedValues();

        //  Next, use a Runnable to update the model objects.
        MafExecutorService.execute(new Runnable()
        {
          public void run()
          {
            doModelUpdates();
            AdfmfJavaUtilities.flushDataChangeEvent();
          }
        });

Note:

To ensure the application does not become unresponsive, the submitted task must be of short duration. Feature locks may be acquired before executing the task, which will not be released until the task completes.

For more information on the oracle.adfmf.framework.api.MafExecutorService.execute class, see the MAF Javadoc.

15.15 Working with Data Change Events

The user interface must be updated to reflect underlying data changes. Selecting the Notify listeners when property changes option sends notifications for property changes; using the ProviderChangeSupport class sends notifications relating to collection elements; and calling the AdfmfJavaUtilities.flushDataChangeEvent method forces data changes on AMX pages to the client.

To simplify data change events, JDeveloper uses the property change listener pattern. In most cases you can use JDeveloper to generate the necessary code to source notifications from the property accessors of the beans by selecting the Notify listeners when property changes checkbox in the Generate Accessors dialog (see About the Managed Beans Category for details). The PropertyChangeSupport object is generated automatically, with the calls to firePropertyChange in the newly-generated setter method. Additionally, the addPropertyChangeListener and removePropertyChangeListener methods are added so property change listeners can register and unregister themselves with this object. This is what the framework uses to capture changes to be pushed to the client cache and to notify the user interface layer that data has been changed.

Note:

If you are manually adding a PropertyChangeSupport object to a class, you must also include the addPropertyChangeListener and removePropertyChangeListener methods (using these explicit method names).

Property changes alone will not solve all the data change notifications, as in the case where you have a bean wrapped by a data control and you want to expose a collection of items. While a property change is sufficient when individual items of the list change, it is not sufficient for cardinality changes. In this case, rather than fire a property change for the entire collection, which would cause a degradation of performance, you can instead refresh just the collection delta. To do this you need to expose more data than is required for a simple property change, which you can do using the ProviderChangeSupport class. Provider change events are like property change events but apply to the entire provider instead of just an individual property.

Note:

The ProviderChangeSupport object is not generated automatically—you must manually add it to your class—along with the addProviderChangeListener, removeProviderChangeListener, and getKey() methods (using these explicit method names). The getKey() method must return a string that produces a unique value for the provider. As an alternative to adding the getKey() method to your class, designate an attribute in the data control as the key attribute in the data control structure file using the overview editor shown in Figure 15-19.

Figure 15-19 Selecting a Key Attribute in the Data Control Structure File of a Data Control

The surrounding text describes the image.

Since the provider change is required only when you have a dynamic collection exposed by a data control wrapped bean, there are only a few types of provider change events to fire:

  • fireProviderCreate—when a new element is added to the collection

  • fireProviderDelete—when an element is removed from the collection

  • fireProviderChange—when a single element is changed in the collection (necessary to prevent the whole list from refreshing)

  • fireProviderRefresh—when multiple changes are done to the collection at one time and you decide it is better to simply ask for the client to refresh the entire collection (this should only be used in bulk operations)

The ProviderChangeSupport class is used for sending notifications relating to collection elements, so that components update properly when a change occurs in a Java bean data control. It follows a similar pattern to the automatically-generated PropertyChangeSupport class, but the event objects used with ProviderChangeSupport send more information, including the type of operation as well as the key and position of the element that changed. ProviderChangeSupport captures structural changes to a collection, such as adding or removing an element (or provider) from a collection. PropertyChangeSupport captures changes to the individual items in the collection.

The following example shows how to use ProviderChangeSupport for sending notifications relating to structural changes to collection elements (such as when adding or removing a child). For more information on the ProviderChangeListener interface as well as the ProviderChangeEvent and ProviderChangeSupport classes, see the MAF Javadoc.

public class NotePad {
   private static List s_notes = null;
 
/* manually adding property change listener as well as provider change listener. */
   protected transient PropertyChangeSupport 
                       propertyChangeSupport = new PropertyChangeSupport(this);
   protected transient ProviderChangeSupport 
                       providerChangeSupport = new ProviderChangeSupport(this);
 
    public NotePad() {
        …
    }
 
    public  mobile.Note[] getNotes() {
        mobile.Note n[] = null;
 
        synchronized (this) {
            if(s_notes.size() > 0) {
                n = (mobile.Note[])
                    s_notes.toArray(new mobile.Note[s_notes.size()]);
            }
            else {
                n = new mobile.Note[0];
            }
        }
 
        return n;
    }
 
    public void addNote() {
        System.out.println("Adding a note ....");
        Note n = new Note();
        int s = 0;
        
        synchronized (this) {
            s_notes.add(n);
            s = s_notes.size();
        }
 
        System.out.println("firing the events");
        providerChangeSupport.fireProviderCreate("notes", n.getUid(), n);
    }
    
    public void removeNote() {
        System.out.println("Removng a note ....");
        if(s_notes.size() > 0) {
            int end = -1;
            Note n = null;
 
            synchronized (this) {
                end = s_notes.size() - 1;
                n = (Note)s_notes.remove(end);
            }
            
            System.out.println("firing the events");
            providerChangeSupport.fireProviderDelete("notes", n.getUid());
        }
    }
    
    public void RefreshNotes() {
        System.out.println("Refreshing the notes ....");
 
        providerChangeSupport.fireProviderRefresh("notes");
    }
    
    public void addProviderChangeListener(ProviderChangeListener l) {
        providerChangeSupport.addProviderChangeListener(l);
    }
 
    public void removeProviderChangeListener(ProviderChangeListener l) {
        providerChangeSupport.removeProviderChangeListener(l);
    }
 
    protected String   status;    
    
    /* --- JDeveloper generated accessors --- */
 
    public void addPropertyChangeListener(PropertyChangeListener l) {
        propertyChangeSupport.addPropertyChangeListener(l);
    }
 
    public void removePropertyChangeListener(PropertyChangeListener l) {
        propertyChangeSupport.removePropertyChangeListener(l);
    }
 
    public void setStatus(String status) {
        String oldStatus = this.status;
        this.status = status;
        propertyChangeSupport.firePropertyChange("status", oldStatus, status);
    }
 
    public String getStatus() {
        return status;
    }
}

Data changes are passed back to the client (to be cached) with any response message or return value from the JVM layer. This allows JDeveloper to compress and reduce the number of events and updates to refresh to the user interface, allowing the framework to be as efficient as possible.

However, there are times where you may need to have a background thread handle a long-running process (such as web-service interactions, database interactions, or expensive computations) and notify the user interface independent of a user action. To update data on an AMX page to reflect the current values of data fields whose values have changed, you can avoid the performance hit associated with reloading the whole AMX page by calling AdfmfJavaUtilities.flushDataChangeEvent to force the currently queued data changes to the client.

Note:

The flushDataChangeEvent method can only be executed from a background thread.

The following example shows how you can use the flushDataChangeEvent method to force pending data changes to the client. For more information about oracle.adfmf.framework.api.AdfmfJavaUtilities.flushDataChangeEvent, see Java API Reference for Oracle Mobile Application Framework.

 
/* Note – Simple POJO used by the NotePad managed bean or data control wrapped bean */        
 
package mobile;
 
import oracle.adfmf.amx.event.ActionEvent;
import oracle.adfmf.framework.api.AdfmfJavaUtilities;
import oracle.adfmf.java.beans.PropertyChangeListener;
import oracle.adfmf.java.beans.PropertyChangeSupport;
 
 
/**
 * Simple note object
 * uid   - unique id - generated and not mutable
 * title - title for the note - mutable
 * note  - note comment - mutable
 */
public class Note {
    /* standard JDeveloper generated property change support */
    protected transient PropertyChangeSupport 
                       propertyChangeSupport = new PropertyChangeSupport(this);
 
    private static boolean s_backgroundFlushTestRunning = false;
 
 
    public Note() {
        this("" + (System.currentTimeMillis() % 10000));
    }
 
    public Note(String id) {
        this("UID-"+id, "Title-"+id, "");
    }
 
    public Note(String uid, String title, String note) {
        this.uid     = uid;
        this.title   = title;
        this.note    = note;
    }
 
 
    /* update the current note with the values passed in */
    public void updateNote(Note n) {
        if (this.getUid().compareTo(n.getUid()) == 0) {
            this.setTitle(n.getTitle());
            this.setNote(n.getNote());
        } 
        else {
            throw new IllegalArgumentException("note");
        }
    }
 
 
    /* background thread to simulate some background process that make changes */
    public void startNodeBackgroundThread(ActionEvent actionEvent) {
        Thread backgroundThread   = new Thread() {
            public void run() {
                System.out.println("startBackgroundThread enter - " + 
                                                      s_backgroundFlushTestRunning);
                
                s_backgroundFlushTestRunning = true;
                for(int i = 0; i <= iterations; ++i) {
                    try {
                        System.out.println("executing " + i + " of " + iterations + "
                                " iterations.");
                        
                        /* update a property value */                    
                        if(i == 0) {
                            setNote("thread starting");
                        }
                        else if( i == iterations) {
                            setNote("thread complete");
                            s_backgroundFlushTestRunning = false;                        
                        }
                        else {
                            setNote("executing " + i + " of " + iterations + " iterations.");
                        }
                        setVersion(getVersion() + 1);
                        setTitle("Thread Test v" + getVersion());
                        AdfmfJavaUtilities.flushDataChangeEvent();  /* key line */
                    }
                    catch(Throwable t) {
                        System.err.println("Error in the background thread: " + t);
                    }
 
                    try {
                        Thread.sleep(delay);  /* sleep for 6 seconds */
                    } 
                    catch (InterruptedException ex) {
                        ex.printStackTrace();
                    }
                }
            }
        };
        
        backgroundThread.start();
    }
    
    protected String uid;
    protected String title;
    protected String note;
    protected int    version;
 
    protected int iterations = 10;
    protected int delay = 500;
    
    /* --- JDeveloper generated accessors --- */
 
    public void addPropertyChangeListener(PropertyChangeListener l) {
        propertyChangeSupport.addPropertyChangeListener(l);
    }
 
    public void removePropertyChangeListener(PropertyChangeListener l) {
        propertyChangeSupport.removePropertyChangeListener(l);
    }
 
    public String getUid() {
        return uid;
    }
 
    public void setTitle(String title) {
        String oldTitle = this.title;
        this.title = title;
        propertyChangeSupport.firePropertyChange("title", oldTitle, title);
    }
 
    public String getTitle() {
        return title;
    }
 
    public void setNote(String note) {
        String oldNote = this.note;
        this.note = note;
        propertyChangeSupport.firePropertyChange("note", oldNote, note);
    }
 
    public String getNote() {
        return note;
    }
 
    public void setVersion(int version) {
        int oldVersion = this.version;
        this.version = version;
        propertyChangeSupport.firePropertyChange("version", oldVersion, version);
    }
 
    public int getVersion() {
        return version;
    }
 
    public void setIterations(int iterations) {
        int oldIterations = this.iterations;
        this.iterations = iterations;
        propertyChangeSupport.
                 firePropertyChange("iterations", oldIterations, iterations);
    }
 
    public int getIterations() {
        return iterations;
    }
 
    public void setDelay(int delay) {
        int oldDelay = this.delay;
        this.delay = delay;
        propertyChangeSupport.
                firePropertyChange("delay", oldDelay, delay);
    }
 
    public int getDelay() {
        return delay;
    }
}
         
  
/* NotePad – Can be used as a managed bean or wrapped as a data control */
           
package mobile;
 
import java.util.ArrayList;
import java.util.List;
 
import oracle.adfmf.amx.event.ActionEvent;
import oracle.adfmf.framework.api.AdfmfJavaUtilities;
import oracle.adfmf.java.beans.PropertyChangeListener;
import oracle.adfmf.java.beans.PropertyChangeSupport;
import oracle.adfmf.java.beans.ProviderChangeListener;
import oracle.adfmf.java.beans.ProviderChangeSupport;
 
 
public class NotePad {
    private static List s_notes = null;
    private static boolean s_backgroundFlushTestRunning = false;
    
    protected transient PropertyChangeSupport propertyChangeSupport = 
                                                   new PropertyChangeSupport(this);
 
    protected transient ProviderChangeSupport 
        providerChangeSupport = new ProviderChangeSupport(this);
 
    public NotePad() {
        if (s_notes == null) {
            s_notes = new ArrayList();
            
            for(int i = 1000; i < 1003; ++i) {
                s_notes.add(new Note(""+i));
            }
        }
    }
 
    public  mobile.Note[] getNotes() {
        mobile.Note n[] = null;
 
        synchronized (this) {
            if(s_notes.size() > 0) {
                n = (mobile.Note[])s_notes.
                     toArray(new mobile.Note[s_notes.size()]);
            }
            else {
                n = new mobile.Note[0];
            }
        }
 
        return n;
    }
 
    public void addNote() {
        System.out.println("Adding a note ....");
        Note  n = new Note();
        int   s = 0;
        
        synchronized (this) {
            s_notes.add(n);
            s = s_notes.size();
        }
 
        System.out.println("firing the events");
        
        /* update the note count property on the screen */
        propertyChangeSupport.
             firePropertyChange("noteCount", s-1, s);
 
        /* update the notes collection model with the new note */
        providerChangeSupport.
             fireProviderCreate("notes", n.getUid(), n);
 
        /* to update the client side model layer */
        AdfmfJavaUtilities.flushDataChangeEvent();
    }
    
    public void removeNote() {
        System.out.println("Removing a note ....");
        if(s_notes.size() > 0) {
            int end = -1;
            Note n = null;
 
            synchronized (this) {
                end = s_notes.size() - 1;
                n = (Note)s_notes.remove(end);
            }
            
            System.out.println("firing the events");
            
            /* update the client side model layer */
            providerChangeSupport.fireProviderDelete("notes", n.getUid());
 
           /* update the note count property on the screen */
           propertyChangeSupport.firePropertyChange("noteCount", -1, end);
        }
    }
    
    public void RefreshNotes() {
        System.out.println("Refreshing the notes ....");
 
        /* update the entire notes collection on the client */
        providerChangeSupport.fireProviderRefresh("notes");
    }
    
    public int getNoteCount() {
        int size = 0;
        
        synchronized (this) {
            size = s_notes.size();
        }
       return size;
    }
 
    public void addProviderChangeListener(ProviderChangeListener l) {
        providerChangeSupport.addProviderChangeListener(l);
    }
 
    public void removeProviderChangeListener(ProviderChangeListener l) {
        providerChangeSupport.removeProviderChangeListener(l);
    }
 
    public void startListBackgroundThread(ActionEvent actionEvent) {
        for(int i = 0; i < 10; ++i) {
            _startListBackgroundThread(actionEvent);
            try {
                Thread.currentThread().sleep(i * 1234);
            } 
            catch (InterruptedException e) {
            }
        }
    }
    
    public void 
    _startListBackgroundThread(ActionEvent actionEvent) {
        Thread backgroundThread = new Thread() {
            public void run() {
                s_backgroundFlushTestRunning = true;
                
                for(int i = 0; i <= iterations; ++i) {
                    System.out.println("executing " + i + 
                          " of " + iterations + " iterations.");
                    
                    try {
                        /* update a property value */                    
                        if(i == 0) {
                            setStatus("thread starting");
                            addNote();  // add a note
                        }
                        else if( i == iterations) {
                            setStatus("thread complete");
                            removeNote();  // remove a note
                            s_backgroundFlushTestRunning = false;                        
                        }
                        else {
                            setStatus("executing " + i + " of " + 
                                    iterations + " iterations.");
                            
                            synchronized (this) {
                                if(s_notes.size() > 0) {
                                    Note n =(Note)s_notes.get(0);
                                
                                    n.setTitle("Updated-" + 
                                          n.getUid() + " v" + i);
                                }
                            }
                        }
                        AdfmfJavaUtilities.flushDataChangeEvent();
                    }
                    catch(Throwable t) {
                        System.err.
                        println("Error in bg thread - " + t);
                    }
 
                    try {
                         Thread.sleep(delay);
                    } 
                    catch (InterruptedException ex) {
                        setStatus("inturrpted " + ex);
                        ex.printStackTrace();
                    }
                }
            }
        };
        
        backgroundThread.start();
    }
 
    
    protected int iterations = 100;
    protected int delay      = 750;
 
    protected String   status;    
    
    /* --- JDeveloper generated accessors --- */
 
    public void addPropertyChangeListener(PropertyChangeListener l) {
        propertyChangeSupport.addPropertyChangeListener(l);
    }
 
    public void removePropertyChangeListener(PropertyChangeListener l) {
        propertyChangeSupport.removePropertyChangeListener(l);
    }
 
    public void setStatus(String status) {
        String oldStatus = this.status;
        this.status = status;
        propertyChangeSupport.firePropertyChange("status", oldStatus, status);
    }
 
    public String getStatus() {
        return status;
    }
 
    public void setIterations(int iterations) {
        int oldIterations = this.iterations;
        this.iterations = iterations;
        propertyChangeSupport.firePropertyChange("iterations", oldIterations, iterations);
    }
 
    public int getIterations() {
        return iterations;
    }
 
    public void setDelay(int delay) {
        int oldDelay = this.delay;
        this.delay = delay;
        propertyChangeSupport.firePropertyChange("delay", oldDelay, delay);
    }
 
    public int getDelay() {
        return delay;
    }
}
         

The StockTracker sample application provides an example of how data change events use Java to enable data changes to be reflected in the user interface. For more information about this and other sample applications, see MAF Sample Applications.