Real Life ADF Mobile: 10 things you don't learn from the devguide
Oracle ADF Mobile has been around for over a year by now. There is a great developer guide available for everybody who wants to create an ADF Mobile application. However, when you are building your first ADF Mobile application you will definitely run into issues that cannot be solved by reading the developer guide.
Think of performance issues when taking pictures with modern devices. Images can take up to 5 Megabytes. What can you do to create a grid like springboard ? These are all topics not covered by the developer guide or by any available ADF mobile training.
In this session you will learn solutions for these and more real life ADF Mobile issues.
4. Remote URLs
• For embedding existing web pages in your
ADF Mobile app.
• For instance:
– News Website
– Existing enterprise app Mobile Browser Pages
• Note:
– Best use Optimized Mobile Browser Pages
– Apache Trinidad components
– Oracle recommends using ADF Mobile browser
5. Feature as Remote URL
• Create New Feature as
Remote URL
• Create URL Connection
6. Whitelisting
• Why do we need to do this ?
• Mobile device is
redirected to m.uefa.com
7. Property Change Events
• Raised when individual
attributes of a model object
are changed
• Use setter method to update
attributes
8. Provider Change Event
• Raised when attributes of type Collection are changed on a model object
• When a new row is created
– fireProviderCreate(providerKey, rowKey, newRow)
– New row is inserted in the UI without refreshing other parts of the page
• When a row is deleted
– fireProviderDelete(providerKey, rowKey) – Row is deleted in the Iterator
• When the collection is refreshed
– fireProviderRefresh(providerKey)
– Iterator is refreshed
– Row currency is lost.
providerChangeSupport.fireProviderRefresh("stadiums");!
9. Device Interaction
• The Device Datacontrol
• Drag n Drop support
• Attributes as fields
• Operations as buttons
10. Camera interaction
• Take a picture ……………
import oracle.adf.model.datacontrols.device;
DeviceManagerFactory.getDeviceManager().getPicture(
100,
DeviceManager.CAMERA_DESTINATIONTYPE_FILE_URI,
DeviceManager.CAMERA_SOURCETYPE_CAMERA, false,
DeviceManager.CAMERA_ENCODINGTYPE_PNG,
200,
200);
• …… or get one from the Library
DeviceManager.CAMERA_SOURCETYPE__PHOTOLIBRARY
11. Be careful !!
• DESTINATIONTYPE_DATA_URL you will get the image as base64 encoded
string
• Camera’s are very good and picture quality is amazing.
– Encoding such images as base64 causes memory issues
• Don’t blow up your app.
– iOS you should set quality parameter to a value less then 50 to avoid memory
issues
– On Android out-of-memory can be caused with default image settings. Make
image smaller by setting targetWidth and targetHeight
• Small sized images can be uploaded using web services.
12. Data Services
Device Native Container
ADF Controller
Local
HTML
Server
HTML
Push
Handler
Java VM
ADF Model
Device
Services
JDBC
Cordova
Business
Logic
Configuration Server
Credential Management,
SSO & Access Control
ADF Mobile
AMX View
Web
View
App
Config
HTML5 & JavaScript Presentation
SQLite
Encrypted
SQLite DB
Mobile
Device
Server-Generated HTML
APN/GCM Push Services
SOAP & REST Services
Server
15. Using Webservices from Java
• Invoke directly from java.
• Does not use the binding layer
• Uses Framework utilityMethod
• AdfmfJavaUtilities.invokeDataControlMethod()
• Datacontrol must be in available in DataBindings.cpx
16. Advantages
•
•
•
•
•
Provides more flexibility to shape model to mobile UI
Perform client side validation
Minimize the number of round trips
Offline caching
Mash-up data from multiple services
20. Obviously all the same……
• Service Object Data Control Pattern
– Whatever “back end” data source you use…..
– It is completely transparent for UI
21. Feature Archives
• Feature Archives can be reused
• Deploy ADF Mobile app as FAR
• Consume features from FAR in other apps
22. Feature Archives
• Feature Archives Deployment Profile
• Connections Detail should be used (default is wrong ?)
• Only if connection is available in consuming APP name only works
29. Navigation
• Drawback
– No access to setPropertyListener
• Solution if you need that functionality:
– Set the value in java Code
ValueExpression ve =!
AdfmfJavaUtilities.getValueExpression(!
"#{pageFlowScope.myBean.currentStadium}”!
, String.class);!
ve.setValue(AdfmfJavaUtilities.getAdfELContext()!
, getCurrentStadium());!
30. Smart Navigation
• Search Stadiums
• What if resultset only contains one row ?
if (s_stadiums.size()==1){!
// only one stadium! Lets navigate
AdfmfContainerUtilities.invokeContainerJavaScriptFunction(!
AdfmfJavaUtilities.getFeatureName(), !
"adf.mf.api.amx.doNavigation", !
new Object[] { "detail" });!
} !
31. Preserve Current Row
• Inside the <amx:listItem> element of the list page, you need to add a
<amx:setPropertyListener> element to store the row key in a
pageFlowScope variable.
• In the page definition of the detail page, you need to add a
setCurrentRowWithKey action, which uses the pageFlowScope variable
to set the current row.
• In the page definition of the detail page, you need to add an
invokeAction executable for the setCurrentRowWithKey action to ensure
the current row is automatically set when entering the detail page.
32. Preserve Current Row (A-Team)
• Easiest way:
– Download and install extension
– adf-mobile-persistence-sample-install.zip
– Extension contains StatefulIteratorBeanDcDefinition
<AdapterDataControl id="PlayerService”
FactoryClass="oracle.adf.model.adapter.DataControlFactoryImpl”
ImplDef=
"oracle.ateam.sample.mobile.model.bean.StatefulIteratorBeanDcDefinition”!
……….
Definition=
"com.blogspot.lucbors.soccer.mobile.model.service.PlayerService”
BeanClass=
"com.blogspot.lucbors.soccer.mobile.model.service.PlayerService"!
} !
• NOTE: This will be the way ADF Mobile will do it in future versions
33. Gesture Support
• You can configure Button, Link, and List Item components to react to the
following gestures:
•
•
•
•
•
Swipe to the right
Swipe to the left
Swipe up
Swipe down
Tap-and-hold
34. Gesture examples
• The Swipe Gesture
<amx:actionListener binding="#{mybean.DoX}"
type="swipeRight"/>
• The Tap Gesture
<amx:showPopupBehavior popupid="pop1"
type="tapHold“ />
36. Ingredients
• A (Web) service and datacontrol
• A Page with Listview
• An ActionListener with type
SwipeDown
• Smart Java Code to call service
conditionally
<amx:listView var="row”!
value="#{bindings.allLocations.collectionModel}" !
fetchSize="#{bindings.allLocations.rangeSize}”!
id="lv1"> !
<amx:listItem id="li1"> !
<amx:actionListener type="swipeDown”
!
binding="#{pageFlowScope.locationsBackingBean.checkForUpdates}”>!
!
40. Swimming-lanes
• No Horizontal scrollbar
• All ‘data’ available
• Use panelGroupLayout
– Width 100%
<amx:panelGroupLayout layout="horizontal”
inlineStyle="width:100%;">!
41. Push Notification (GCM)
•
•
•
•
Subscribe to GCM
Receive token
Register with Enterprise app
Enterprise app Pushes message to
GCM
• GCM delegates message to
device(s)
42. Server Side
• Class to push a message to a device.
–
–
–
–
import com.google.android.gcm.server.Constants;
import com.google.android.gcm.server.Message;
import com.google.android.gcm.server.Result;
import com.google.android.gcm.server.Sender;
public class PushMessageBean {
public String message;
private Sender sender = new Sender(”<someSenderKey>");
public static final String ERROR_NOT_REGISTERED="NotRegistered”;
private Message createMessage(String msg) {
String sound = "default";
Message message = new Message.Builder().collapseKey("1")
.delayWhileIdle(true)
.addData("alert", msg)
.addData("sound", sound).build();
return message;}
43. Server Side Send Code
public void pushNow(ActionEvent actionEvent) {
// Add event code here…
DCBindingContainer bindings = (DCBindingContainer)
BindingContext.getCurrent().getCurrentBindingsEntry();
DCIteratorBinding iter =
bindings.findIteratorBinding("GcmSubscribersIterator");
Row curr = iter.getCurrentRow();
String target = (String)curr.getAttribute("DeviceToken");
String type = (String)curr.getAttribute("DeviceType");
if(type.equalsIgnoreCase("Android")){
Message message = createMessage(this.message);
Result result = null;
sendSingleMessage(target, message);
}