Real Life ADF Mobile 10 things that you don't get from the developer guide DOAG Konferenz; November 19th 2013
Who Am I Luc Bors Principal Consultant AMIS, Netherlands Friends of Oracle & Java 5 Oracle ACE(D) Oracle Partner
10 Things One App... 10 Things... In 45 Minutes..Really?
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
Feature as Remote URL Create New Feature as Remote URL Create URL Connection
Whitelisting Why do we need to do this? Mobile device is redirected to m.uefa.com
Property Change Events Raised when individual attributes of a model object are changed Use setter method to update attributes
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");!
Device Interaction The Device Datacontrol Drag n Drop support Attributes as fields Operations as buttons
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
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.
Data Services Device Native Container HTML5 & JavaScript Presentation ADF Mobile AMX View ADF Controller Cordova Local HTML Java VM Business Logic ADF Model Web View Server HTML JDBC SQLite App Config Push Handler Credential Management, SSO & Access Control Configuration Server Server-Generated HTML APN/GCM Push Services SOAP & REST Services Device Services Encrypted SQLite DB Mobile Device Server
The Webservice Datacontrol Using Webservices
Using Webservices directly Just drag & drop the method from the Data Control
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
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
SOAP Webservice Web Service Data Control Patterns
REST Webservice Rest Service Adapter Patterns
SQLite Database Plain JDBC Patterns.
Obviously all the same Service Object Data Control Pattern Whatever back end data source you use.. It is completely transparent for UI
Feature Archives Feature Archives can be reused Deploy ADF Mobile app as FAR Consume features from FAR in other apps
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
Springboard configuration in adfmf-application.xml Springboard & navigationbar
The Default Springboard
The Custom SpringBoard
Configuring the springboard Do Not set AllowDeviceAccess to False for Springboard Feature!
Navigation Declarative Navigation Button/Link/ListItem <amx:listitem id="li1" action="detail" showlinkicon="true">! <amx:setpropertylistener id="x" from="#{row.rowkey} to="#{pageflowscope.mybean.currentstadium}"! type="action"/>!
Navigation Declarative Navigation Button/Link/ListItem Programmatic Navigation JavaCode AdfmfContainerUtilities.invokeContainerJavaScriptFunction(! AdfmfJavaUtilities.getFeatureName(),! "adf.mf.api.amx.donavigation",! new Object[] { detail" }); }!
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());!
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" });! }!
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.
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
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
Gesture examples The Swipe Gesture <amx:actionlistener binding="#{mybean.dox}" type="swiperight"/> The Tap Gesture <amx:showpopupbehavior popupid="pop1" type="taphold />
Use case example
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} >!!
Change the data
Swimming-lanes No Horizontal scrollbar All data available Use panelgrouplayout Width 100% <amx:panelgrouplayout layout="horizontal inlinestyle="width:100%;">!
Push Notification (GCM) Subscribe to GCM Receive token Register with Enterprise app Enterprise app Pushes message to GCM GCM delegates message to device(s)
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;}
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); }
Example Select device Send message Get notified +
GCM Demo
Summary One App... 10 Things... In 45 Minutes.. Really! 1. Whitelisting 2. Provider Refresh 3. Pictures 4. Data Service Pattern 5. Feature Archives 6. Springboard 7. Prog Navigation 8. Keep current Row 9. Pull to Refresh 10. Push Notifications
User Experience Patterns and Guidelines WIKI
Luc Bors, AMIS, The Netherlands Luc.Bors@amis.nl LucBors@gmail.com Follow me on : @lucb_