Deliver an exceptional mobile web experience using WebSphere Portal and IBM Worklight, Part 5: Integrating an event-based portlet with a hybrid mobile application

This article describes how you can use IBM® Rational® Application Developer V9 to create an IBM WebSphere® Portal V8-based portlet application that provides the web content for a hybrid mobile application, created using IBM Worklight® V6. The hybrid application further augments the web content with device native features.

Share:

Gaurav Bhattacharjee, Staff Software Engineer, IBM Corporation

author photoGaurav Bhattacharjee is a technical lead in the IBM India Software Labs in Delhi, India. He works with the Rational Application Developer Portal Tooling team in the IBM Software Group, IBM Collaboration Solutions.



09 October 2013

Introduction

With a growing number of smartphones and tablets in the market, users expect a variety of applications for their devices that will access web content and leverage native features. Users have a choice for accessing plain mobile web content: either use applications that are purely native, or use hybrid applications that combine the native experience with web content; the latter usually offers the best of both worlds.

IBM Worklight provides an open, comprehensive, and advanced mobile application development platform for smartphones and tablets that offers a framework to wrap around HTML5 web pages and transforms them into native applications. IBM WebSphere Portal provides a platform for creating exceptional, personalized, and engaging user experiences by employing a variety of technologies, such as Dojo or jQuery.

By combining the web experience capabilities created using WebSphere Portal with the mobile capabilities provided by Worklight, you can create hybrid applications that fetch web content from the portal runtime in a native device shell and augment the web content with native device capabilities.

This article describes how you can create a WebSphere Portal-based portlet application to provide the web content for a hybrid Worklight mobile application. The hybrid application further augments the web content with device-native features. The steps in this article will help you build these sample use cases:

  • Create a multi-channel portlet application that will provide a different UI for a desktop browser and for a smartphone or tablet browser

    In this case, you will design the application in such a manner that it will show you a list of events in the UI. Figure 1 shows the UI you'll see when the portal page containing the portlet is accessed through a smartphone or tablet browser. Similarly, Figure 2 shows the UI that you'll see when the portal page containing the portlet is accessed through a desktop browser.

    Figure 1. UI in the smartphone or tablet browser
    UI in the smartphone or tablet browser
    Figure 2. UI in the desktop browser
    UI in the desktop browser

    Clicking on the Add to Calendar button will download an iCalendar file that can be opened through any desktop calendar management software.

  • Create a hybrid application for smartphones in Worklight

    In this case, you will develop a hybrid application that will get its content from the portal server and augment it with functionality that will invoke the native device’s calendar. This is depicted in Figure 3, where the Android application (running on an Android emulator) shows the same UI as Figure 1, with the addition of the Add to Calendar buttons. Clicking on Add to Calendar would invoke the calendar application of the device.

    Figure 3. UI in an Android application
    UI in an Android application

In this article, you'll be using Dojo mobile widgets to develop the UI.


Prerequisites

To follow the steps outlined in this article requires:

  1. IBM Rational Application Developer V9.0 with portal toolkit 8.0 installed.
  2. IBM Worklight 6.0 installed in shell shared mode with Rational Application Developer.
  3. IBM WebSphere Portal V8.0.0.1.
  4. Worklight theme profile created on WebSphere Portal, as explained in Part 1 of this series.

Creating a multi-channel portlet application

The first task in the development process is creating the portlet application that will provide the application content. These are the major steps involved:

  1. Create project portlet
  2. Review the generated code
  3. Develop UI for smartphones
  4. Develop UI for desktop
  5. Enable calendar function for desktop

1. Create portlet project

  1. The first thing you need to do is create a Dojo-enabled Portlet project. In Rational Application Developer, select File > New > Project > Portlet project. This will invoke the New Portlet Project creation wizard (Figure 4).
  2. For Project name, enter EventsSample.
  3. For Target runtime, select WebSphere Portal v8.0.
    Figure 4. New Portlet project wizard
    New Portlet project wizard
  4. Click the Modify button for Configuration to invoke Modify Portlet Project Configuration dialog (Figure 5).
    Figure 5. Modify Portlet Project Configuration
    Modify Portlet Project Configuration
  5. For Portlet API, select JSR 286 Portlet. For Portlet type, select Basic Portlet. Click OK.
  6. Click on the Modify button for Web 2.0 Features (Figure 4) to invoke the Modify Portlet Project Web Features dialog (Figure 6).
  7. Be sure to select both the JavaScript toolkit and Dojo Toolkit options. Click OK.
    Figure 6. Dojo toolkit option
    Dojo toolkit option
  8. Click on the Modify button for Devices supported (Figure 4) to invoke the Modify Portlet Project Device Support dialog (Figure 7).
    Figure 7. Modify Portlet Project Device Support
    Modify Portlet Project Device Support
  9. Select both SmartPhone and Tablet options, then click OK to return to the portlet project creation wizard.
  10. Click Next (Figure 4) to go to the Portlet Settings page, then click Next again. This will take you to the Action and Preferences panel (Figure 8).
    Figure 8. Action and Preferences
    Action and Preferences
  11. Uncheck Add action listener to portlet to handle action requests to remove action listener support.
  12. Click Finish to complete the creation of the portlet project.

