Wednesday Jul 01, 2015

From the Cloud Launch - Focusing on Mobile, the Other Side of the Proverbial Coin

No doubt you tuned into the big Oracle Cloud Platform as a Service (PaaS) launch last week. Lots of new and exciting cloud based offerings including:

  • Oracle Database Cloud - Exadata Service
  • Oracle Archive Storage Cloud Service
  • Oracle Big Data Cloud Service and Big Data SQL Cloud Service
  • Oracle Integration Cloud Service
  • Oracle Process Cloud Service

And last but certainly not least Oracle Mobile Cloud Service! The Cloud is touching every facet of IT infrastructure - and the other side of the coin, that's Mobile, which is changing the interfaces to all those systems.  Mobile is the new first screen, overtaking television and growing. The web browser reached hundreds of millions of people, but yet somehow, Mobile has taken it 10X, a magnitude greater than before, reaching billions of people. It's predicted that by 2020, 80% of the world, will have a smartphone.


Get a quick intro into Oracle Mobile Cloud Service in the following easy on the eyes, mind sharpening assets:

  1. Watch the overview video here (< 3min), it's the main video on the page
  2. Watch the subsequent videos (< 1 min each) in the Learn More tab
  3. Read the eBook, the big eBook button on the page ;-)

Then get down to business in strengthening your mobile strategy by simplifying mobility.

Follow @OracleMobile 

Wednesday Jun 24, 2015

Introducing Oracle Mobile Cloud Service

Oracle has released the much anticipated Mobile Cloud Service (or MCS for short). Unlike other cloud offerings, MCS is focused not only on providing enterprise quality Mobile Backend as a Service (MBaaS), but also providing easy to use tools for business professionals to analyze and mine data about the usage of the MBaaS.[Read More]

Tuesday Jun 23, 2015

Free Oracle Mobile Cloud Service Training - Your Oracle Mobile Learning Advantage

Oracle Product Management is happy to announce along side the announcement of Oracle Mobile Cloud Service yesterday, that we’re delivering free online training to get you up and running with MCS today.

The training is available on YouTube via the Oracle Mobile Platform channel along side our existing Oracle Mobile Application Framework training.  Initially two MCS playlists are available providing first an overview of MCS, and a deep dive into the MCS concept of Mobile Backends.  Thereafter we will be delivering 2 additional videos a week to keep you ticking over in learning the core MCS concepts.

The complete topics areas that will be covered:
  • MCS Overview
  • Mobile Backends
  • Platform API Overview & the MCS Mobile Client SDK
  • User Management Services API
  • Notifications API
  • Storage API
  • Data Offline API
  • Analytics API
  • Connectors
  • Custom APIs & node.js
  • Database API
  • Diagnostics & Logging
  • Lifecycle

This training takes a departure from our content of old in that it has been built from the ground up to be watched on YouTube.  Where our previous online training was adapted from face to face classes with 60min+ long discussions which stretches even the most patient attention span online, the Oracle Mobile Platform MCS episodes are built around short sharp lessons of no more than 10-15 minutes.  This makes them easily digestible at work, on the bus home or just when you have a spare couple of minutes.  

Overall 1 day after MCS is officially announced, Oracle is not only delivering a leading edge cloud solution to solve your mobile development needs, we’re delivering everything you and your team need to start learning MCS as fast as possible right now.

All available day 0 of the official product release. That’s your Oracle learning advantage.

Tuesday Apr 28, 2015

Oracle MAF 2.1.2 Released

Our mobile development team has been busy continuously improving the framework we provide you. Last month we released Oracle MAF 2.1.1 and this month we are releasing Oracle MAF 2.1.2. This is part of Oracle's commitment to a much faster release cycle for our mobile customers.

This new version is a minor release with no major new features, but it contain several fixes for issues you might have ran into, so check out our 2.1.2 release notes

As always, you should get the extension using the help->check for updates option in JDeveloper. 

Wednesday Apr 22, 2015

New Oracle University MAF Virtual Course : April 29 - May 1st

Looking for a way to skill-up on mobile development? Attend the Oracle Mobile Application Framework: Develop Mobile Apps course (now updated for MAF 2.1) where you'll learn how to develop real-world mobile applications that run in HTML5 on iOS or Android, access data from a variety of data sources including REST, and ensure your mobile application is secure, performant, and engaging.

Sign up today to take the course from the comfort of your own desk and spend April 29-May 1 learning how to build your organization's next great application without having to code for multiple platforms or form factors.

Follow this link to get more info


Tuesday Apr 07, 2015

How to implement iBeacon in MAF 2.1.1

Want to alert your customer to a special loyalty discount offer when they enter the menswear department in one of your retail stores?  Want to present information about a famous artist’s life when a visitor to your museum nears one of the artist’s paintings?  These are just two of the many real-world scenarios made possible by the use of iBeacon technology.

