Wednesday, June 19, 2013

ADF Mobile Push Notifications With Google Cloud Messaging (GCM) Part 1

Oracle ADF Mobile 11.1.2.4 release adds the Push Notification Support,  now ADF Mobile application can register to receive notifications through both the Apple and Google notification services. In this article I try to explain on how to implement “Google Cloud Messaging for Android (GCM) with ADF Mobile, GCM is a service that helps developers to send data from servers to their Android applications on Android devices“.

Thanks to Joe Haung his article helped me to understand the implementation of ADF Mobile with push notification.    

Below diagram illustrates the overview working concept of ADF Mobile with Push Notification support.


[Runs with Oracle JDeveloper 11.1.2.4.0].

Registering with Google Cloud Messaging
  • Open Google APIs Console page and create a new project. (If you haven’t created already otherwise it will take you to dashboard). 
  • After creating project, click on Overview tab. Note down the Project Number which will be used as Sender Id / Authorization Id in ADF Mobile application
  • Click on Services tab on the left panel and turn on Google Cloud Messaging for Android.
  • Once you are done, click on API Access and note down the API Key. This API key will be used when sending requests to GCM server.

ADF Mobile Implementation Steps

Create an ADF Mobile application, In the application controller project, register an application lifecycle event listener (ALCL) class. Open the Application Resources -> Descriptors -> ADF META-INF -> adfmf-application.xml, click on the LifeCycle Event Listener search icon and added “application.LifeCycleListenerImpl”.


Open the LifeCycleListenerImpl class and implement the oracle.adfmf.application.PushNotificationConfig interface. This interface provides the registration configuration for push notifications. Override and implement the getNotificationStyle and getSourceAuthorizationID methods of the PushNotificationConfig interface. LifeCycleListenerImpl java class looks like below.
public class LifeCycleListenerImpl implements LifeCycleListener, PushNotificationConfig {
    public LifeCycleListenerImpl() {
    }
  
    public long getNotificationStyle() {
        return 0L;
    }

    public String getSourceAuthorizationId() {
        return null;
    }
  
    public void start() {
       // Add code here...
    }

    public void stop() {
        // Add code here...
    }

    public void activate() {
        // Add code here...
    }

    public void deactivate() {
        // Add code here...
    }
} 
In the application controller project, create a push notification listener class (for example, NativePushNotificationListener) that handles push notification events. This class must implement the oracle.adfmf.framework.event.EventListener interface that defines an event listener.
public class NativePushNotificationListener implements EventListener {

    public NativePushNotificationListener() {
        super();
    }

    public void onMessage(Event event) {
        //Parsing the payload JSON string
        JSONBeanSerializationHelper jsonHelper = new JSONBeanSerializationHelper();
        try {
            PayloadServiceResponse serviceResponse =
                (PayloadServiceResponse)jsonHelper.fromJSON(PayloadServiceResponse.class, event.getPayload());
            //Can access the variables like below
            serviceResponse.getCustomMessage();
            // To do 
        } catch (Exception e) {
            e.printStackTrace();

        }
    }

    public void onError(AdfException adfException) {
    }

    public void onOpen(String string) {
        // Storing the regisration id send by GCM server in #{applicationScope.deviceToken} for further usage
        ValueExpression ve = AdfmfJavaUtilities.getValueExpression("#{applicationScope.deviceToken}", String.class);
        ve.setValue(AdfmfJavaUtilities.getAdfELContext(), string);
    }
}
Create a PayloadServiceResponse.java class and add the below code.
public class PayloadServiceResponse {
    private double from;
    private String collapse_key;
    private String customMessage;
    private String sound;
    private String alert;

    public void setFrom(double from) {
        this.from = from;
    }

    public double getFrom() {
        return from;
    }

    public void setCollapse_key(String collapse_key) {
        this.collapse_key = collapse_key;
    }

    public String getCollapse_key() {
        return collapse_key;
    }