2. Review the generated code

The above exercise leads to the creation of a portlet project with three portlet JSPs, one each for a desktop mode, smartphone mode, and tablet mode. The JSP prefixed as _Device.jsp corresponds to the smartphone mode, _Tablet.jsp corresponds to tablet mode, and the remaining one corresponds to the desktop mode. With three different JSPs for different device forms, you can now design your UIs differently, keeping in mind the real estate layout and availability of the devices.

The automatic code generation in Rational Application Developer also creates a JavaScript™ control file for every portlet view JSP at this location: WebContent/js/portlet/<namespace>/portlethelper, as specified in the Web 2.0 Dojo configuration section. Thus, for the portlet device view JSP, there is a corresponding JavaScript file as well, which is going to contain all associated JavaScript code; for example, WebContent\js\portlet\namespace_64a58d5ce3\portlethelper\EventsSamplePortletView_Devices.js. This .js file contains a JavaScript helper class for the portlet where you'll write all your JavaScript functions and code. You'll make use of this class later.

Now that your project creation is completed, you will develop the UI for smartphones, as shown in Figure 1. The UI would be created by fetching data from JSON files that will be part of the portlet project. The next section takes you through the development of the smartphone UI.

3. Develop UI for smartphones

  1. Open the Enterprise Explorer view. Right-click on the file EventsSamplePortletView_Devices.jsp and choose Open With > Rich Page Editor (RPE). This will open the portlet JSP file in the Rich Page Editor.
  2. Click on the Source View tab and add <div id="list_<portlet:namespace/>"></div> as the child of container element with id scrollableView_<portlet:namespace/>. The code after the change will look like that shown in Listing 1.
    Listing 1. Container for mobile widgets
    <div id="mobileWidgetContainer_<portlet:namespace/>">
    	<div data-dojo-type="dojox.mobile.ScrollableView"
    		id="scrollableView_<portlet:namespace/>"
    			data-dojo-props="selected:true">
    	   	<div id="list_<portlet:namespace/>"></div>	
    	</div>
    </div>

    The container element with id=list_<portlet_namespace/> will contain the Dojo mobile list items that you will be creating in the next steps.

  3. Add the dojo.require statements dojo.require("dojox.mobile.ListItem") and dojo.require("dojox.mobile.Button") under the script tag that already contains several dojo.require statements. The resulting dojo.require code looks like that shown in Listing 2.
    Listing 2. Dojo require statements
     <script type="text/javascript">
    	dojo.require("dojox.mobile.parser");
    	dojo.require("dojox.mobile");
    	dojo.require("dojox.mobile.deviceTheme");
    	dojo.require("dojox.mobile.compat");
    	dojo.require("dojox.mobile.ScrollableView");
    	dojo.require("dojox.mobile.ListItem");
    	dojo.require("dojox.mobile.Button");
    </script>
  4. Select the WebContent folder in the project EventsSample and select File > New > File.
  5. Name the file as events.json and click Finish.
  6. Open the file events.json and copy and paste the contents in Listing 3.
    Listing 3. JSON data
    {
          "items": [
               { "label": "New site launch party", 
                 "description": "New site launch party",           
                 "location":"Orlando",
                 "moveTo":"#",
                 "fromDate":"August 15, 2013 ",
                 "toDate":"August 16, 2013",
                 "variableHeight":true, 
                 "image":"/images/night2big.jpg" 
                },
                { "label": "Exceptional Web Experience conference",
                  "description": "Exceptional Web Experience conference description",  
                  "location":"Berlin", 
                  "fromDate":"August 21, 2013 ",
                  "toDate":"August 23, 2013",
                  "variableHeight":true,
                  "image":"/images/night1big.jpg"
                }
        ]
    }

    The above code represents two rows of JSON data; for example, the attribute label represents the label of one list item, shown in Figure 1.

  7. Create a copy of the images folder included in the downloadable sample file.
  8. Open the file WebContent\js\portlet\<namespace>\portlethelper\EventsSamplePortletView_Devices.js.
  9. Create a new function with the name createUI just after the constructor function. The resulting code would look like that shown in Listing 4.
    Listing 4. Contents of EventsSamplePortletView_Devices.js file
    dojo.provide("portlet.namespace_64a58d5ce3.portlethelper.EventsSamplePortletView_Devices");
    
    dojo.require("portlet.namespace_64a58d5ce3.Portlet");
    /**
     * The PorletHelper class may have all the business logic related to JavaScript   
           for its portlet jsp.
     */
    dojo.declare("portlet.namespace_64a58d5ce3.portlethelper.EventsSamplePortletView_Devices", 
    portlet.namespace_64a58d5ce3.PortletHelper,{
    	constructor : function(args){
    		dojo.mixin(this, args);	
    },
    createUI : function(){
    		
    }
    });
  10. Copy and paste the code from Listing 5 into the createUI function.
    Listing 5. Code to query the json file and create the UI
        var url = this.portlet.contextPath+"/events.json";
    		
        var list = this.portlet.byId("list");
        var len = 0;
        dojo.xhrGet( { 
    		  url: url,
    		  handleAs: "json",
    		  headers: {"If-Modified-Since":0},
    		   load: dojo.hitch(this,function(response){
    
    				  
    		     len = response.items.length;
    	            var ul = dojo.create("ul");
    		     dojo.attr(ul, "id", "ul_"+this.portlet.namespace);
    		     dojo.addClass(ul, "listClass");
    		     dojo.place(ul, list.id);
    				  
    		    var length = response.items.length;
                      
    		    for(var i = 0;i<length; i++){
                    	  var item = response.items[i];
                    	                 	  
                    	  var childWidget = new  
                    	  var childWidget = new  
                           dojox.mobile.ListItem(
                        		 {id:"dojox_mobile_ListItem"+i+"_"+this.portlet.
                        	      namespace, mylabel:item.label, 
                        	      description: item.description,
                        	      variableHeight:true, 
                        	      fromDate : item.fromDate, 
                        	      toDate : item.toDate, image: item.image, location : 
                        	      item.location }
                        		 );
                    	
                           //hack
                    	    dojo.removeClass(childWidget.domNode,"mblListItem");
                    	    dojo.removeClass(childWidget.domNode,"mblVariableHeight");
                    	  
                    	  
                    	    dojo.addClass(childWidget.domNode, "listStyleClass");
                    	  
                    	    if(i+1 == length)
                    		 dojo.addClass(childWidget.domNode, "listLastLiClass");
                    	  
                    	    dojo.place(childWidget.domNode, ul.id);
                    	 
                    	     var imageDivNode = dojo.create("div");
                    	     dojo.addClass(imageDivNode, "itemImageClass");
                    	     dojo.place(imageDivNode, childWidget.id);
                    	  
                    	     var imageNode = dojo.create("img", { height: "80"});
                    	     imageNode.src = this.portlet.contextPath+item.image;
                    	     dojo.place(imageNode, imageDivNode);
                    	  
                    	     var itemDetailsDivNode = dojo.create("div");
                    	     dojo.addClass(itemDetailsDivNode, "itemDetailsClass");
                    	     dojo.place(itemDetailsDivNode, childWidget.id);
                    	  
                    	     var firstRowNode = dojo.create("div");
                    	     dojo.place(firstRowNode, itemDetailsDivNode);
                    	  
                    	     var itemTitleNode = dojo.create("h3", { innerHTML: item.label });
                    	     dojo.addClass(itemTitleNode, "itemTitleClass");
                    	     dojo.place(itemTitleNode, firstRowNode);
                    	  
                   	  
                            var itemDateNode = dojo.create("div", { innerHTML:  
                                            item.fromDate+"-"+item.toDate });
                    	     dojo.addClass(itemDateNode, "itemDateClass");
                    	     dojo.place(itemDateNode, itemDetailsDivNode);
                    	                  	  
                    	     var itemSummarySpanNode = dojo.create("div", { innerHTML: 
                    	    	 "<a href=#>More Information</a> "});
                    	     dojo.addClass(itemSummarySpanNode, "itemSummaryClass");
                    	     dojo.place(itemSummarySpanNode, itemDetailsDivNode);
                    	  
                              
    
                            //Create the buttons but hide them 
                    	     var childButton = new dijit.form.Button(
                    	     {id:"button"+i+"_"+this.portlet.namespace,
                    	     label:"+ Add To Calendar"});
    
                    	     var buttonNode = dojo.create("div",{align : "right"});
                       	     dojo.addClass(buttonNode, "buttonClass");
                    	   
    
                    	     dojo.place(buttonNode, childWidget.id);
                    	     dojo.place(childButton.domNode, buttonNode);
                    	   
                            dojo.style("button"+i+"_"+this.portlet.namespace,
                            "visibility","hidden");
                            //End code to create the buttons but hide them                 	   
                    	  
                      }
                       /***Associate event handlers with buttons
                        ** Clicking on a button will invoke a function named  
                        ** openCalendar
                       */
                       for(var i = 0; i<len; i++){
                    	   dojo.connect(this.portlet.byId("button"+i+"_"+this.portlet.namespace
                           ), "onclick", this, "openCalendar");
                    	}
                      
    		  })
                });

    This code utilizes various Dojo mobile widgets to create a list from the data that's fetched from the JSON file you created earlier. The code includes the programmatic creation of list items and of individual list child elements, such as label, button, and so on. It also programmatically applies CSS styles (defined in the CSS file that you will create in the next step) to individual elements. It will finally create the UI that is shown in Figure 2.

    You will also find that this code programmatically creates Dojo mobile buttons and hides them (under the commented section “Create the buttons but hide them”). You will unhide these buttons later and make use of them to invoke native device functionality (while creating hybrid applications in Worklight).

  11. Select the WebContent folder in the EventsSample project and choose File > New > CSS File. This CSS file will define all the styles used by the Dojo mobile widgets created in Listing 5.
  12. Name the file styles.css and click Finish.
  13. Open the styles.css file, then copy and paste the contents from Listing 6.
    Listing 6. Contents of styles.css file
    	 @CHARSET "ISO-8859-1";
    
        .listClass {
          margin: 0;
          padding: 5px;
         }
        .itemDetails {
          overflow: hidden;
          font-size: 12px;
         }
        .itemTitle {
          color: #115D94;
          text-decoration: none;
          font-size: 1em;
        }
        .itemSummaryClass {
         color: #444444;
         font-size: 1em;
         line-height: 1.4;
         padding-top: 10px; 
        }
        .item {
         list-style-type: none;
        }
        .itemLink {
         font-size: 0.9em;
         padding-top: 12px;
        }
        .listStyle{
         border-top: 1px solid #D9D9D9;
         list-style-type: none;
         padding: 10px 0;
         position: relative;
        }
       .itemImage {
         border: 4px solid white;
         margin-right: 2%;
         width: 22%;
         float: left;
         margin-top: 4px;
         overflow: hidden;
         border: 0 none;
         vertical-align: middle;
        }
        .itemDate {
         color: #676767;
         font-size: 1em;
         margin-top: -2px;
        }
    
        .listLastLi{
          border-bottom:1px solid #D9D9D9;
         }
    
        .buttonClass{
          padding-right:5px;
         }
    
        .blockHeading {
          border-bottom: medium solid;
          margin-bottom: 12px;
          background: none repeat scroll 0 0 transparent;
          box-shadow: none;
          color: #115D94;
          font-family: Georgia,"Times New Roman",Times,serif;
          font-size: 1.8em;
          font-weight: normal;
          line-height: 1.3;
          margin: 0;
        }
        .mobile body{
          font: 75%/1.5 Arial,Helvetica,sans-serif;
        }
  14. Open the EventsSamplePortletView_Devices.jsp file in the Source view and locate the comment <!-- include PortletHelper class and create object -- >
  15. Add the code from Listing 7 immediately after this comment line.
    Listing 7. Add the reference of styles.css
    <link rel="stylesheet"
    	href='<%= renderResponse.encodeURL(renderRequest.getContextPath() + "/styles.css")  
    %>' />

    This line basically adds the reference to the styles.css file that you created earlier.

  16. In the EventsSamplePortletView_Devices.jsp file, locate the dojo.addOnLoad statement and, in the same block, update the code such that it looks like Listing 8.
    Listing 8. Add the reference of styles.css
    	dojo.addOnLoad( function() {
              portletHelper_<portlet:namespace/> = new  
              portlet.namespace_64a58d5ce3.portlethelper.
              EventsSamplePortletView_Devices({portlet: portlet_<portlet:namespace/>});
              portletHelper_<portlet:namespace/>.createUI();
        } );

    This code invokes the function that you coded in step j. This will lead to the creation of the desired UI.

  17. Save the file.
  18. Right-click on the EventsSample project and choose Run As > Run on Mobile Browser Simulator. You should now be able to view the UI as shown in Figure 1.