This blog post provides an introduction to iBeacon technology and a description of how to build two different MAF apps – one that pretends to be an iBeacon and another that detects iBeacons and uses the local notifications functionality provided in MAF 2.1.1 to inform the user, even when the app isn’t even running.

To deploy these apps to iOS devices you will need an iOS developer account.

What is a beacon?

A beacon is a device that is intentionally conspicuous to draw attention to a location, such as a lighthouse sitting on the edge of a cliff. 

In in the Internet of Things (or IoT), a beacon is a small electronic device that transmits a regular radio signal according to the Bluetooth v4 Low Energy spec (otherwise known as “BLE”).  A beacon typically does no more than advertise its existence by transmitting a unique identifier and can last for months on a single cell battery.

Any BLE-enabled device, such as a modern smartphone, can detect a beacon by listening for BLE-based transmissions.

Whilst the possibilities appear endless, typical applications for beacons currently include retail stores, exhibition halls, museums, places of employment and homes, where users can be alerted to information pertaining to their current location within a building.

What is (an) iBeacon?

iBeacon is a technology introduced by Apple in iOS 7 that defines a standard for how a beacon identifies itself (or “advertises”) in its BLE transmissions.  Any beacon that implements this standard can be called an iBeacon.

Most beacon manufacturers implement the iBeacon standard by default, whilst some can also be configured to use their own proprietary protocol.  It’s also possible to configure a post-2012 iOS device, or Mac running OS X Mavericks (not Yosemite), to act as an iBeacon.

Whilst the iBeacon technology is included in the iOS Core Location framework since iOS 7, any BLE-enabled device can detect iBeacons and various libraries exist for use on devices running Android 4.3 or above.

How does iBeacon work?

The iBeacon standard defines three properties that determine a beacon’s identity:

  • A proximity UUID (universally unique identifier), which is a 128-bit value that uniquely identifies one or more beacons as a certain type or from a certain organization.
  • A major value, which is a 16-bit unsigned integer that can be used to group related beacons that have the same proximity UUID.
  • A minor value, which is a 16-bit unsigned integer that differentiates beacons with the same proximity UUID and major value.

Every iBeacon must advertise a proximity UUID, whilst the advertisement of major and minor values is optional.  All beacon manufacturers allow customers to modify these values on their purchased beacons.

A typical iBeacon deployment (e.g. within a retail store chain) would see all beacons advertising the same proximity UUID, those in a particular location (e.g. a single store) advertising the same major value, and the minor values being used to uniquely identify each beacon.

When using iOS Location Services, an app wishing to detect iBeacons must start by monitoring for an iBeacon region.  A region is defined by the proximity UUID and optionally major and minor values, and can therefore represent one or more beacons.  Consider an app for a retail store chain that monitors for a region defined only by the proximity UUID.  This app will be notified when any of the retain chain’s beacons are detected.  Alternatively, if the region being monitored is defined by proximity UUID and major value, the app might only be notified when a beacon from a particular store (represented by the major value) is detected.

Once an app has been launched and the user has given permission for the app to monitor for beacons, the app will be notified when the device enters a beacon region that is being monitored, even if the app is not running and even if the device has been restarted.  If the app is not running, iOS launches the app for a short period (around 10 seconds), allowing the app to receive and react to the event.  Typically, the app fires an immediate local notification to notify the user.

When a user’s device enters a beacon region, the app can start ranging for individual beacons within the region to determine its relative proximity to each beacon.  This is used to determine when the user is in the immediate proximity of a particular beacon, so that information related to that beacon can be displayed to the user.  The relative proximity is an approximation that can be affected by physical objects including walls, water and the human body.  Most beacon manufacturers allow customers to modify the transmission power and advertising interval on their purchased beacons, so as to fine-tune the distance at which the relative proximity is considered ‘immediate’.

When iOS Location Services determines that it can no longer detect any beacons in the region, it notifies the app that the beacon region has been exited.  In practice, I’ve found that this takes around 30 seconds, but some bloggers have reported much longer times.

Libraries exist for Android that provide similar functionality to iOS Location Services, but I have not explored these (yet).

How to create an iBeacon client app

If you are an existing MAF app developer, you can follow these steps to create a MAF app that detects iBeacons.

Step 1 – Find a suitable Cordova plugin

The first thing required is a Cordova plugin to provide access to the native iBeacon functionality on iOS and mimic the same on Android.  Typing “beacon” into the Cordova plugin repository produces a few results, with the most downloaded being this plugin.  According to the readme, this plugin is suitable for both iOS and Android apps and provides the ability to fake an iBeacon advertisement on iOS devices.

Open its GitHub page and download it by clicking on the Download ZIP button.

Step 2 – Create a MAF app and add the plugin

Open JDeveloper with MAF 2.1.1 installed, create a new MAF app called BeaconDemo.

Before adding the plugin to your app, you must extract the contents of the downloaded zip file into a location that is on the same drive as your MAF app and into a folder whose path contains no spaces.  I like to extract the plugin into the source tree of the app that's using it, for example within the top-level src folder of the app. 

