Skip to content

Instantly share code, notes, and snippets.

@filmaj
Created May 4, 2010 16:47
Show Gist options
  • Save filmaj/389641 to your computer and use it in GitHub Desktop.
Save filmaj/389641 to your computer and use it in GitHub Desktop.
/**
* The MIT License
* -------------------------------------------------------------
* Copyright (c) 2008, Rob Ellis, Brock Whitten, Brian Leroux, Joe Bowser, Dave Johnson, Nitobi
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.nitobi.phonegap.api.impl;
import java.io.ByteArrayInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import javax.microedition.io.Connector;
import javax.microedition.io.HttpConnection;
import javax.microedition.io.StreamConnection;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import net.rim.device.api.system.DeviceInfo;
import net.rim.device.api.system.RadioInfo;
import net.rim.device.api.ui.UiApplication;
import net.rim.device.api.xml.parsers.DocumentBuilder;
import net.rim.device.api.xml.parsers.DocumentBuilderFactory;
import com.nitobi.phonegap.PhoneGap;
import com.nitobi.phonegap.api.Command;
public class NetworkCommand implements Command {
private static final int REACHABLE_COMMAND = 0;
private static final int XHR_COMMAND = 1;
private static final String CODE = "PhoneGap=network";
private static final String NETWORK_UNREACHABLE = ";navigator.network.lastReachability = NetworkStatus.NOT_REACHABLE;if (navigator.network.isReachable_success) navigator.network.isReachable_success(NetworkStatus.NOT_REACHABLE);";
private static final int NOT_REACHABLE = 0;
private static final int REACHABLE_VIA_CARRIER_DATA_NETWORK = 1;
private static final int REACHABLE_VIA_WIFI_NETWORK = 2;
public PhoneGap berryGap;
private ConnectionThread connThread = new ConnectionThread();
public NetworkCommand(PhoneGap gap) {
berryGap = gap;
connThread.start();
}
/**
* Determines whether the specified instruction is accepted by the command.
* @param instruction The string instruction passed from JavaScript via cookie.
* @return true if the Command accepts the instruction, false otherwise.
*/
public boolean accept(String instruction) {
return instruction != null && instruction.startsWith(CODE);
}
public String execute(String instruction) {
switch (getCommand(instruction)) {
case REACHABLE_COMMAND:
if (RadioInfo.isDataServiceOperational()) {
// Data services available - determine what service to use.
int service = RadioInfo.getNetworkType();
int reachability = NOT_REACHABLE;
if ((service & RadioInfo.NETWORK_GPRS) != 0 || (service & RadioInfo.NETWORK_UMTS) != 0 ) {
reachability = REACHABLE_VIA_CARRIER_DATA_NETWORK;
}
if ((service & RadioInfo.NETWORK_802_11) != 0) {
reachability = REACHABLE_VIA_WIFI_NETWORK;
}
return ";navigator.network.lastReachability = "+reachability+";if (navigator.network.isReachable_success) navigator.network.isReachable_success("+reachability+");";
} else {
// No data services - unreachable.
return NETWORK_UNREACHABLE;
}
case XHR_COMMAND:
String reqURL = instruction.substring(CODE.length()+5);
String POSTdata = null;
if (reqURL.indexOf("|") > -1) {
POSTdata = reqURL.substring(reqURL.indexOf("|")+1);
reqURL = reqURL.substring(0,reqURL.indexOf("|"));
}
if (!DeviceInfo.isSimulator()) {
reqURL += ";deviceside=true";
}
connThread.fetch(reqURL, POSTdata);
reqURL = null;
POSTdata = null;
break;
}
return null;
}
private int getCommand(String instruction) {
String command = instruction.substring(CODE.length()+1);
if (command.startsWith("reach")) return REACHABLE_COMMAND;
if (command.startsWith("xhr")) return XHR_COMMAND;
return -1;
}
private void updateContent(final String text)
{
// This will create significant garbage, but avoids threading issues
// (compared with creating a static Runnable and setting the text).
UiApplication.getUiApplication().invokeLater(new Runnable()
{
public void run()
{
berryGap.pendingResponses.addElement(text);
}
});
}
public void stopXHR() {
connThread._stop = true;
}
private class ConnectionThread extends Thread
{
private static final int TIMEOUT = 500; // ms
private String _theUrl;
private String _POSTdata;
private volatile boolean _fetchStarted = false;
public volatile boolean _stop = false;
// Retrieve the URL.
private synchronized String getUrl()
{
return _theUrl;
}
private synchronized String getPOSTdata()
{
return _POSTdata;
}
// Fetch a page.
// Synchronized so that we don't miss requests.
private void fetch(String url, String POSTdata)
{
synchronized(this)
{
_fetchStarted = true;
_theUrl = url;
_POSTdata = POSTdata;
}
}
public void run()
{
for(;;)
{
_stop = false;
// Thread control
while( !_fetchStarted && !_stop)
{
// Sleep for a bit so we don't spin.
try
{
sleep(TIMEOUT);
}
catch (InterruptedException e)
{
System.err.println(e.toString());
}
}
// Exit condition
if ( _stop )
{
continue;
}
// This entire block is synchronized. This ensures we won't miss fetch requests
// made while we process a page.
synchronized(this)
{
String content = "";
HttpConnection httpConn = null;
StreamConnection s = null;
String postData = getPOSTdata();
// Open the connection and extract the data.
try
{
if (postData != null) {
s = (StreamConnection)Connector.open(getUrl(), Connector.READ_WRITE);
} else {
s = (StreamConnection)Connector.open(getUrl());
}
httpConn = (HttpConnection)s;
httpConn.setRequestMethod((postData != null)?HttpConnection.POST:HttpConnection.GET);
httpConn.setRequestProperty("user-agent", "BlackBerry" + DeviceInfo.getDeviceName() + "/" + DeviceInfo.getSoftwareVersion());
httpConn.setRequestProperty("Content-Type","text/plain,text/html,application/rss+xml,application/x-www-form-urlencoded");
if (getUrl().startsWith("http://tcktcktck.org") || getUrl().startsWith("http://new.nitobi.com")) {
httpConn.setRequestProperty("Accept","text/plain,text/html,application/rss+xml,text/javascript,text/xml");
}
httpConn.setRequestProperty("Accept-Charset","UTF-8,*");
if (postData != null) {
httpConn.setRequestProperty("Content-length", String.valueOf(postData.length()));
DataOutputStream dos = httpConn.openDataOutputStream();
byte[] postBytes = postData.getBytes();
for (int i = 0; i < postBytes.length; i++) {
dos.writeByte(postBytes[i]);
}
dos.flush();
dos.close();
dos = null;
}
int status = httpConn.getResponseCode();
if (status == HttpConnection.HTTP_OK)
{
InputStream input = s.openInputStream();
byte[] data = new byte[256];
int len = 0;
int size = 0;
StringBuffer raw = new StringBuffer();
while ( -1 != (len = input.read(data)) )
{
raw.append(new String(data, 0, len));
size += len;
}
content = raw.toString();
raw = null;
// If this is XML, parse it into JSON.
if (content.startsWith("<?xml")) {
content = parseRSS(content);
}
input.close();
input = null;
}
if (_stop) continue;
updateContent(";if (navigator.network.XHR_success) { navigator.network.XHR_success(" + (!content.equals("")?content:"{error:true,message:'Bad server response.',httpcode:" + status + "}") + "); };");
s.close();
}
catch (IOException e)
{
if (_stop) continue;
String resp = e.getMessage().toLowerCase();
if (content.equals("")) {
if (resp.indexOf("tunnel") > -1 || resp.indexOf("Tunnel") > -1 || resp.indexOf("apn") > -1 || resp.indexOf("APN") > -1) {
resp = "{error:true,message:'There was a communication error. Are your APN settings configured (BlackBerry menu -> Options -> Advanced Options -> TCP/IP). Contact your service provider for details on how to set up your APN settings.'}";
} else {
resp = "{error:true,message:'IOException during HTTP request: " + e.getMessage().replace('\'', ' ') + "',httpcode:null}";
}
} else {
resp = content;
}
updateContent(";if (navigator.network.XHR_success) { navigator.network.XHR_success(" + resp + "); };");
resp = null;
} finally {
content = null;
s = null;
httpConn = null;
postData = null;
}
// We're finished with the operation so reset
// the start state.
_fetchStarted = false;
}
}
}
private String parseRSS(String input) {
String content = "";
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = null;
ByteArrayInputStream bs = null;
Document document = null;
NodeList items = null;
String type = null;
String img = null;
boolean parseExtra = false;
String JSON = "[";
try {
builder = factory.newDocumentBuilder();
bs = new ByteArrayInputStream(input.getBytes());
document = builder.parse( bs );
// Determine if parsing Event or Moment feed.
type = ((Element)document.getElementsByTagName("title").item(0)).getChildNodes().item(0).getNodeValue();
if (type.equals("Events")) {
parseExtra = true;
}
items = document.getElementsByTagName("item");
int numItems = items.getLength();
for (int i = 0; i < numItems && i < 4; i++) {
String item = "{";
String theDate = "";
Element curItem = (Element) items.item(i);
NodeList title = curItem.getElementsByTagName("title");
String titleStr = ((Element)title.item(0)).getChildNodes().item(0).getNodeValue();
int pos;
while ( (pos = titleStr.indexOf("'")) > -1 )
{
titleStr = titleStr.substring(0,pos) + "`" + titleStr.substring(pos+1);
}
item += "'title':'" + titleStr + "',";
if (!parseExtra) {
NodeList date = curItem.getElementsByTagName("pubDate");
theDate = ((Element)date.item(0)).getChildNodes().item(0).getNodeValue();
date = null;
}
NodeList desc = curItem.getElementsByTagName("description");
String descStr = ""; //((Element)desc.item(0)).getChildNodes().item(0).getNodeValue();
int lengthOfDesc = ((Element)desc.item(0)).getChildNodes().getLength();
NodeList theElement = ((Element)desc.item(0)).getChildNodes();
if (!parseExtra) {
img = "";
String desc_details = "";
boolean inP = false;
for(int j=0;j<lengthOfDesc;j++)
{
String theValue = theElement.item(j).getNodeValue().trim();
if (theValue.equals("&")) {
continue;
}
if (theValue.toLowerCase().equals("p")) {
inP = true;
desc_details = "<";
}
if (theValue.toLowerCase().startsWith("img src")) {
img = theElement.item(j+2).getNodeValue();
}
if (inP) {
if (theValue.startsWith("amp;")) {
theValue = " & " + theValue.substring(4);
} else if (theValue.startsWith("<")) {
theValue = " " + theValue;
} else if (theValue.startsWith(">")) {
theValue = theValue + " ";
}
desc_details += theValue;
}
}
descStr = desc_details;
} else {
img = "";
String desc_details = "";
String fieldset = "";
boolean inField = false;
boolean inSponsor = false;
boolean inP = false;
for(int j=0;j<lengthOfDesc;j++)
{
String theValue = theElement.item(j).getNodeValue().trim();
if (theValue.equals("&")) {
j++;
continue;
}
if (theValue.toLowerCase().equals("p")) {
inP = true;
desc_details += "<";
}
if (theValue.toLowerCase().startsWith("fieldset class")) {
inField = true;
fieldset = "<";
}
if (theValue.toLowerCase().startsWith("field field-type-nodereference field-field-sponsors")) {
inSponsor = true;
fieldset += "<div id=\"sponsorDiv\" class=\"";
}
if (theValue.toLowerCase().startsWith("/fieldset")) {
inField = false;
fieldset += theValue + ">";
}
if (theValue.toLowerCase().equals("/p")) {
inP = false;
desc_details += theValue + "> ";
}
if (inField || inSponsor ) {
fieldset += theValue;
}
if (inP) {
if (theValue.startsWith("<")) {
theValue = " " + theValue;
} else if (theValue.startsWith(">")) {
theValue = theValue + " ";
}
desc_details += theValue;
}
if (theValue.toLowerCase().startsWith("img src")) {
img = theElement.item(j+2).getNodeValue();
}
if (theValue.toLowerCase().startsWith("date-display-start") || theValue.toLowerCase().startsWith("date-display-separator") || theValue.toLowerCase().startsWith("date-display-end") || theValue.toLowerCase().startsWith("date-display-single")) {
theDate += theElement.item(j+3).getNodeValue();
}
}
if ( (pos = fieldset.indexOf("<div class=\"description\">Event date and location.</div>")) > -1) {
fieldset = fieldset.substring(0, pos) + fieldset.substring(pos+55);
}
descStr = desc_details + fieldset;
}
while ( (pos = descStr.indexOf("'")) > -1 )
{
descStr = descStr.substring(0,pos) + "`" + descStr.substring(pos+1);
}
while ( (pos = theDate.indexOf("'")) > -1 ) {
theDate = theDate.substring(0,pos) + "`" + theDate.substring(pos+1);
}
item += "'description':'" + descStr + "',";
item += "'date':'" + theDate + "',";
NodeList link = curItem.getElementsByTagName("link");
item += "'link':'" + ((Element)link.item(0)).getChildNodes().item(0).getNodeValue() + "'";
if (img != null) {
item += ",'img':'" + img + "'";
}
item += "},";
JSON += item;
item = null;
curItem = null;
title = null;
titleStr = null;
desc = null;
descStr = null;
theElement = null;
link = null;
}
if (JSON.length() > 2) JSON = JSON.substring(0,JSON.length()-1);
JSON += "]";
content = JSON;
} catch (net.rim.device.api.xml.parsers.ParserConfigurationException e1) {
content = "{error:true,message:'XML parser error, ParserConfigurationError: " + e1.getMessage().replace('\'', ' ') + "',httpcode:null}";
} catch (SAXException e) {
content = "{error:true,message:'XML parser error, SAXException: " + e.getMessage().replace('\'', ' ') + "',httpcode:null}";
} catch (IOException e) {
content = "{error:true,message:'XML parser error, IOException: " + e.getMessage().replace('\'', ' ') + "',httpcode:null}";
} finally {
factory = null;
builder = null;
bs = null;
document = null;
items = null;
JSON = null;
type = null;
img = null;
}
return content;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment