Skip to main content

MAF 2.1.1 : Using Local Notifications

One of the new features in version 2.1.1. of Oracle MAF are local notifications.
These notifications originate within the MAF application and are received by the same application. They are delivered to the end user through standard mechanisms supported by the mobile device platform (for example, banner, sound) and can work when the application is either in the foreground, background or not running at all.

I this post I show you an example of how to work with Local Notifications from Java. I use a simple MAF app. I will not explain how to build this app, but the source can be downloaded here. It is mainly derived from the "LocalNotificationDemo" public sample app.

Introducing Local Notifications
As with many framework features, MAF supports three ways to set Local Notifications. First you can use the device features datacontrol. To support declarative use of Local Notifications, the DeviceFeatures data control includes the addLocalNotification and cancelLocalNotification methods, which enable MAF applications to leverage a device's interface for managing notifications so end users can schedule or cancel local notifications.


Second you have the option to set Local Notifications from JavaScript. MAF allows you to manage local notifications using JavaScript APIs in the adf.mf.api.localnotification namespace. The methods add() and cancel() are available. More info on this is available from the developer guide (see resources at the end of this post).

Finally you can set Local Notifications from Java code, which is what I will explain in the remaining part of this post.

Set up the Listening Part
Because the Listening part is the same for all methods mentioned above I will start to explain this before going into detail for setting Local Notifications from Java code.
The concept of Local Notifications is from an MAF perspective not different from Push Notifications
First we need to create an eventListener that specifically listens for Local Notifications.
This class must implement oracle.adfmf.framework.event.EventListener.
In this class we must use the onMessage() method, which will fire when a notification is received.

public class MyLocalNotificationListener implements EventListener {
    public MyLocalNotificationListener() {
    }