Open the maf-application.xml file located in Application Resources > Descriptors > ADF META-INF and select the Plugins tab.  Click on the Add Plugin button under Additional Plugins, browse to the location of the extracted plugin and click on Select.  The plugin is now added to your app.

Save all your changes.

Step 3 – Create a JavaScript interface file

The Cordova plugin interface is written in JavaScript, so we need to write some JavaScript that calls this interface.  We will also include some of the business logic in JavaScript.

Create a new JavaScript file by selecting the ViewController node in the Projects window, right click, select New > JavaScript File and enter "beacon.js" as the filename.  Enter the following code into the empty JavaScript file:

(function() {

  // Our region identifier
  var uuid = "0AC59CA4-DFA6-442C-8C65-22247851344C";
  var beaconRegion;

  function initialise() {
    beaconRegion = createRegion();
    var delegate = new cordova.plugins.locationManager.Delegate();

    // Callback for enter/exit region whilst monitoring
    delegate.didDetermineStateForRegion = function (pluginResult) {
      var options;
      if (pluginResult.state == "CLRegionStateInside") {
        // Fire local notification and start ranging from there so that ranging only
        // starts if the app is in the foreground, or the user taps on the notification
        options = {"alert":"Welcome! Thank you for entering the " +
                     pluginResult.region.identifier + " region.",
                   "sound":"SYSTEM_DEFAULT",
                   "vibration":"SYSTEM_DEFAULT",
                   "payload":{"id":pluginResult.region.identifier, "inside":true}};
        adf.mf.api.localnotification.add(options,  function() {}, function() {});
      } else if (pluginResult.state == "CLRegionStateOutside") {
        // Stop ranging immediately
        stopRanging();

        // Fire local notification
        options = {"alert":"Goodbye. Thank you for visiting the " +
                     pluginResult.region.identifier + " region.",
                   "sound":"SYSTEM_DEFAULT",
                   "vibration":"SYSTEM_DEFAULT",
                   "payload":{"id":pluginResult.region.identifier, "inside":false}};
        adf.mf.api.localnotification.add(options,  function() {}, function() {});
      }
    };

    // Callback for ranging region
    delegate.didRangeBeaconsInRegion = function(pluginResult) {
      var beacons = pluginResult.beacons;
      for (var i = 0; i < beacons.length; i++) {
        // Add to list of beacons in BeaconManager
        adf.mf.api.invokeMethod("mobile.BeaconManager", "beaconRanged",
                                beacons[i].uuid, beacons[i].major,
                                beacons[i].minor, beacons[i].proximity,
                                function() {}, function() {});
      }
    };

    // Set delegate
    cordova.plugins.locationManager.setDelegate(delegate);

    // Required in iOS 8+
    cordova.plugins.locationManager.requestAlwaysAuthorization();

    // Start monitoring by default each time the app is launched
    startMonitoring();
  }

  function createRegion() {
    // Monitor any beacons in this UUID-based region
    return new cordova.plugins.locationManager.BeaconRegion("FakeBeacon", uuid);
  }

  // Callable externally
  startMonitoring = function() {
    cordova.plugins.locationManager.startMonitoringForRegion(
      beaconRegion).fail(console.error).done();
    adf.mf.api.setValue({"name": "#{applicationScope.region}",
                         "value": beaconRegion.uuid},
                        function() {}, function() {});
    adf.mf.api.setValue({"name": "#{applicationScope.monitoring}", "value": true},
                        function() {}, function() {});
  }

  // Callable externally
  stopMonitoring = function() {
    stopRanging(); // Must stop ranging first
    cordova.plugins.locationManager.stopMonitoringForRegion(
      beaconRegion).fail(console.error).done();
    adf.mf.api.setValue({"name": "#{applicationScope.monitoring}", "value": false},
                        function() {}, function() {});
  }

  // Callable externally
  startRanging = function() {
    cordova.plugins.locationManager.startRangingBeaconsInRegion(
      beaconRegion).fail(console.error).done();
    adf.mf.api.setValue({"name": "#{applicationScope.ranging}", "value": true},
                        function() {}, function() {});
  }

  // Callable externally
  stopRanging = function() {
    cordova.plugins.locationManager.stopRangingBeaconsInRegion(
      beaconRegion).fail(console.error).done();
    adf.mf.api.setValue({"name": "#{applicationScope.ranging}", "value": false},
                        function() {}, function() {});
    // Clear list of beacons
    adf.mf.api.invokeMethod("mobile.BeaconManager", "clearBeacons",
                            function() {}, function() {});
  }

  document.addEventListener("showpagecomplete", initialise, false);

}) ();