4. Develop UI for desktop

Now that you have created the smartphone UI, you will develop the UI for desktop viewing. This view will be rendered whenever a portal page containing the portlet you're developing is accessed from a desktop browser. As shown in Figure 2, the desktop based UI will have the same UI as the smartphone. This means that the JavaScript code to generate the UI would be similar to that which you already developed for smartphones. The difference would be the default visibility of the Add to Calendar button, and the action that clicking that button would invoke.

  1. Open the Enterprise Explorer view and right-click on the EventsSamplePortletView.jsp file. Select Open With > Rich Page Editor. This will open the portlet jsp in Rich Page Editor.
  2. Click on the Source View tab and add <div id="list_<portlet:namespace/>"></div> as child of the container element with id widgetContainer_<portlet:namespace/>. The code after the change will look like that shown in Listing 9.
    Listing 9. Container for desktop UI
    <div id="widgetContainer_<portlet:namespace/>">
    	<div id="list_<portlet:namespace/>"></div>
    </div>

    The container element with id=list_<portlet_namespace/> will contain the Dojo mobile list items that you will be creating in the next steps.

  3. Add the dojo.require statements dojo.require("dojox.mobile.ListItem") and dojo.require("dojox.mobile.Button") under the script tag that already contains several dojo.require statements. The resulting dojo.require code looks like that shown in Listing 10.
    Listing 10. Dojo require statements
    	<script type="text/javascript">
          dojo.require("dojo.parser");
          dojo.require("dojox.mobile.ListItem");
          dojo.require("dojox.mobile.Button");
        </script>
  4. Open the file WebContent\js\portlet\<namespace>\portlethelper\EventsSamplePortletView.js.
  5. Repeat steps i and j from the Develop UI for smartphones section to define a createUI function and provide its implementation code.
  6. Remove this code from the createUI function:

    dojo.style("button"+i+"_"+this.portlet.namespace, "visibility","hidden");

    This resulting code will again utilize various Dojo mobile widgets to create a list from the data that's fetched from the JSON file that you created earlier. The code also includes programmatic creation of list items and creation of individual list child elements like label, button, and so on. This code will create the UI that is shown in Figure 2. Removing the above line of code now makes the buttons visible in the UI.
  7. Repeat steps n through p from the Develop UI for smartphones section.
  8. Save the file.
  9. Run this project on the server by right-clicking on the project and choosing Run As > Run On Server. This will display the desktop view (Figure 2).