    public void onMessage(Event event) {
    
       // work with the notification event
       // Here we can get the application state (for wich we can use a util method) and the payload
        String appState = stringifyAppState(event.getApplicationState());
        String payload = event.getPayload();

       // Now do whatever we want, for instance call a feature
        AdfmfContainerUtilities.gotoFeature("com.blogspot.lucbors.lnb.NotificationShower");
        
        }
    private String stringifyAppState(int appState) {
    switch(appState) {
        case Event.APPLICATION_STATE_FOREGROUND: return "FOREGROUND";
        case Event.APPLICATION_STATE_BACKGROUND: return "BACKGROUND";
        case Event.APPLICATION_STATE_NOT_RUNNING: return "NOT RUNNING";
    }
    return "UNKNOWN";
    }


After creating this Listener class it must be added as an eventSource in the start Method of the application Lifecycle Listener.
In this way, each time the app starts, we make sure the app is actually Listening to the local Notification event.
public void start()
  {
    // Listen for local notifications
    EventSource evtSource =  
            EventSourceFactory.getEventSource(EventSourceFactory.NATIVE_LOCAL_NOTIFICATION_EVENT_SOURCE_NAME);
    evtSource.addListener(new MyLocalNotificationListener());
  }

With all of this code in place, the app is ready to listen for and respond to local notification.

Creating local Notifications from Java
Creating a new Notification is pretty much straightforward. In this example we use a button to create a local Notification.

To work with Local Notifications using Java, the frameworks' utility class oracle.adfmf.framework.api.AdfmfContainerUtilities contains several methods to help you. The first one is addLocalNotification(). This method can be used to create a local notification. It needs an MafNativeLocalNotificationOptions Object.

The MafNativeLocalNotificationOptions Object contains several properties that can be used for notifications.

protected String title;
    protected String alert;
    protected LocalDateTime date;
    protected RepeatInterval repeat;
    protected int badge;
    protected String sound;
    protected String vibration;
    protected HashMap <String> payload;

So for creating a local Notification we first need to create a new MafNativeLocalNotificationOptions object, called options in the code below. Next we set the values for all, or many, of the properties such as alertTitle, alert, notifationDate, badge and maybe even sound and vibration. Also, if the notification needs a payload we need to set this payload. Once these values are properly set we can add a new notification to the app by calling addLocalNotification(). The call to this method returns a String containing the notificationId.

public void addNotificationForAction(ActionEvent actionEvent) {
        String notificationDate = "now";
        try
        {
            // Set the notification options
            MafNativeLocalNotificationOptions options = new MafNativeLocalNotificationOptions();
            options.setTitle("Just Some Reminder");
            options.setAlert("Did you Forget Something ?");
            if (date != null) {
                date.setSeconds(0); // Clear the seconds component to fire on the minute
                LocalDateTime l = LocalDateTime.ofInstant(Instant.ofEpochMilli(date.getTime())
                                                        , ZoneOffset.UTC);
                options.setDate(l);
                DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
                notificationDate = dateFormat.format(date);
            }
            options.setBadge(-1);
            if (sound) {
                options.setSound(MafNativeLocalNotificationOptions.SOUND_DEFAULT_SYSTEM);
            }
            if (vibration) {
                options.setVibration(MafNativeLocalNotificationOptions.VIBRATION_DEFAULT_SYSTEM);
            }
            // Set 3 values in the JSON payload
            HashMap<String Object> payload = new HashMap<String Object>();
            payload.put("key1", 1);
            payload.put("key2", "hello");
            payload.put("key3", true);
            options.setPayload(payload);
            
            // Add the notification
            String notificationID = AdfmfContainerUtilities.addLocalNotification(options);
            System.out.println("++++ Notification added successfully for " + notificationDate);
            System.out.println("++++ Notification ID is " + notificationID);
        }
        catch(Exception e)
        {
            System.out.println("++++ There was a problem adding notification: " + e.getMessage());
            e.printStackTrace();
        }    
    }

Once this notification is added, it will show up at the given moment; in this case the date that is set with options.setDate(). It will show the Notification with the corresponding message; in this case "Did you Forget Something ?" as set by setAlert();



Cancelling a Notification
Sometimes, a notification is set as a reminder for some action. As in the previous example, "Did you forget something", the user is reminded to do something. There are situations where the user already did what you reminded him to do. In that case we don't want to show the notification.

For that purpose we can use the cancelLocalNotification() method in the AdfmfContainerUtilities class. To cancel the notification we must provide the notification ID.

    public void cancelLocalNotification(ActionEvent actionEvent) {   
        try {
       // We use the notificationId that was set when the notification was added
       //setNotificationId( AdfmfContainerUtilities.addLocalNotification(options));
          String cancelledNotificationId = AdfmfContainerUtilities.
                 cancelLocalNotification(notificationId);
          System.out.println("Notification successfully canceled"); 
        }
        catch(AdfException e) {
          System.err.println("There was a problem cancelling notification");
        }
    }


Scheduling a Repeating Notification
If we want to schedule a notification to fire every time during a given interval we can use the setRepeat() on the MafNativeLocalNotificationOptions. An example of this could be that you want to remind the user to open his app every day to check for changes. So if we want a notification that is scheduled to fire every Day we simply need to call setRepeat() with interval Daily.

options.setRepeat(MafNativeLocalNotificationOptions.RepeatInterval.DAILY);

Just for testing and for the purpose of the demo I like to work with a Minutely interval. This makes not a lot of sense, but for testing, it is a good interval, at least compared to yearly.... The available options are displayed below:



Cancelling a Repeating Notification: A Solution for a Limitation
Now lets assume we have a clever user who by now knows that we what him to check his app every day. So here is the situations where the user already did what you reminded him to do. In that case we don't want to show the notification.

For that purpose we must also use the cancelLocalNotification() method in the AdfmfContainerUtilities class. Cancelling a notification means that the notification is COMPLETELY cancelled even if it was scheduled to fire every DAY. Once cancelled, it will no longer notify, not even the next day. So in order to make this really work, cancel today and notify tomorrow, we must create a new local notification, exactly like the one that was just cancelled with interval DAILY.
Just for testing purposes I added an application preference that I can use to indicate whether or not I really want to recreate the local notification.