Let’s take a look at what this code does:

  • There are four externally callable functions – startMonitoring, stopMonitoring, startRanging and stopRanging, each of which invokes the corresponding plugin API.
  • There are two internal functions – initialize, which is invoked when the framework loads the feature, and createRegion, which creates a new beacon region based on the uuid variable defined at the top of the file. You should enter your iBeacons’ UUID here, or generate your own.  The beacon region identifier “FakeBeacon” can be any string you wish.
  • The initialize function creates and registers two callback functions via a delegate, requests authorization from the user to monitor for beacons at all times, and starts monitoring the defined beacon region.
  • The delegate.didDetermineStateForRegion callback function is invoked when the beacon region is entered or exited, even if the app was not running.  When this happens, it schedules an immediate local notification. To avoid unnecessarily ranging when the user is not actively using this app, ranging is not started here, but within the local notification handler.  For the same reason, ranging is stopped as soon as the device exits the beacon region.
  • The delegate.didRangeBeaconsInRegion callback is invoked when ranging has detected one or more iBeacons.  For each identified beacon, proximity, RSSI and accuracy values will be returned.  We will manage the list of beacons in a Java bean, so the bean’s beaconRanged method is invoked here.
  • Three application-scope variables are used to maintain the monitoring state, ranging state and the identity of the region being monitored or ranged.
  • The stopRanging function also clears the list of beacons by calling the clearBeacons method of the Java bean.

Save all your changes.

Step 4 – Create a local notification handler

There are a couple of steps required to ensure our app can receive local notifications.

Open the LifecycleListenerImpl.java file located in Projects > ApplicationController > application and modify the start method as follows to register an EventListener for local notifications:

public void start() {
  EventSource evtSource = EventSourceFactory.getEventSource(
    EventSourceFactory.NATIVE_LOCAL_NOTIFICATION_EVENT_SOURCE_NAME);
  evtSource.addListener(
new NativeLocalNotificationListener());
}

Create a new Java file by selecting the ApplicationController node in the Projects window, right click, select New > Java Class and enter "NativeLocalNotificationListener" as the name.  Enter the following code into the empty Java file:

package application;

import java.util.HashMap;
import oracle.adfmf.framework.api.AdfmfContainerUtilities;
import oracle.adfmf.framework.event.Event;
import oracle.adfmf.framework.event.EventListener;
import oracle.adfmf.framework.event.NativeLocalNotificationEvent;
import oracle.adfmf.framework.exception.AdfException;
import oracle.adfmf.json.JSONException;
import oracle.adfmf.json.JSONObject;

public class NativeLocalNotificationListener implements EventListener {

  public NativeLocalNotificationListener() {
  }

  public
void onMessage(Event event) {
    String payload = event.getPayload();

    if (!"{}".equals(payload)) {
      try {
        HashMap<String, Object> payloadMap =
          ((NativeLocalNotificationEvent)event).getPayloadObject();
        JSONObject jsonPayload = (JSONObject)payloadMap.get(
"payload");
        boolean inside = jsonPayload.getBoolean("inside");

        // If inside, start ranging (If outside, ranging already has been stopped)
        if (inside) {
          AdfmfContainerUtilities.invokeContainerJavaScriptFunction(
"Beacon",
                                                                    "startRanging",
                                                                    new Object[] {});
        }
      } 
catch (JSONException e) {
        System.out.println(
"Local Notification JSON error: " + e.getMessage());
      }
    }
  }

  public void onError(AdfException adfException) {
    System.out.println(
"Local Notification error: " + adfException.getMessage());
  }

  public void onOpen(String token) {
    System.out.println(
"Local Notification opened: " + token);
  }
}

Let’s take a look at what this code does:

  • The local notification will be fired by the JavaScript code when the device enters or exits a beacon region, no matter whether the app was running in the foreground or not.
  • The onMessage method will be invoked immediately if the app is running in the foreground, otherwise it will only be invoked when the user does one of the following:
    • Taps on the notification banner (iOS only)
    • Taps on the “View” button of the notification alert  (iOS only)
    • Taps on the notification in the Notification Center (iOS) or Notification Drawer (Android)
  • The notification triggered by the JavaScript code includes a custom JSON payload that holds a Boolean value for the attribute inside, which defines whether the device is inside or outside the beacon region.
  • If the device is inside the beacon region, the Javascript startRanging function is invoked to start ranging for beacons in this region.

Save all your changes.

Step 5 – Create a bean Data Control

We will model the list of detected beacons in two Java classes (beans) and provide access to certain methods and properties via a Data Control.

Create a new Java file by selecting the ViewController node in the Projects window, right click, select New > Java Class and enter "Beacon" as the name.  Enter the following code into the empty Java file:

package mobile;

import oracle.adfmf.java.beans.PropertyChangeListener;
import oracle.adfmf.java.beans.PropertyChangeSupport;

public class Beacon {

  private String uuid;
  private int major;
  private int minor;
  private String proximity;

