<!DOCTYPE html>
<html>
<head>
<title>Google Map Example</title>
<style type="text/css">
html { height: 100% }
body { height: 100%; margin: 0; padding: 0 }
#map_canvas { height: 100% }
</style>
<script type="text/javascript" charset="utf-8" src="cordova-1.7.0.js"></script>
<script type="text/javascript" charset="utf-8" src="jquery.js"></script>
<script src="http://maps.google.com/maps/api/js?sensor=true"></script>
<script type="text/javascript" charset="utf-8">
function displayCurrentLocation(){
console.log("Entering displayCurrentLocation()");
try{
var currentLocationLatAndLong = new google.maps.LatLng(37.422006,-122.084095);
var mapOptions ={
zoom:8,
center:currentLocationLatAndLong,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
var mapDiv = document.getElementById("map");
map = new google.maps.Map(mapDiv,mapOptions);
}catch(e){
console.log("Error occured in ConsultantLocator.displayMap() " + e);
}
console.log("Exiting displayCurrentLocation()");
}
function addMarker(latLng,title,contentString){
console.log("Entering addMarker()");
var markerOptions = new google.maps.Marker({
map: map,
position: latLng,
title:title,
clickable:true
});
var marker = new google.maps.Marker(markerOptions);
var infoWindowOptions = {
content: contentString,
position: latLng
};
var infoWindow = new google.maps.InfoWindow(infoWindowOptions);
google.maps.event.addListener(marker, "click", function(){
infoWindow.open(map);
});
console.log("Exiting addMarker()");
}
function getLatLangFromAddress(address){
console.log("Entering getLatLangFromAddress()");
var geocoder = new google.maps.Geocoder();
geocoder.geocode( { 'address': address}, function(results, status) {
if (status == google.maps.GeocoderStatus.OK) {
var returnedValue =results[0].geometry.location;
console.log("Address found is " + returnedValue);
addMarker(returnedValue);
}else{
alert("Geocode was not successful for the following reason: " + status);
}
});
console.log("Exiting getLatLangFromAddress()");
}
function addMarkerForAddress(){
console.log("Entering addMarkerForAddress()");
var address = $("#address").val();
console.log($("#address"));
var latLangForLocation = getLatLangFromAddress(address);
console.log("Value returned by getLatLangFromAddress " +latLangForLocation);
addMarker(latLangForLocation,address,address);
console.log("Exiting addMarkerForAddress()");
}
document.addEventListener("deviceready", displayCurrentLocation, false);
</script>
</head>
<body >
<p>
<input type="text" name="address" id="address" />
<input type="button" id="getLocation" onclick="addMarkerForAddress()" value="Get Location"/>
</p>
<div id="map" style="width:100%; height:100%"></div>
</body>
</html>
This application loads the google map during startup. When user enters address and clicks on get location it takes the address and uses it to find the latitude and longitude for that address and then users addMarker() method to display maker for that location
Google Map in PhoneGap/Cordova application
I wanted to figure out how to use Google Maps API in the Phone GAP application so i built this sample application that lets you enter address and it displays that address on the Map, you can download the sample application from here
This is a screen shot of how my application looks like
This is how the index.html page for my application looks like
Using ripple mobile browser emulator for testing PhoneGap
One of the biggest pain point for testing PhoneGap application is deploying it on emulator and testing it, that process takes long time. So i started using the Ripple which is Google Chrome extension and it makes testing PhoneGap application really easy.
- Install Ripple extension in Chrome
- Start the Google Chrome browser with its access to local file system by executing
chrome.exe -–allow-file-access-from-files
- Then Right click on the Ripple symbol and say Manage Google Extensions, on the next screen check
Allow access to file URLs
check box - Now open index.html from the phoneGap application using file URL and enable Ripple for it
- Now you can test the geolocation application like this. With Ripple advantage is you can directly open the HTML in browser and then set geolocation directly using Ripple
Using Geolocation API in Android Emulator
In the Getting address of the current location using GeoLocation API and Google MAP api entry i talked about how to use the GeoLocation API provided as part of HTML 5 to get the current address of the user. Now Phone Gap also provides support for geolocation which means it checks if the browser on the device has support for geolocation if yes it lets it work if not it will call the native API of the underlying browser to get the the location and return it to browser. I wanted to try that so i took the content of
geolocation.html
and copied it in the index.html file that my phonegap application for android is using, you can download that app from here
I followed these steps to build Android PhoneGap application
- I followed the instructions in Getting Started with Android to build application that points to index.html inside the application, i tried it once to make sure that it works
- Then i copied the content of Geolocation.html in the index.html page
- Change the AndroidManifest.xml file to allow application to use the mock location
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.webspherenotes.phonegap" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="8" /> <uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.VIBRATE" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" /> <uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION" /> <uses-permission android:name="android.permission.READ_PHONE_STATE" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.RECEIVE_SMS" /> <uses-permission android:name="android.permission.RECORD_AUDIO" /> <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" /> <uses-permission android:name="android.permission.READ_CONTACTS" /> <uses-permission android:name="android.permission.WRITE_CONTACTS" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.GET_ACCOUNTS" /> <uses-permission android:name="android.permission.BROADCAST_STICKY" /> <application android:icon="@drawable/ic_launcher" android:label="@string/app_name" > <activity android:name=".HelloPhoneGapActivity" android:label="@string/app_name" android:configChanges="orientation|keyboardHidden" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
- The android emulator does not know how to find out current location of the user, so we have to setup mock location i.e. hard code a location for the user. For that you have two options either use the DDMS perspective in your Eclipse and after your emulator is started select it and enter value of longitude and latitude and click on send like this.
-
Or open a Telnet session to the emulator by executing
telnet localhost 5554
(you can get port number 5554 from the emulator window it would be in title bar) and then in the telnet window usegeo fix -121.9754144 37.5669038
command to set the longitude and latitude like this
Getting address of the current location using GeoLocation API and Google MAP api
The HTML 5 has concept of Geo Location that lets you read current address of the user, So if your using a mobile device it will give you current address using GPS but if your using normal Laptop or Desktop it still gives you current address with less accuracy. In my case it gives address of AT&T office which is my internet provider. I wanted to figure out how GeoLocation API's work so i build this sample application that displays my current address. You can download the Geolocation.html file from here
This is how my Geolocation.html page looks like in browser
My Geolocation.html page has one
Get Location
button when you click on that button it will show you address of the current location. Now the address is not always accurate. Also i tested this code in Firefox 12.0 and Internet Explorer 9, the Geolocation code does not work in Chrome if your accessing the file directly from file system i mean using file://
URL.
This is the source code for the Geolocation.html
<!DOCTYPE html>
<html>
<head>
<title>GeoLocation</title>
<script src="http://maps.google.com/maps/api/js?sensor=true">
</script>
<script type="text/javascript" charset="utf-8">
function getLocation(){
console.log("Entering getLocation()");
if(navigator.geolocation){
navigator.geolocation.getCurrentPosition(
displayCurrentLocation,
displayError,
{
maximumAge: 3000,
timeout: 5000,
enableHighAccuracy: true
});
}else{
console.log("Oops, no geolocation support");
}
console.log("Exiting getLocation()");
};
function displayCurrentLocation(position){
console.log("Entering displayCurrentLocation");
var latitude = position.coords.latitude;
var longitude = position.coords.longitude;
console.log("Latitude " + latitude +" Longitude " + longitude);
getAddressFromLatLang(latitude,longitude);
console.log("Exiting displayCurrentLocation");
}
function displayError(error){
console.log("Entering ConsultantLocator.displayError()");
var errorType = {
0: "Unknown error",
1: "Permission denied by user",
2: "Position is not available",
3: "Request time out"
};
var errorMessage = errorType[error.code];
if(error.code == 0 || error.code == 2){
errorMessage = errorMessage + " " + error.message;
}
alert("Error Message " + errorMessage);
console.log("Exiting ConsultantLocator.displayError()");
}
function getAddressFromLatLang(lat,lng){
console.log("Entering getAddressFromLatLang()");
var geocoder = new google.maps.Geocoder();
var latLng = new google.maps.LatLng(lat, lng);
geocoder.geocode( { 'latLng': latLng}, function(results, status) {
console.log("After getting address");
console.log(results);
if (status == google.maps.GeocoderStatus.OK) {
if (results[1]) {
console.log(results[1]);
alert(results[1].formatted_address);
}
}else{
alert("Geocode was not successful
for the following reason: " + status);
}
});
console.log("Entering getAddressFromLatLang()");
}
</script>
</head>
<body>
<h1>Display the map here</h1>
<input type="button" id="getLocation"
onclick="getLocation()" value="Get Location"/>
<div id="map"></div>
</body>
</html>
When you click on Get Location button the control goes to getLocation()
function which first checks if the browser supports GeoLocation API by checking navigator.geolocation
object, if that object is not null that means browser supports GeoLocation and we ask browser for current location by calling navigator.geolocation.getCurrentPosition()
with displayCurrentLocation()
as a call back function if the current location lookup was successful.
The browser calls displayCurrentLocation()
function with current location using position object, which has longitude and latitude as properties. The latitude and longitude would have values like Latitude 37.5668988 Longitude -121.9753273
, so we have to use the Google MAP API to get street address from the longitude and latitude values, for that we call getAddressFromLatLang
function.
Inside the getAddressFromLatLang()
function i am creating object of google.maps.Geocoder
and calling its geocode()
method with latitude and longitude and it returns array of addresses for that location. Once i have the address i can print it using alert.
Using JQuery Mobile in PhoneGap/Cordova application
JQuery Mobile is one of the most popular Mobile UI frameworks, i wanted to figure out how to use it in Cordova application so i built a sample application that uses JQuery Mobile and also the Cordova device API to display device related properties in JQuery Mobile UI. You can download the sample application from here. I followed these steps to build this application
- I followed the instructions in Getting Started with Android to build application that points to index.html inside the application, i tried it once to make sure that it works
- I did download the
jquery.mobile-1.1.0.zip
from JQuery Mobile Download page - I did unzip the
jquery.mobile-1.1.0.zip
inc:\software
folder - Next i copied the css, docs and js folder from
C:\software\jquery.mobile-1.1.0\demos
into assets/www folder of my web application. - If your just starting with JQuery mobile or playing around then you can even directly point to the CDN URL of JQuery Mobile without actually downloading it in your application, in that case you can skip step 2 and 3. You can simply add these lines in your HTML page
<link rel="stylesheet" href="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.css" /> <script src="http://code.jquery.com/jquery-1.6.4.min.js"></script> <script src="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.js"></script>
- Next change your index.html page to look like this
My application has one JQuery Mobile page whose content is defined using<!DOCTYPE html> <html> <head> <title>Device Properties Example</title> <script type="text/javascript" charset="utf-8" src="cordova-1.7.0.js"></script> <!-- <link rel="stylesheet" href="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.css" /> <script src="http://code.jquery.com/jquery-1.6.4.min.js"></script> <script src="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.js"></script> --> <link rel="stylesheet" href="css/themes/default/jquery.mobile-1.1.0.css" /> <script src="js/jquery.js"></script> <script src="js/jquery.mobile-1.1.0.js"></script> <script type="text/javascript" charset="utf-8"> document.addEventListener("deviceready", onDeviceReady, false); function onDeviceReady() { console.log("Entering index.html.onDeviceReady"); //var element = document.getElementById('deviceProperties'); var html = ""; html = html + "<li>" + 'Device Name: ' + device.name + "</li>"; html = html + "<li>" + 'Device Cordova: ' + device.cordova + "</li>"; html = html + "<li>" + 'Device Platform: ' + device.platform + "</li>"; html = html + "<li>" + 'Device UUID: ' + device.uuid + "</li>"; console.log(html); $("#deviceProperties").html(html); $("#deviceProperties").listview('refresh'); console.log("Exiting index.html.onDeviceReady"); } </script> </head> <body> <div data-role="page" id="page1"> <div data-theme="a" data-role="header"> <h3>Hello JQuery Mobile</h3> </div> <div data-role="content"> Device Properties <ul data-role="listview" data-inset="true" id="deviceProperties"> </ul> </div> <div data-theme="a" data-role="footer"> <h3>Copyright stuff</h3> </div> </div> </body> </html>
div
with id equal topage1
. The div with data-role equal to content defines the content of the page. By default it only has emptyul
element, i am usingonDeviceReady()
JavaScript function to read device property and add them to the list.
Accessing external website from PhoneGap application
I wanted to create a PhoneGap application, which instead of pointing to a HTML page inside the application should open my blog site
http://wpcertification.blogspot.com
these are the steps that i followed to build the application, you can download it from here
- I followed the instructions in Getting Started with Android to build application that points to index.html inside the application, i tried it once to make sure that it works
- Then i changed the HelloCordovaActivity like this so that it points to
http://wpcertification.blogspot.com
Thepackage com.webspherenotes.cordova.sample; import org.apache.cordova.DroidGap; import android.app.Activity; import android.os.Bundle; public class HelloCordovaActivity extends DroidGap { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); super.loadUrl("http://wpcertification.blogspot.com"); } }
onCreate()
method is callingsuper.loadUrl("http://wpcertification.blogspot.com");
so thathttp://wpcertification.blogspot.com
page is opened in the WebView as soon as application starts - The default configuration of the PhoneGap application disallows opening of any external URL so i had to change the cordova.xml like this to allow access to
http://wpcertification.blogspot.com
Take a look at XML comments in this file for syntax on how to allow access to external URLs<?xml version="1.0" encoding="utf-8"?> <cordova> <!-- access elements control the Android whitelist. Domains are assumed blocked unless set otherwise --> <access origin="http://127.0.0.1*"/> <!-- allow local pages --> <access origin="http://wpcertification.blogspot.com" /> <!-- <access origin="https://example.com" /> allow any secure requests to example.com --> <!-- <access origin="https://example.com" subdomains="true" /> such as above, but including subdomains, such as www --> <!-- <access origin=".*"/> Allow all domains, suggested development use only --> <log level="DEBUG"/> <preference name="classicRender" value="true" /> </cordova>
Setting up Android PhoneGap 1.7 development environment
Before few days i did blog about Setting up Android PhoneGap development environment in that case i had PhoneGap 1.6 working on Android 2.2. But then PhoneGap 1.7 came along and the Getting started document document talks about how to setup PhoneGap 1.7 which is now called Apache Cordova with Android 4.0.3.
I followed the steps in the document and was able to get it working, one strange thing was after i said run as Android application it did not work as expected for first couple of times but after couple of times it started working, i am still trying to figure out what happened.
I built this simple application that uses the device API to print device and Cordova related information. This is how my HTML looks like, you can download the source code for the application from here
<!DOCTYPE html>
<html>
<head>
<title>Device Properties Example</title>
<script type="text/javascript" charset="utf-8"
src="cordova-1.7.0.js"></script>
<script type="text/javascript" charset="utf-8">
// Wait for Cordova to load
//
document.addEventListener("deviceready", onDeviceReady, false);
// Cordova is ready
//
function onDeviceReady() {
var element = document.getElementById('deviceProperties');
element.innerHTML = 'Device Name: ' + device.name + '<br />' +
'Device Cordova: ' + device.cordova + '<br />' +
'Device Platform: ' + device.platform + '<br />' +
'Device UUID: ' + device.uuid + '<br />' +
'Device Version: ' + device.version + '<br />';
}
</script>
</head>
<body>
<p id="deviceProperties">Loading device properties...</p>
</body>
</html>
On the deviceready
event i am reading different device properties and displaying them in the page. This is how the page looks like
Creating custom skin for modular theme
In the Create custom Modular Theme i talked about how to create a custom theme based on modular theme architecture, Now i wanted to create a Custom Skin to go along with that theme so i followed these steps.
- First connect to
http://localhost:10039/wps/mycontenthandler/dav/skinlist
WebDAV URL and you will see list of all the existing skins. - Since i want this theme to be very minimal i started with
ibm.portal.7002NoSkin
, so i copied that folder to my local machine like this - Rename the
ibm.portal.7002NoSkin
folder towebspherenotes.firstmodularskin
- Open the
webspherenotes.firstmodularskin\metadata\localized_en.properties
file to rename the theme like this# #Thu May 10 10:41:16 PDT 2012 description=My first modular skin title=First Modular Skin
- Change the content of the theme_en.html to look like this
Basically i did take out most of the code from the skin except the code required for the portlet title and body and i am adding<span class="wpthemeAccess"> <h4><a rel="dynamic-content" href="lm:title"></a></h4> </span> <div class="wpthemeOverflowAuto"> <!-- lm:control dynamic spot injects markup of layout control --> <a rel="dynamic-content" href="lm:control"></a> </div>
First Modular Skin -
to the portlet title - Then copy the
webspherenotes.firstmodularskin
folder back to thehttp://localhost:10039/wps/mycontenthandler/dav/skinlist
like this - Now when you login into Portal admin console you should see the Custom Skin in the list of skins so assign it to either Portal 7002 Theme or some other theme of your choice, i am adding it to Portal 7002 theme like this
Deferred module loading in the default Modular theme in WPS 7002
I am playing around with the Modular Theme in WPS 7002 for last few days and one thing that i noticed is the new theme is quite past compared to the older theme. So i was trying to figure out what is going on and it seems IBM used one simple trick(In addition to lot of more complex stuff like combining of resources,.. etc) to speed up the page.
If you load a portal page in the browser and use firebug to look at the requests you will notice that it is loading 24 files. Please note that i am using Disabling combining of resource for debugging modular theme settings so every file gets loaded separately so that i can debug and figure out what is going on. If you have not disabled combining of resources you will see very few requests but little less amount of data will be downloaded from the server Now if you click on the edit button you will notice that it takes few seconds to load the customization palette and if you look at the firebug you will notice that it downloads 124 resources and close to 1.7MB of data and when you move to options like Change Layout or Change Style you will see few more requests and some more data being downloaded. The Default theme defers loading of edit page related modules which makes sense because the normal user will view page more frequently compared to editing it, some sites wont even allow to edit the page, so why load stuff that is not required and slow down every page.
If you load a portal page in the browser and use firebug to look at the requests you will notice that it is loading 24 files. Please note that i am using Disabling combining of resource for debugging modular theme settings so every file gets loaded separately so that i can debug and figure out what is going on. If you have not disabled combining of resources you will see very few requests but little less amount of data will be downloaded from the server Now if you click on the edit button you will notice that it takes few seconds to load the customization palette and if you look at the firebug you will notice that it downloads 124 resources and close to 1.7MB of data and when you move to options like Change Layout or Change Style you will see few more requests and some more data being downloaded. The Default theme defers loading of edit page related modules which makes sense because the normal user will view page more frequently compared to editing it, some sites wont even allow to edit the page, so why load stuff that is not required and slow down every page.
Creating a Custom Page Layout in WPS 7002
I followed these steps to add a custom page layout in the WPS 7002.
- Create a folder named
CustomLayout
under/fs-type1/themes/webspherenotes.firstmodular/layout-templates/
folder - Copy the layout.html and icon.gif from say 1Column folder and copy it to
CustomLayout
folder after the copy it should look like this - Copy the newly created CustomLayout folder to the
/fs-type1/themes/webspherenotes.firstmodular/layout-templates/
in WebDav storage like this - Change the markup of layout.html in the
CustomLayout
folder to look like this<!-- Entering CustomLayout.layout.html --> <div class="component-container wpthemeCol wpthemeCol-1 ibmDndColumn wpthemeLeft" name="ibmMainContainer"></div> <!-- Entering CustomLayout.layout.html -->
I made changes to take out the section that displays hidden widgets on the page and i also did add the HTML comments to show beginning and end of the layout -
Then open the
/fs-type1/themes/Portal7.0.0.2/system/layouts.json
which holds list of layouts that appear in the change layout panel. change it to add one row for newly create CustomLayout like this{ 'label':'change_layout_CustomLayout', 'url':ibmCfg.themeConfig.themeWebDAVBaseURI+'layout-templates/CustomLayout/', 'id':'CustomLayout', 'thumbnail':ibmCfg.themeConfig.themeRootURI+'/layout-templates/CustomLayout/icon.gif', 'help':'' }
- After make changes copy the layouts.json file to the WebDAV folder
- Now try opening the change layout UI and you might not see the newly added CustomLayout in the option, or you might if you do you will see option to switch to CustomLayout like this. If you dont see CustomLayout option go to next step
- In my case i was not able to see the option for CustomLayout so i looked in the firebug what is going on and it seems that when i go to Change Layout tab browser makes a request to
http://localhost:10039/wps/mycontenthandler/!ut/p/digest!EWsjUKfQQnHqfLpBEXvbog/dav/fs-type1/themes/Portal7.0.0.2/system/layouts.json
and the browser was using the cached copy for this file and as a result the changes in the layouts.json were not getting reflected. So i did disable the browser caching using firebug like this - After that when i did refresh the page and i could see the new layouts.json being returned by the server and as a result i could see the change custom layout button
Subscribe to:
Posts (Atom)