    public void setCustomMessage(String customMessage) {
        this.customMessage = customMessage;
    }

    public String getCustomMessage() {
        return customMessage;
    }

    public void setSound(String sound) {
        this.sound = sound;
    }

    public String getSound() {
        return sound;
    }

    public void setAlert(String alert) {
        this.alert = alert;
    }

    public String getAlert() {
        return alert;
    }
}
Open the LifeCycleListenerImpl class and modify the below method code as followed.
public void start() {
        //Create an EventSource object that represents the source of a native push notification event:
        EventSource evtSource =
            EventSourceFactory.getEventSource(NativePushNotificationEventSource.NATIVE_PUSH_NOTIFICATION_REMOTE_EVENT_SOURCE_NAME);
        //Create and add an object of the push notification listener class to the event source
        evtSource.addListener(new NativePushNotificationListener());
    }
   
   public long getNotificationStyle() {
        return 0L;
    }

    public String getSourceAuthorizationId() {
        //Here AuthorizationId is the "Sender Id" of Google project created earlier
        String senderId = "765350429141";
        ValueExpression ve = AdfmfJavaUtilities.getValueExpression("#{applicationScope.applicationId}", String.class);
        ve.setValue(AdfmfJavaUtilities.getAdfELContext(), senderId);
        return senderId;
    } 
In the application controller project, click on new gallery expand the General - Data Control nodes and select Webservice Service Data Control. Refer to the ADF Mobile Push Notifications With Google Cloud Messaging (GCM) Part 2, set the server side code to access http://hostname/GCMService.php?wsdl


In the Create Web Service Data Control - Step 2 of 5 wizard, move the persistGCMUser from available to selected and click on Finish.


In ViewController project. Locate and expand the Application Sources folder, then expand the META-INF folder. You will see the adfmf-feature.xml file, click on the adfmf-feature.xml file to launch the Feature editor. Add a new feature by clicking the green plus sign on the Features table near top of the editor this will launch the new Create ADF Mobile Feature dialog, modify the values as shown below.


In the Features table, select the newly created feature SaveDeviceToken. Under the Features table, click the Content tab, and locate the Content table. Notice that the content item SaveDeviceToken.1 is created by default. Next add a new file by clicking the green plus sign and select ADF Mobile AMX Page option, this will launch the new Create ADF Mobile AMX Page, modify the File Name as "SaveDeviceToken.amx".

From Data Control palette drag and drop GCMService->persistGCMUser_parameters->gcmUser as ADF Mobile Form and  GCMService->persistGCMUser as ADF Mobile button as shown below.


In SaveDeviceToken.amx page,  create a managed bean with action listener method (name as executePersistGCMUser) and map to  persistGCMuser button action listener.


Open the SaveDeviceToken.amx and modify the page with below code.
<?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="Save Device Token" id="ot1"/>
        </amx:facet>
        <amx:panelFormLayout id="pfl1">
            <amx:inputText value="#{bindings.username.inputValue}" label="#{bindings.username.hints.label}" id="it4"/>
            <amx:inputText value="#{bindings.email.inputValue}" label="#{bindings.email.hints.label}" id="it3"/>
            <amx:panelLabelAndMessage label="GCM Device Token" id="plam1">
                <amx:outputText value="#{applicationScope.deviceToken}" id="ot3"/>
            </amx:panelLabelAndMessage>
        </amx:panelFormLayout>
        <amx:commandButton actionListener="#{SaveDeviceTokenBean.executePersistGCMUser}" text="Save GCM User"
                           disabled="#{!bindings.persistGCMUser.enabled}" id="cb1">
            <amx:setPropertyListener id="spl1" from="#{applicationScope.applicationId}"
                                     to="#{bindings.applicationId.inputValue}" type="action"/>
            <amx:setPropertyListener id="spl2" from="#{deviceScope.device.os}" to="#{bindings.deviceType.inputValue}"
                                     type="action"/>
            <amx:setPropertyListener id="spl3" from="#{applicationScope.deviceToken}"
                                     to="#{bindings.deviceToken.inputValue}" type="action"/>
        </amx:commandButton>
    </amx:panelPage>