  private PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);

  public Beacon(String uuid, int major, int minor, String proximity) {
    this.uuid = uuid;
    this.major = major;
    this.minor = minor;
    this.proximity = proximity;
  }

  public void setUuid(String uuid) {
    String oldUuid = 
this.uuid;
    this.uuid = uuid;
    propertyChangeSupport.firePropertyChange(
"uuid", oldUuid, uuid);
  }

  public String getUuid() {
    return uuid;
  }

  public void setMajor(int major) {
    int oldMajor = this.major;
    this.major = major;
    propertyChangeSupport.firePropertyChange(
"major", oldMajor, major);
  }

  public int getMajor() {
    return major;
  }

  public void setMinor(int minor) {
    int oldMinor = 
this.minor;
    this.minor = minor;
    propertyChangeSupport.firePropertyChange(
"minor", oldMinor, minor);
  }

  public int getMinor() {
    return minor;
  }

  public void setProximity(String proximity) {
    String oldProximity = 
this.proximity;
    this.proximity = proximity;
    propertyChangeSupport.firePropertyChange(
"proximity", oldProximity, proximity);
  }

  public String getProximity() {
    return proximity;
  }

  public String getIdentifier() {
    return 
"Major: " + getMajor() + " Minor: " + getMinor();
  }

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

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

Let’s take a look at what this code does:

  • Beacon has five properties – uuid, major, minor, proximity and identifier, with associated getter and setter methods.
  • Consuming classes can add and remove PropertyChangeListeners, as required, to be notified of changes to these properties.

Create a new Java file by selecting the ViewController node in the Projects window, right click, select New > Java Class and enter "BeaconManager" as the name.  Enter the following code into the empty Java file:

package mobile;

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

import oracle.adfmf.amx.event.ActionEvent;
import oracle.adfmf.framework.api.AdfmfContainerUtilities;
import oracle.adfmf.framework.api.AdfmfJavaUtilities;
import oracle.adfmf.java.beans.ProviderChangeListener;
import oracle.adfmf.java.beans.ProviderChangeSupport;

public class BeaconManager {

  private static List<Beacon> beacons = null;
  protected transient static ProviderChangeSupport providerChangeSupport;

  public BeaconManager() {
    if (beacons == null) {
      beacons = new ArrayList<Beacon>();
      providerChangeSupport = new ProviderChangeSupport(this);
    }
  }

  public void startMonitoring(ActionEvent actionEvent) {
    AdfmfContainerUtilities.invokeContainerJavaScriptFunction(
      AdfmfJavaUtilities.getFeatureId(), "startMonitoring", new Object[] {});
  }

  public void stopMonitoring(ActionEvent actionEvent) {
    AdfmfContainerUtilities.invokeContainerJavaScriptFunction(
      AdfmfJavaUtilities.getFeatureId(), "stopMonitoring", new Object[] {});
  }

  public List<Beacon> getBeacons() {
    return beacons;
  }

  public static void beaconRanged(String uuid, int major, int minor, String proximity) {
    for (Beacon beacon : beacons) {
      if (beacon.getMajor() == major && beacon.getMinor() == minor) {
        // Existing beacon, update proximity
        beacon.setProximity(proximity);
        return;
      }
    }

    // No existing beacon found, add new one
    beacons.add(new Beacon(uuid, major, minor, proximity));
    providerChangeSupport.fireProviderRefresh("beacons");
  }

  public static void clearBeacons() {
    beacons.clear();
    providerChangeSupport.fireProviderRefresh("beacons");
  }

  public void addProviderChangeListener(ProviderChangeListener l) {
    providerChangeSupport.addProviderChangeListener(l);
  }

  public void removeProviderChangeListener(ProviderChangeListener l) {
    providerChangeSupport.removeProviderChangeListener(l);
  }
}

Let’s take a look at what this code does:

  • The BeaconManager contains a list of Beacon objects, which can be accessed via a getter method.
  • Four methods are provided for managing the beacons:
    • startMonitoring calls the JavaScript startMonitoring function
    • stopMonitoring calls the JavaScript stopMonitoring function
    • beaconRanged is called by JavaScript to inform the BeaconManager that a beacon has been detected.  If newly found, it is added to the list, otherwise the proximity property is updated.
    • clearBeacons is called by JavaScript to clear the list of beacons
  • Consuming classes can add and remove ProviderChangeListeners, as required, to be notified of changes to the list.
  • The list of beacons and the methods accessing the list are static because each time these methods are invoked from JavaScript, a new BeaconManager object is instantiated.

Save all your changes.

To create the Data Control, select the BeaconManager.java file, which will be located in Projects > ViewController > Application Sources > mobile, right click, select Create Data Control and accept the defaults.  The newly created BeaconManager Data Control will appear in the Data Controls window (you may need to Refresh it).

Save all your changes.

Step 6 – Create the AMX feature

The final step prior to deployment is to create the AMX feature.

Return to the maf-feature.xml file located in Projects > ViewController > Application Sources > META-INF and click on the Insert Feature button to add a feature called Beacon.