5. Enable calendar function for desktop

Now that you have created the desktop UI, you can enable the Add to Calendar function that will enable you to download an iCalendar (.ics) file that can be opened through your preferred desktop calendar program.

If you review the createUI function in the EventsSamplePortletView.js file, you will find that it contains the code shown in Listing 11.

Listing 11. Associating function with handler
       for(var i = 0; i<len; i++){
         dojo.connect(this.portlet.byId("button"+i+"_"+this.portlet.namespace
                       ), "onclick", this, "openCalendar");
       }

This code associates an onclick event handler function by the name openCalendar with the button. You can now implement the function openCalendar.

  1. Open EventsSamplePortletView.js file and paste the code shown in Listing 12.
    Listing 12. Implement openCalendar function
    	openCalendar : function(args){
    		var selectedListItem = 
    		this.portlet.byDijitId(args.target.offsetParent.id);
    		
    		if(selectedListItem == null || selectedListItem == 'undefined')
    		selectedListItem = 
    		this.portlet.byDijitId(args.target.parentElement.parentElement.id);		    
                 
                 var fDate = selectedListItem.get("fromDate");
    		var fromDate = new Date(fDate);
    		var fDay =  fromDate.getDate();
    		var fMonth = fromDate.getMonth();
    
    		if(fMonth == 11)
    			fMonth = 12;
    		else 
    			fMonth = fMonth + 1;
    
    		var fYear = fromDate.getFullYear();
    
    		fMonth = fMonth+"";
    		var fmonthLength =  fMonth.length;
    		if(fmonthLength == 1){
    			fMonth = "0"+fMonth;
    		}
    		var dtStart = fYear+""+fMonth+""+fDay;
    		dtStart = dtStart+"T080000";
    		var tDate = selectedListItem.get("toDate");
    		var toDate = new Date(tDate);
    		var tDay =  toDate.getDate();
    		var tMonth = toDate.getMonth();
    		if(tMonth == 11)
    			tMonth = 12;
    		else 
    			tMonth = tMonth + 1;
    
    		tMonth = tMonth+"";
    		var tmonthLength =  tMonth.length;
    		if(tmonthLength == 1){
    			tMonth = "0"+tMonth;
    		}
    		var tYear = toDate.getFullYear();
    
    		var dtEnd = tYear+""+tMonth+""+tDay;
    		dtEnd = dtEnd+"T180000";
    		var subject = selectedListItem.get("mylabel");
    		var description = selectedListItem.get("description");
    		var location = selectedListItem.get("location");
    
    		var uid = new Date(fDate);
    
    		var msg = "BEGIN:VCALENDAR\nPRODID:-//Microsoft Corporation//Outlook 11.0"+  
                "MIMEDIR//EN\nVERSION:2.0\nMETHOD:PUBLISH\nBEGIN:VEVENT\nDTSTART:" +  
                dtStart +"\nDTEND:" + dtEnd +"\nLOCATION:" + location 
                +"\nTRANSP:OPAQUE\nSEQUENCE:0\nUID:" + uid +"\nDTSTAMP:" + uid            
                +"\nCATEGORIES:Conference\nDESCRIPTION:" + description 
                +"\nSUMMARY:" + subject +"\nPRIORITY:5\nX-MICROSOFT-CDO-"+
                "IMPORTANCE:1\nCLASS:PUBLIC\nBEGIN:VALARM\nTRIGGER:-"+
                "PT60M\nACTION:DISPLAY\nDESCRIPTION:Reminder\nEND:VALARM\nEND:VEVENT"+
                "\nEND:VCALENDAR";
    		window.open( "data:text/calendar;charset=utf8," + escape( msg ) );
    	}
  2. Now, when you click on the Add to Calendar button, this code gets start date and end date from the selected list item. It then fetches the day, month, and year from the start and end dates, and dynamically creates an iCalendar format.
  3. Save the file.
  4. Run this project on the server by right-clicking on the project and choosing Run As > Run On Server. This will show you the desktop view (Figure 2). Clicking on Add to Calendar will download individual .ics (iCalendar) files that can be opened using a desktop calendar program, such as Microsoft® Outlook® (Figure 9).
    Figure 9. UI showing implemented Add to calendar functionality
    UI showing implemented Add to calendar functionality