</amx:view>

Open the SaveDeviceTokenBean.java class and modify the executePersistGCMUser as shown below.
public void executePersistGCMUser(ActionEvent actionEvent) {
        MethodExpression me =
            AdfmfJavaUtilities.getMethodExpression("#{bindings.persistGCMUser.execute}", Object.class,
                                                   new Class[] { });
        me.invoke(AdfmfJavaUtilities.getAdfELContext(), new Object[] { });
        //Navigate to the Tasks feature
        AdfmfContainerUtilities.gotoFeature("Tasks");
    }
SaveDeviceToken.amx preview looks as below.


Open on the adfmf-feature.xml file to launch the Feature editor. Add a new feature by clicking the green plus sign on the Features table near top of the editor this will launch the new Create ADF Mobile Feature dialog, modify the values as shown below.


In the Features table, select the newly created feature Tasks. Under the Features table, click the Content tab, and locate the Content table. Notice that the content item Tasks.1 is created by default. Next add a new file by clicking the green plus sign and select ADF Mobile AMX Page option, this will launch the new Create ADF Mobile AMX Page, modify the File Name as "TasksIndexPage.amx". Page preview looks like below.


Deployed and tested the Push Notification support on both Android Device/Emulator and work's fine. If your are running the application on the emulator make sure you select target as Google APIs while creating the Android Virtual Device.


Click on the Settings icon in the emulator and go to ACCOUNTS section. Add the Google account, so that C2DM configure with Gmail account for mapping a unique device.


Application screen looks like below when it run on Android Device/Emulator. At this stage the application would initiate a registration request with push notification services, after successful registration, GCM server issues registration id back to the android device that would uniquely identify the ADF Mobile application and the device.  Note in the first screen GCM Device Token is displayed after successful registration.



Enter form details and click save button to send back the data to application provider server, also navigates to Tasks Details screen.


Handling Push Notifications
  • The ADF Mobile application is installed, but not running and ADF Mobile application is running in the background - In both the cases notification the notification message displays with the registered notification style (none, banner, or alert). When the user taps the message (if its a banner-style notification) or touches the action button (if the message appears as an alert), the ADF Mobile application launches, invoking the application notification handlers. 
  • The ADF Mobile application is running in the foreground - No notification message displays. The application notification handlers are invoked.
Open the http://hostname in the browser will load the index.php page and displays devices which are registered and send the message.


Refer the ADF Mobile Push Notifications With Google Cloud Messaging (GCM) Part 2, how to set the server side code to access http://hostname/index.php and send message from sever side to GCM. Once the message is sent you can see the notification in both Device/Emulator as shown below.


In the NativePushNotificationListener class - onMessage method load, you can parse the event payload and invoke additional application logic accordingly. Below is the example how the payload looks when you run in the debug mode. You can refer to NativePushNotificationListener class I have showed how to parse the payload. 


[Runs with Oracle JDeveloper 11.1.2.4.0].