Open the Content tab and click on the Insert Include button to include the JavaScript file we created earlier by entering resources/js/beacon.js as the file location.

Click on the Create button in the Content tab to create an AMX file called beacon.amx.  Using the BeaconManager Data Control found in the Data Controls window and the AMX components found in the Components palette, create an AMX page similar to the following:

<?xml version="1.0" encoding="UTF-8" ?>
<amx:view xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xmlns:amx="http://xmlns.oracle.com/adf/mf/amx"
          xmlns:dvtm="http://xmlns.oracle.com/adf/mf/amx/dvt">
  <amx:panelPage id="pp1">
    <amx:facet name="header">
      <amx:outputText value="Beacon Demo" id="ot1"/>
    </amx:facet>
    <amx:facet name="secondary">
      <amx:commandButton actionListener="#{bindings.startMonitoring.execute}"
                         text="Start"
                         disabled="#{!bindings.startMonitoring.enabled}" id="cb1"
                         rendered="#{applicationScope.monitoring != true}"/>
      <amx:commandButton actionListener="#{bindings.stopMonitoring.execute}" text="Stop"
                         disabled="#{!bindings.stopMonitoring.enabled}" id="cb2"
                         rendered="#{applicationScope.monitoring == true}"/>
    </amx:facet>
    <amx:outputText value="Not monitoring a region." id="ot2"
                    rendered="#{applicationScope.monitoring != true}"/>
    <amx:outputText value="Monitoring region: #{applicationScope.region}." id="ot3"
                    rendered="#{applicationScope.monitoring == true &amp;&amp;
                                applicationScope.ranging != true}"/>
    <amx:outputText value="Ranging region: #{applicationScope.region}." id="ot4"
                    rendered="#{applicationScope.ranging == true}"/>
    <amx:outputText value="Beacons located:" id="ot5"
                    rendered="#{applicationScope.ranging == true}"/>
    <amx:listView var="row" value="#{bindings.beacons.collectionModel}"
                  fetchSize="#{bindings.beacons.rangeSize}"
                  styleClass="adfmf-listView-insetList"
                  showMoreStrategy="autoScroll" bufferStrategy="viewport"
                  id="lv1" rendered="#{applicationScope.ranging == true}">
      <amx:listItem showLinkIcon="false" id="li1">
        <amx:tableLayout width="100%" id="tl1">
          <amx:rowLayout id="rl2">
            <amx:cellFormat width="10px" rowSpan="2" id="cf2"/>
            <amx:cellFormat width="100%" id="cf3"
                            height="#{deviceScope.device.os=='Android'?'36':'32'}px">
              <amx:outputText value="#{row.identifier}" id="ot7"/>
            </amx:cellFormat>
          </amx:rowLayout>
          <amx:rowLayout id="rl1">
            <amx:cellFormat width="100%" id="cf1"
                            height="#{deviceScope.device.os=='Android'?'22':'19'}px">
              <amx:outputText value="#{row.proximity}"
                              styleClass="adfmf-listItem-captionText" id="ot6"/>
            </amx:cellFormat>
          </amx:rowLayout>
        </amx:tableLayout>
      </amx:listItem>
    </amx:listView>
  </amx:panelPage>
</amx:view>

Let’s take a look at what this code does:

  • Start and Stop buttons enable you to start or stop monitoring for the beacon region, depending on whether monitoring is active, by calling the startMonitoring and stopMonitoring methods on the BeaconManager Data Control.
  • The page displays the current state of monitoring or ranging the beacon region, using the application-scope variables that are updated by the JavaScript code.
  • Whilst ranging, the page displays the list of detected beacons managed by the BeaconManager Data Control, along with their estimated proximity.

Save all your changes.

Step 7 – Deploy the app

The app must be deployed to a BLE-enabled device.  I have verified it on various iOS devices, but have not yet verified it on an Android device.

In order to deploy to an iOS device, you must configure a suitable provisioning profile and signing identity in JDeveloper  > Preferences > Mobile Application Framework > iOS Platform.

You must also configure an appropriate bundle ID in the maf-application.xml file.  Return to this file, which is located in Application Resources > Descriptors > ADF META-INF, select the Application tab and enter an Id that matches your provisioning profile.

Deselect Show Navigation Bar on Application Launch, save all your changes and deploy the app to a BLE-enabled device.

Before launching the app, ensure you have Bluetooth enabled, as well as Location Services if running on iOS.  When you launch the app on iOS, you will be prompted to allow the app to access your location even when you are not using the app, which you should allow.

The app will start monitoring for the defined region immediately.  If you have access to beacons (or a fake beacon) with the same UUID that you have configured in the beacon.js file, you will be notified when you enter a region containing those beacons.  If the app is running in the foreground when this occurs, ranging will commence and you will see a screen similar to the following:


How to create a fake iBeacon app