Create a hybrid application with native functionality

At this point, you have developed a mutli-channel portlet application. You will now create a hybrid application with Worklight that will achieve native device functionality for your application. At a high level, this requires that you:

  1. Create Worklight hybrid application
  2. Update application descriptor to fetch content from WebSphere Portal
  3. Create Apache Cordova native plug-in
  4. Update portlet JSP to make buttons visible
  5. Invoke Apache Cordova native plug-in

6. Create Worklight hybrid application

Follow the instructions in the Create the Worklight application section of this article to create a Worklight hybrid application. Be sure to use these values for this exercise:

  1. Step 1. Create the worklight project: Name the project PortalWL.
  2. Step 2. Create the worklight hybrid application: Name the application PortalEvent.
  3. Step 3. Add Android environment.

7. Update application descriptor to fetch content from WebSphere Portal

Next, you will change to the hybrid application descriptor file so that the hybrid application fetches its content from WebSphere Portal.

  1. Open the application-descriptor.xml file located in the PortalWL\apps\PortalEvent folder.
  2. Open the Source tab.
  3. Update the value of the mainfile attribute to http://<yourip>:<yourport>/wps/myportal. The final contents of the application-descriptor.xml should look like that shown in Listing 13.
    Listing 13. Import statements
    <?xml version="1.0" encoding="UTF-8"?>
    <application   
             xmlns="http://www.worklight.com/application-descriptor" id="PortalEvent"   
             platformVersion="6.0.0">
              <displayName>PortalEvent</displayName>
        <description>PortalEvent</description>	
        	    <author>
               <name>application's author</name>
               <email>application author's e-mail</email>
               <homepage>http://mycompany.com</homepage>
               <copyright>Copyright My Company</copyright>
               </author>
               <mainFile>http://9.182.230.90:10039/wps/myportal</mainFile>
               <features>
                 <JSONStore/>
                </features>
               <thumbnailImage>common/images/thumbnail.png</thumbnailImage>
               <android version="1.0">
                <worklightSettings include="true"/>
                 <security>
                  <encryptWebResources enabled="false"/>
                  <testWebResourcesChecksum enabled="false" ignoreFileExtensions="png, jpg,  
                   jpeg, gif, mp4, mp3"/>
                  <publicSigningKey>Replace this text with the public key of the certificate   
                   with which you sign the APK. For details see the Worklight Developer's   
                   Reference Guide.</publicSigningKey>
                  </security>
                </android>
      </application>

    By specifying the appropriate value of the mainFile in the above code, your hybrid application loads in web content from the portal runtime. You'll need to change the IP address and port number to reflect your portal server’s IP address and port.

  4. Right-click on the PortalEvent folder in the apps folder and select Run As > Build All and Deploy, as shown in Figure 10.
    Figure 10. Build All and deploy
    Build All and deploy

    This will deploy the application to all environments that you have selected. In this case, it is only Android. If all goes well, the console will display:

    Application 'PortalEvent' deployed successfully with all environments.

    This will also update the project PortalWLPortalEventAndroid with new artifacts.

  5. To run the application, right-click on the project PortalWLPortalEventAndroid and choose Run As > Android Application, as shown in Figure 11.
    Figure 11. Running the android hybrid application
    Running the android hybrid application

    This will invoke the Android emulator that will display the portal login page.

    The Android emulator is a virtual mobile device that runs on your computer. The emulator lets you test Android applications without using a physical device. You can also deploy and test your Android applications on a physical device by connecting it to the computer with a USB cable. (For more information see this page on Using Hardware Devices.

  6. Enter your portal credentials. The portal page will display (Figure 12).
    Figure 12. Portlet content displayed in the hybrid app
    Portlet content displayed in the hybrid app

8. Create Apache Cordova native plug-in

Next, you will create the plug-in that will implement the functionality to invoke the native calendar application when you click the Add To Calendar button in the portal UI.

  1. Right-click on the package com.PortalEvent in the Android project named PortalWLPortalEventAndroid. Select New > Class.
  2. Specify the Name of the class as CalendarPlugin and the Superclass as org.apache.cordova.api.CordovaPlugin (Figure 13).
    Figure 13. New class creation dialog
    New class creation dialog
  3. Click Finish.
  4. Open CalendarPlugin.java and paste the code from Listing 14. This code implements the execute method that will be invoked when the Add To Calendar button is clicked.
    Listing 14. Implementation of execute method in the android plugin class
    	@Override
        public boolean execute(String action,  
    			JSONArray arguments, CallbackContext callbackContext) {
    		PluginResult result = null;
    
    		if (action.equals("addEvent")) {
    			try{
    				int fDay = Integer.parseInt(arguments.getString(0));
    				int fMonth = Integer.parseInt(arguments.getString(1));
    				int fYear = Integer.parseInt(arguments.getString(2));
    
    				int tDay = Integer.parseInt(arguments.getString(3));
    				int tMonth = Integer.parseInt(arguments.getString(4));
    				int tYear = Integer.parseInt(arguments.getString(5));
    
    				Calendar beginTime = Calendar.getInstance();
    				beginTime.set(fYear, fMonth, fDay, 5, 30);
    				Calendar endTime = Calendar.getInstance();
    				endTime.set(tYear, tMonth, tDay, 9, 30);
    
    				Uri uri = 
                            Uri.parse("content://com.android.calendar/events");
    				
                            Intent calIntent = new  
                            Intent("android.intent.action.INSERT", uri);
    				calIntent.setAction(Intent.ACTION_INSERT);
    
    				calIntent.putExtra(Events.TITLE, arguments.getString(6));
    			      calIntent.putExtra(CalendarContract.EXTRA_EVENT_ALL_DAY,                 
                            true);
    				calIntent.putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME,  
                              beginTime.getTimeInMillis());
    				calIntent.putExtra(CalendarContract.EXTRA_EVENT_END_TIME,  
                              endTime.getTimeInMillis());
    
    				this.cordova.getActivity().startActivity(calIntent);
    
    				callbackContext.success("Success");
    				return true;
    			} 
    			catch (Exception ex) {
    				callbackContext.error("Not done");
    				return false;
    			}
    		} 
    		else
    			result = new PluginResult(PluginResult.Status.INVALID_ACTION);
    		      callbackContext.error("Not done");
    		return false;
    	}

    This code creates calendar objects for the start and end times of the event, and also creates an Android specific Intent object. These objects are all used to start an activity that invokes the native calendar application.

    The JavaScript wrapper that you will create later will invoke the execute method of the plug-in. The JavaScript will pass the action type as addEvent, and also pass multiple parameters, such as title of the event, start day, start month, start year, end day, end month, and end year of the event.

    All the parameters are passed from the JavaScript to the native plug-in in a JSONArray object.

  5. Press Ctrl+O to add necessary import statements.
  6. Save the file.
  7. Open file the config.xml file in the PortalWLPortalEventAndroid/res/xml folder.
  8. Add this line of code under the plugins section:

    <plugin name="CalendarPlugin" value="com.PortalEvent.CalendarPlugin"/>

  9. Save and close the file.