18 comments:

  1. Good article, few corrections :
    1. In onOpen definition above change variable "string" to "token"
    2. Where you say "In the Application Lifecycle Listener - onMessage method load, you can parse the event payload and invoke additional application logic accordingly.", onMessage does not belong to Application Lifecycle Listener class but to Push Notification Event Listener.

    ReplyDelete
    Replies
    1. Thanks Piyush for the correction.

      -Deepak

      Delete
  2. Nice post with great details. I really appreciate your work. Thanks for sharing.

    emergency notification

    ReplyDelete
  3. Hi Deepak,
    Nice Article. I am started implementing it for ios and android. Could you help me in the following questions.
    1) Could you please let me know the best customer configurable place holder for placing the Sender Id/Authroization Id for android (GCM) push notifications. If i configure in some place then how to make use it lifecyclelistner's getSourceAuthorizationId().
    2)I want to send some other info in push payload(custom attributes ) like
    "myapp" : {
    "goto" : "Feature1",
    "key" : "key1",
    "userid" : "shyam"
    }
    so do i need to parse the json response on my own or JSONBeanSerializationHelper.fromJSON() will be helpfull. I didnt try it. But i want to know if i use custom attributes how to parse them .
    Please let me know the details.

    Thanks
    Shyam

    ReplyDelete
  4. Nice post deepak! It would be interesting if you do the same with Apple Notification Service.

    Thanks!

    ReplyDelete
    Replies
    1. I don't have the mac book to try Apple Notification Service :). Planning to get one in few days, at that time I will try and post the article.

      Delete
  5. Hi Deepak, could you help me?
    i send message successfully,but not receive the notification .
    how can i solve it?

    ReplyDelete
    Replies
    1. Are you using emulator or device to get push notification. If you are using any company network with vpn, please come out of that and try.

      Delete
    2. Have you solve the problem? Is VPN cause the issue? THX.

      Delete
    3. Hi Deepak,
      I also faced the same above issue "Able to send message successfully,but not able to receive notification". I tried with device with phone internet. And what i observed is,in my google api console, when i open the Reports section of my project, It shows "No data available" does it mean provider does not send any information(such as registration-id) to GCM to send the message to the device?
      Please suggest on this.

      Delete
  6. Thank you for providing the information regarding push notification.

    ReplyDelete
  7. Hi Deepak,
    how to send message successfully in iOS devices with Oracle ADF Mobile Technology?
    can you please help me?

    ReplyDelete
  8. Hi Deepak,
    Thank you for the wonderful blog.
    I have implemented the push notification as explained by you and facing one issue.
    I am only able to receive only alert sound along client app icon whenever i send message from provider side and once i taped the message it will open the actual push notification client app but with no message.only alert sound and client app icon am getting every time i send the message. please suggest, where i might doing wrong.

    this what i have done in my Native push notification listener to show the message in TaskIndexPage.amx

    public void onMessage(Event event) {
    //Parsing the payload JSON string
    JSONBeanSerializationHelper jsonHelper = new JSONBeanSerializationHelper();
    try {
    PayloadServiceResponse serviceResponse =
    (PayloadServiceResponse)jsonHelper.fromJSON(PayloadServiceResponse.class, event.getPayload());
    //Can access the variables like below
    serviceResponse.getCustomMessage();
    // To do
    } catch (Exception e) {
    e.printStackTrace();

    }
    }

    Please suggest on this.
    Thank you

    ReplyDelete
  9. Hi everyone,
    I have also added the same code in my OnMessage method but i am getting only notification and not the message. Any help on the above would be deeply appreciated as I have my finals tomorrow.
    Thanks & Warm Regards.
    mail : mehul.c.dedhia@gmail.com

    ReplyDelete
  10. Hi Deepak,
    Thanks for the nice blog.
    I am not getting Device Token when I open Save Device Token Page.
    I have also set up google account.
    One change is that I am using MAF2.1 in jdev 12.1.3.
    Do I need to do any thing else. I have tried running your sample application and that is also not working.
    Any hints much appreciated.

    Regards,
    KP

    ReplyDelete
    Replies
    1. Sry Its My Bad.
      I forgot to enable Network and push plugin in Maf-application.xml file.

      Delete
  11. Hi Deepak,
    I am able to get the notification butt the message is not displying.
    Can you let me know what can be the possible cause.

    Regards,
    kaustubh Patel.

    ReplyDelete
  12. Hi Deepak,
    I'm having a problem when two app open at the same time, he requested the gcm the token, however one only returns the token the other not. Can you tell me if the token is empty, how do I make a new soliciatacao the gcm? If you can help me I will be very grateful.

    ReplyDelete