If you don’t have any beacons lying around for testing, you can create a MAF app that pretends to be an iBeacon by following these steps.

Step 1 – Find a suitable Cordova plugin

Already done!  Just reuse the plugin you have already downloaded.

Step 2 – Create a MAF app and add the plugin

Open JDeveloper with MAF 2.1.1 installed, create a new MAF app called FakeBeacon and add the plugin as you did for the BeaconDemo app.  Save all your changes.

Step 3 – Create a JavaScript interface file

Create a new JavaScript file called beacon.js as you did for the BeaconDemo app and enter the following code into the empty JavaScript file and save your changes:

(function() {

  // Our region identifiers
  var uuid = "0AC59CA4-DFA6-442C-8C65-22247851344C";
  var major = 1;
  var minor = 100;

  function initialise() {
    var delegate = new cordova.plugins.locationManager.Delegate();

    // Event when advertising starts (there may be a short delay after the request)
    delegate.peripheralManagerDidStartAdvertising = function(pluginResult) {
      console.log('StartAdvertising: ' + JSON.stringify(pluginResult.region));
    };

    // Event when bluetooth transmission state changes
    delegate.peripheralManagerDidUpdateState = function(pluginResult) {
      console.log('UpdateState: ' + JSON.stringify(pluginResult.region));
    };

    // Set delegate
    cordova.plugins.locationManager.setDelegate(delegate);

    // Set initial beacon identifiers in EL
    adf.mf.api.setValue({"name": "#{applicationScope.uuid}", "value": uuid},
                        function() {}, function() {});
    adf.mf.api.setValue({"name": "#{applicationScope.major}", "value": major},
                        function() {}, function() {});
    adf.mf.api.setValue({"name": "#{applicationScope.minor}", "value": minor},
                        function() {}, function() {});
  }

  function createRegion() {
    // Read the values input by the user
    adf.mf.el.getValue("#{applicationScope.major}",
                       function(req, res) { major = res[0]['value'] }, function() {});
    adf.mf.el.getValue("#{applicationScope.minor}",
                       function(req, res) { minor = res[0]['value'] }, function() {});
    return new cordova.plugins.locationManager.BeaconRegion("FakeBeacon", uuid,
                                                            major, minor);
  }

  // Callable externally
  startAdvertising = function() {
    // Verify the platform supports transmitting as a beacon
    cordova.plugins.locationManager.isAdvertisingAvailable().then(function(isSupported){
      if (isSupported) {
        cordova.plugins.locationManager.startAdvertising(
          createRegion()).fail(console.error).done();
      } else {
        alert("Advertising not supported");
      }
    }).fail(console.error).done();
  }

  // Callable externally
  stopAdvertising = function() {
    cordova.plugins.locationManager.stopAdvertising().fail(console.error).done();
  }

  document.addEventListener("showpagecomplete", initialise, false);

}) ();

Let’s take a look at what this code does:

  • There are two externally callable functions – startAdvertising and stopAdvertising, each of which invokes the corresponding plugin API.
  • There are two internal functions – initialize, which is invoked when the framework loads the feature, and createRegion, which creates a new beacon region based on the uuid, major and minor variables defined at the top of the file. The value for uuid should be the same as you specified in the BeaconDemo app.
  • The initialize function creates and registers a Delegate object with two callback functions (which are only included for debug logging) and initializes three application-scope variables.
  • The application-scope variables are used on the AMX page we will create.

Step 4 – Create a managed bean

We will use a managed bean to provide the interface between the AMX code and the JavaScript functions.

Create a new Java file called BeaconBean.java by selecting the ViewController node in the Projects window, right click, select New > Java Class and enter “BeaconBean” as the name.  Enter the following code into the empty Java file and save your changes:

package mobile;

import oracle.adfmf.amx.event.ActionEvent;
import oracle.adfmf.framework.api.AdfmfContainerUtilities;
import oracle.adfmf.framework.api.AdfmfJavaUtilities;
import oracle.adfmf.java.beans.PropertyChangeListener;
import oracle.adfmf.java.beans.PropertyChangeSupport;

public class BeaconBean {

  private boolean advertising;
  private PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);

  public BeaconBean() {
  }

  public void setAdvertising(boolean advertising) {
    boolean oldAdvertising = this.advertising;
    this.advertising = advertising;
    propertyChangeSupport.firePropertyChange("advertising",
                                             oldAdvertising,
                                             advertising);
  }

  public boolean isAdvertising() {
    return advertising;
  }

  public void startAdvertising(ActionEvent actionEvent) {
    AdfmfContainerUtilities.invokeContainerJavaScriptFunction(
      AdfmfJavaUtilities.getFeatureId(), "startAdvertising", new Object[] {});
    setAdvertising(true);
  }

  public void stopAdvertising(ActionEvent actionEvent) {
    AdfmfContainerUtilities.invokeContainerJavaScriptFunction(
      AdfmfJavaUtilities.getFeatureId(), "stopAdvertising", new Object[] {});
    setAdvertising(false);
  }

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

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