9. Update portlet JSP to make buttons visible

You will now unhide the buttons that were programmatically hidden in the Develop UI for smartphones section. These buttons should be visible only when the portal page is accessed through the native shell; that is, through a hybrid application installed on a device.

  1. Open the EventsSamplePortletView_Devices.js file and paste the code shown in Listing 15.
    Listing 15. Implementation of execute method in the android plugin class
    makeButtonsVisible:function(){
        	url = this.portlet.contextPath+"/events.json";
        	dojo.xhrGet( {
            url: url,
    	  handleAs: "json",
    	  headers: {"If-Modified-Since":0},
    	  load: dojo.hitch(this,function(response){
    	  var len = response.items.length;
            for(var i = 0; i<len; i++){
              dojo.style("button"+i+"_"+this.portlet.namespace,"visibility","visible");
             }
             })
            });
        	
    }

    This code unhides the buttons that were previously hidden.

  2. Open EventsSamplePortletView_Devices.jsp.
  3. Paste this line of code under the dojo.addOnload call:

    document.addEventListener("deviceready", onDeviceReady, false);

    This line of code adds an event listener function for the event deviceready. This event, provided by the Cordova API, is fired when you access the application in a mobile device. (Check the Apache Cordova documentation for more information on the deviceready event.)

  4. Paste the code from Listing 16 inside a script tag in the portlet JSP.
    Listing 16. Implementation of deviceready event listener function
    function onDeviceReady() {
    	portletHelper_<portlet:namespace/>.makeButtonsVisible();
    }

    This function invokes the makeButtonsVisible function.