    public void cancelLocalNotification(ActionEvent actionEvent) {   
        try {
       // We use the notificationId that was set when the notification was added
       //setNotificationId( AdfmfContainerUtilities.addLocalNotification(options));
          String cancelledNotificationId = AdfmfContainerUtilities.
                 cancelLocalNotification(notificationId);
          System.out.println("Notification successfully canceled"); 
          

            Boolean recreate = (Boolean)AdfmfJavaUtilities.evaluateELExpression(
            "#{preferenceScope.application.ReCreateNotificationAfterCancel.ReCreate}");
            // if recreate preference is true
            if (recreate.booleanValue()){
               addNotification();
            }
        }
        catch(AdfException e) {
          System.err.println("There was a problem cancelling notification");
        }
    }

Look at the logfile below and see what happens. First, the notification is created (1), then it is cancelled (2), and immediately recreated !


NOTE (1): The above limitation is not a limitation of Oracle MAF. This is simply how local notifications work on the Operating Systems. Although I am not 100% sure that the solution of re-creating the notification is the most optimal, for now it really seems to be the only way to make this work.

Resources
MAF Developer Guide Chapter 24
The sources of this app are available here.


Comments

Popular posts from this blog

ADF 12.1.3 : Implementing Default Table Filter Values

In one of my projects I ran into a requirement where the end user needs to be presented with default values in the table filters. This sounds like it is a common requirement, which is easy to implement. However it proved to be not so common, as it is not in the documentation nor are there any Blogpost to be found that talk about this feature. In this blogpost I describe how to implement this. The Use Case Explained Users of the application would typically enter today's date in a table filter in order to get all data that is valid for today. They do this each and every time. In order to facilitate them I want to have the table filter pre-filled with today's date (at the moment of writing July 31st 2015). So whenever the page is displayed, it should display 'today' in the table filter and execute the query accordingly. The problem is to get the value in the filter without the user typing it. Lets first take a look at how the ADF Search and Filters are implemented by

How to: Adding Speech to Oracle Digital Assistant; Talk to me Goose

At Oracle Code One in October, and also on DOAG in Nurnberg Germany in November I presented on how to go beyond your regular chatbot. This presentation contained a part on exposing your Oracle Digital Assistant over Alexa and also a part on face recognition. I finally found the time to blog about it. In this blogpost I will share details of the Alexa implementation in this solution. Typically there are 3 area's of interest which I will explain. Webhook Code to enable communication between Alexa and Oracle Digital Assistant Alexa Digital Assistant (DA) Explaining the Webhook Code The overall setup contains of Alexa, a NodeJS webhook and an Oracle Digital Assistant. The webhook code will be responsible for receiving and transforming the JSON payload from the Alexa request. The transformed will be sent to a webhook configured on Oracle DA. The DA will send its response back to the webhook, which will transform into a format that can be used by an Alexa device. To code

ADF 11g Quicky 3 : Adding Error, Info and Warning messages

How can we add a message programatically ? Last week I got this question for the second time in a months time. I decided to write a short blogpost on how this works. Adding messages is very easy, you just need to know how it works. You can add a message to your faces context by creating a new FacesMessage. Set the severity (ERROR, WARNING, INFO or FATAL ), set the message text, and if nessecary a message detail. The fragment below shows the code for an ERROR message. 1: public void setMessagesErr(ActionEvent actionEvent) { 2: String msg = "This is a message"; 3: AdfFacesContext adfFacesContext = null; 4: adfFacesContext = AdfFacesContext.getCurrentInstance(); 5: FacesContext ctx = FacesContext.getCurrentInstance(); 6: FacesMessage fm = 7: new FacesMessage(FacesMessage.SEVERITY_ERROR, msg, ""); 8: ctx.addMessage(null, fm); 9: } I created a simple page with a couple of buttons to show the result of setting the message. When the but