Let’s take a look at what this code does:

  • Two methods are provided for managing the advertising of iBeacon data:
    • startAdvertising calls the JavaScript startAdvertising function
    • stopAdvertising calls the JavaScript stopAdvertising function
  • The advertising attribute holds the advertising status and can be accessed by getter and setter methods.
  • Consuming classes can add and remove PropertyChangeListeners, as required, to be notified of changes to the advertising status.

Step 5 – Create the AMX feature

The final step prior to deployment is to create the AMX feature.

Return to the maf-feature.xml file and click on the Insert Feature button to add a feature called Beacon.

Open the Content tab and click on the Insert Include button to include the JavaScript file we created earlier by entering "resources/js/beacon.js" as the file location.

Click on the Create button in the Content tab to create an AMX file called beacon.amx.

Here's where I should point out, if you haven't noticed already, that the Java bean we created earlier isn't yet "managed" by anything.  To turn it into a managed bean, open the adfc-mobile-config.xml file, which is located in Projects > ViewController > Web Content, select the Overview tab, select the Managed Beans tab and select Add.  Enter “BeaconBean” as the name, “mobile.BeaconBean” as the class and select application as the scope, then save all your changes.

Returning to beacon.amx, use the AMX components found in the Components palette to create an AMX page similar to the following and save all your changes:

<?xml version="1.0" encoding="UTF-8" ?>
<amx:view xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xmlns:amx="http://xmlns.oracle.com/adf/mf/amx"
          xmlns:dvtm="http://xmlns.oracle.com/adf/mf/amx/dvt">
  <amx:panelPage id="pp1">
    <amx:facet name="header">
      <amx:outputText value="Fake Beacon" id="ot1"/>
    </amx:facet>
    <amx:inputText label="UUID" id="it1" readOnly="true"
                   value="#{applicationScope.uuid}"/>
    <amx:inputText label="Major" id="it2" readOnly="#{BeaconBean.advertising == true}"
                   value="#{applicationScope.major}" inputType="number"/>
    <amx:inputText label="Minor" id="it3" readOnly="#{BeaconBean.advertising == true}"
                   value="#{applicationScope.minor}" inputType="number"/>
    <amx:commandButton text="Start advertising" id="cb1"
                       actionListener="#{BeaconBean.startAdvertising}"
                       rendered="#{BeaconBean.advertising != true}"/>
    <amx:commandButton text="Stop advertising" id="cb2"
                       actionListener="#{BeaconBean.stopAdvertising}"
                       rendered="#{BeaconBean.advertising == true}"/>
  </amx:panelPage>
</amx:view>

Let’s take a look at what this code does:

  • The page displays the UUID, major value and minor value of the fake beacon, which are stored in application-scope variables. 
  • The major and minor values can be edited by the user when the app is not advertising.
  • Start Advertising and Stop Advertising buttons enable you to start or stop advertising as a fake beacon, depending on whether advertising is active, by calling the startAdvertising and stopAdvertising methods on the BeaconBean managed bean.

Step 7 – Deploy the app

The app must be deployed to a BLE-enabled iOS device.  Use the same iOS provisioning profile and signing identity as you did for the BeaconDemo app.

Configure an appropriate bundle ID in the maf-application.xml file as you did for the BeaconDemo app.  Deselect Show Navigation Bar on Application Launch, save all your changes and deploy the app to a BLE-enabled device.

Before launching the app, ensure you have Bluetooth enabled.  While the app is running, if it is not advertising, you can modify the major and minor values for your fake beacon.  The app looks similar to the following:


Conclusion

This post demonstrates how easily you can create a MAF app that detects iBeacons and notifies the user when a particular iBeacon region has been entered.

Detecting the beacons is the easy part, but how will you provide relevant information to your users in a real-world scenario, such as those mentioned?

When a beacon region is entered, or a user’s proximity to a particular beacon is closer than a defined limit, your MAF app could trigger a local notification, display a popup dialog or navigate to a page containing the relevant information.  MAF provides APIs for each of these scenarios and there are sample apps provided within each MAF release demonstrating how to use these APIs.

To keep your MAF app up-to-date, your app could connect to a REST-based service when launched, and download a current list of beacon identifiers and all relevant information.

For more information about these and other topics, please refer to the Oracle A-Team Chronicles, in particular this article on iBeacon and this article on the Mobile Persistence Accelerator (which includes REST-JSON and REST-XML wizards for MAF).

About

This blog is is dedicated to announcements,tips and tricks and other items related to developing, integrating, securing, and managing mobile applications using Oracle's Mobile Platform. It is created and maintained by the Oracle Mobile product development team.

Archive of past entries

Even More Mobile Development Blogs

Oracle A-Team Site - Mobile Related Entries

Code samples from the Community

Fusion Middleware Blogs

Search

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