10. Invoke Apache Cordova native plug-in

Now you need JavaScript code that will invoke (onclick of Add to calendar button) the Cordova plug-in that you created earlier.

  1. Open the EventsSamplePortletView_Devices.js file and implement the function openCalendar. This function is fired on click of the Add To Calendar button. This function is bound to the onclick event in createUI function that was explained earlier. Paste the code from Listing 17.
    Listing 17. Implementing openCalendar function for the hybrid application use-case
    	openCalendar : function(args){
    		var dataArray = [];
    		 
    		var selectedListItem =   
                 this.portlet.byDijitId(args.target.parentElement.parentElement.id);
    		
    		var fDate = selectedListItem.get("fromDate");
    		var fromDate = new Date(fDate);
    		var fDay =  fromDate.getDate();
    		var fMonth = fromDate.getMonth();
    		var fYear = fromDate.getFullYear();
    		
    			
    		dataArray.push(fDay);
    		dataArray.push(fMonth);
    		dataArray.push(fYear);
    		
    		var tDate = selectedListItem.get("toDate");
    		var toDate = new Date(tDate);
    		var tDay =  toDate.getDate();
    		var tMonth = toDate.getMonth();
    		var tYear = toDate.getFullYear();
    		
    		dataArray.push(tDay);
    		dataArray.push(tMonth);
    		dataArray.push(tYear);
    		
    		dataArray.push(selectedListItem.get("mylabel"));
    		
    	    cordova.addConstructor(
    	    function() {
    		 if (!window.plugins) window.plugins = {};
    		    window.plugins.CalendarPlugin  = new CalendarPlugin();
    		});
    		
    	    this.invokeCal(dataArray);
    		
    	}

    When you click on the Add to Calendar button, this code gets the start and end dates from the selected list item. It then fetches the day, month, and year from the start and end dates and invokes the Cordova plug-in that you created earlier.

  2. Add the function as shown in Listing 18 outside the dojo class in the EventsSamplePortletView_Devices.js file.
    Listing 18. Declare plug-in in the DOM and creating a wrapper
    	//This creates an empty function that serves as a wrapper for the plug-in
    function CalendarPlugin(){
        }
        //create a invokeCal function by using CalendarPlugin prototype and hardcoded  
        //plug-in class name and action. It will invoke the plug-in by using 
        //cordova.exec()
        CalendarPlugin.prototype.invokeCal = function(onSuccess, onFailure, dataArray){
    	cordova.exec(onSuccess, onFailure, "CalendarPlugin", "addEvent", dataArray);
        };

    The intent of this JavaScript code is explained in the comments.

  3. Add the code from Listing 19 inside the dojo class in the file EventsSamplePortletView_Devices.js.
    Listing 19. Implementing callback and plug-in invocation functions
    	invokePlugin : function(dataArray){
    	window.plugins.CalendarPlugin.invokeCal(this.success, this.failure, dataArray);
        },
        success : function(data){
    	alert("Done : " + JSON.stringify(data));
        },
        failure:function(data){
    	alert("Sorry, failed : " + JSON.stringify(data));
        }
  4. Finally, run the Android project in the Android simulator by right-clicking on the project PortalWLPortalEventAndroid and select Run As > Android Application (Figure 11). You would now get the functionality as shown in Figure 14.
    Figure 14. Android events hybrid application
    Android events hybrid application

About the sample files

This article has two separate ZIP files. One is the sample portlet project and another is the sample Worklight project. To use these projects, you need to import these files into your Rational Application Developer workspace. Deploy the project named EventSamples into your portal server before running the Worklight hybrid application. Also, make sure that you have performed all the prerequisite steps.


Conclusion

This article guided you through the creation of an IBM WebSphere Portal based multi-channel portlet application. You also created an Android mobile hybrid application using IBM Worklight that fetched its UI contents from the portlet application. Finally, you added native functionality to create a true hybrid application that could also access device features. This is a recommended pattern for creating hybrid applications that gets you the best of both native mobile applications and mobile web applications. Both Rational Application Developer and Worklight work in tandem in a shared shell environment to give you a single window development environment that helps you create this application end to end, including testing and debugging it on both WebSphere Portal and the Android simulator of your choice.


Downloads

DescriptionNameSize
Sample projectEventsPortletProject.zip109 KB
Sample projectEventsWorklightProject.zip5.1 MB

Resources

Learn

Get products and technologies

Discuss

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into Mobile development on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Mobile development, WebSphere
ArticleID=947269
ArticleTitle=Deliver an exceptional mobile web experience using WebSphere Portal and IBM Worklight, Part 5: Integrating an event-based portlet with a hybrid mobile application
publish-date=10092013