You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by bc...@apache.org on 2012/05/15 06:38:01 UTC

[13/15] Updates.

http://git-wip-us.apache.org/repos/asf/incubator-cordova-android/blob/c8fafa6b/framework/src/org/apache/cordova/DroidGap.java
----------------------------------------------------------------------
diff --git a/framework/src/org/apache/cordova/DroidGap.java b/framework/src/org/apache/cordova/DroidGap.java
index 1c832c4..5be0fa9 100755
--- a/framework/src/org/apache/cordova/DroidGap.java
+++ b/framework/src/org/apache/cordova/DroidGap.java
@@ -18,22 +18,24 @@
 */
 package org.apache.cordova;
 
-import java.io.IOException;
-import java.util.ArrayList;
+//import java.io.IOException;
+//import java.util.ArrayList;
 import java.util.HashMap;
-import java.util.Hashtable;
-import java.util.Iterator;
-import java.util.Stack;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import org.apache.cordova.PreferenceNode;
-import org.apache.cordova.PreferenceSet;
+//import java.util.Hashtable;
+//import java.util.Iterator;
+//import java.util.Stack;
+//import java.util.regex.Matcher;
+//import java.util.regex.Pattern;
+
+//import org.apache.cordova.PreferenceNode;
+//import org.apache.cordova.PreferenceSet;
 import org.apache.cordova.api.IPlugin;
 import org.apache.cordova.api.LOG;
 import org.apache.cordova.api.CordovaInterface;
-import org.apache.cordova.api.PluginManager;
-import org.xmlpull.v1.XmlPullParserException;
+import org.json.JSONException;
+import org.json.JSONObject;
+//import org.apache.cordova.api.PluginManager;
+//import org.xmlpull.v1.XmlPullParserException;
 
 import android.app.Activity;
 import android.app.AlertDialog;
@@ -43,10 +45,10 @@ import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.res.Configuration;
-import android.content.res.XmlResourceParser;
+//import android.content.res.XmlResourceParser;
 import android.graphics.Color;
 import android.media.AudioManager;
-import android.net.Uri;
+//import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
 import android.view.Display;
@@ -57,15 +59,13 @@ import android.view.View;
 import android.view.ViewGroup;
 import android.view.Window;
 import android.view.WindowManager;
-import android.webkit.WebChromeClient;
-import android.webkit.WebSettings;
-import android.webkit.WebSettings.LayoutAlgorithm;
-import android.webkit.WebView;
+//import android.webkit.WebChromeClient;
+//import android.webkit.WebSettings;
+//import android.webkit.WebSettings.LayoutAlgorithm;
+//import android.webkit.WebView;
 import android.webkit.WebViewClient;
 import android.widget.LinearLayout;
 
-
-
 /**
  * This class is the main Android activity that represents the Cordova
  * application.  It should be extended by the user to load the specific
@@ -149,10 +149,10 @@ import android.widget.LinearLayout;
  */
 public class DroidGap extends Activity implements CordovaInterface {
     public static String TAG = "DroidGap";
-    
+
     // The webview for our app
     protected CordovaWebView appView;
-    protected WebViewClient webViewClient;
+    protected CordovaWebViewClient webViewClient;
 
     protected LinearLayout root;
     public boolean bound = false;
@@ -162,16 +162,16 @@ public class DroidGap extends Activity implements CordovaInterface {
     // The initial URL for our app
     // ie http://server/path/index.html#abc?query
     private String url = null;
-    private Stack<String> urls = new Stack<String>();
-    
+    //private Stack<String> urls = new Stack<String>();
+
     // Url was specified from extras (activity was started programmatically)
-    private String initUrl = null;
-    
+    //private String initUrl = null;
+
     private static int ACTIVITY_STARTING = 0;
     private static int ACTIVITY_RUNNING = 1;
     private static int ACTIVITY_EXITING = 2;
     private int activityState = 0;  // 0=starting, 1=running (after 1st resume), 2=shutting down
-    
+
     // The base of the initial URL for our app.
     // Does not include file name.  Ends with /
     // ie http://server/path/
@@ -182,12 +182,12 @@ public class DroidGap extends Activity implements CordovaInterface {
     protected boolean activityResultKeepRunning;
 
     // Flag indicates that a loadUrl timeout occurred
-    int loadUrlTimeout = 0;
-    
+    //int loadUrlTimeout = 0;
+
     // Default background color for activity 
     // (this is not the color for the webview, which is set in HTML)
     private int backgroundColor = Color.BLACK;
-    
+
     /*
      * The variables below are used to cache some of the activity properties.
      */
@@ -195,30 +195,89 @@ public class DroidGap extends Activity implements CordovaInterface {
     // Draw a splash screen using an image located in the drawable resource directory.
     // This is not the same as calling super.loadSplashscreen(url)
     protected int splashscreen = 0;
+    protected int splashscreenTime = 0;
 
     // LoadUrl timeout value in msec (default of 20 sec)
-    protected int loadUrlTimeoutValue = 20000;
-    
+    //protected int loadUrlTimeoutValue = 20000;
+
     // Keep app running when pause is received. (default = true)
     // If true, then the JavaScript and native code continue to run in the background
     // when another application (activity) is started.
     protected boolean keepRunning = true;
-    
+
     // Store the useBrowserHistory preference until we actually need it.
-    private boolean useBrowserHistory = false;
-    
+    //private boolean useBrowserHistory = false;
+
     // preferences read from cordova.xml
-    protected PreferenceSet preferences;
-    
-    
+    //protected PreferenceSet preferences;
+
+    /**
+    * Sets the authentication token.
+    * 
+    * @param authenticationToken
+    * @param host
+    * @param realm
+    */
+    public void setAuthenticationToken(AuthenticationToken authenticationToken, String host, String realm) {
+        if (this.appView != null && this.appView.viewClient != null) {
+            this.appView.viewClient.setAuthenticationToken(authenticationToken, host, realm);
+        }
+    }
+
+    /**
+     * Removes the authentication token.
+     * 
+     * @param host
+     * @param realm
+     * 
+     * @return the authentication token or null if did not exist
+     */
+    public AuthenticationToken removeAuthenticationToken(String host, String realm) {
+        if (this.appView != null && this.appView.viewClient != null) {
+            return this.appView.viewClient.removeAuthenticationToken(host, realm);
+        }
+        return null;
+    }
+
+    /**
+     * Gets the authentication token.
+     * 
+     * In order it tries:
+     * 1- host + realm
+     * 2- host
+     * 3- realm
+     * 4- no host, no realm
+     * 
+     * @param host
+     * @param realm
+     * 
+     * @return the authentication token
+     */
+    public AuthenticationToken getAuthenticationToken(String host, String realm) {
+        if (this.appView != null && this.appView.viewClient != null) {
+            return this.appView.viewClient.getAuthenticationToken(host, realm);
+        }
+        return null;
+    }
+
+    /**
+     * Clear all authentication tokens.
+     */
+    public void clearAuthenticationTokens() {
+        if (this.appView != null && this.appView.viewClient != null) {
+            this.appView.viewClient.clearAuthenticationTokens();
+        }
+    }
+
     /** 
      * Called when the activity is first created. 
      * 
      * @param savedInstanceState
      */
+    @SuppressWarnings("deprecation")
     @Override
     public void onCreate(Bundle savedInstanceState) {
-        preferences = new PreferenceSet();
+        //preferences = new PreferenceSet();
 
         LOG.d(TAG, "DroidGap.onCreate()");
         super.onCreate(savedInstanceState);
@@ -226,38 +285,48 @@ public class DroidGap extends Activity implements CordovaInterface {
         getWindow().requestFeature(Window.FEATURE_NO_TITLE);
 
         getWindow().setFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN,
-                    WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
-        
+                WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
+
         // This builds the view.  We could probably get away with NOT having a LinearLayout, but I like having a bucket!
-        Display display = getWindowManager().getDefaultDisplay(); 
+        Display display = getWindowManager().getDefaultDisplay();
         int width = display.getWidth();
         int height = display.getHeight();
-        
+
         root = new LinearLayoutSoftKeyboardDetect(this, width, height);
         root.setOrientation(LinearLayout.VERTICAL);
         root.setBackgroundColor(this.backgroundColor);
-        root.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 
+        root.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                 ViewGroup.LayoutParams.MATCH_PARENT, 0.0F));
 
         // If url was passed in to intent, then init webview, which will load the url
-        Bundle bundle = this.getIntent().getExtras();
-        if (bundle != null) {
-            String url = bundle.getString("url");
-            if (url != null) {
-                this.initUrl = url;
-            }
-        }
+//        Bundle bundle = this.getIntent().getExtras();
+//        if (bundle != null) {
+//            String url = bundle.getString("url");
+//            if (url != null) {
+//                this.initUrl = url;
+//            }
+//        }
         // Setup the hardware volume controls to handle volume control
         setVolumeControlStream(AudioManager.STREAM_MUSIC);
     }
-    
+
+    /**
+     * Get the Android activity.
+     * 
+     * @return
+     */
+    public Activity getActivity() {
+        return this;
+    }
+
     /**
      * Create and initialize web container with default web view objects.
      */
     public void init() {
-    	this.init(new CordovaWebView(DroidGap.this), new CordovaWebViewClient(this), new CordovaChromeClient(DroidGap.this));
+        CordovaWebView webView = new CordovaWebView(DroidGap.this);
+        this.init(webView, new CordovaWebViewClient(this, webView), new CordovaChromeClient(this, webView));
     }
-    
+
     /**
      * Initialize web container with web view objects.
      * 
@@ -265,177 +334,201 @@ public class DroidGap extends Activity implements CordovaInterface {
      * @param webViewClient
      * @param webChromeClient
      */
-    public void init(CordovaWebView webView, WebViewClient webViewClient, WebChromeClient webChromeClient) {
+    public void init(CordovaWebView webView, CordovaWebViewClient webViewClient, CordovaChromeClient webChromeClient) {
         LOG.d(TAG, "DroidGap.init()");
-        
+
         // Set up web container
-       	this.appView = webView;
+        this.appView = webView;
         this.appView.setId(100);
-        
+
+        this.appView.setWebViewClient(webViewClient);
+        this.appView.setWebChromeClient(webChromeClient);
+        webViewClient.setWebView(this.appView);
+        webChromeClient.setWebView(this.appView);
+
         // Load Cordova configuration:
         //      white list of allowed URLs
         //      debug setting
-        this.loadConfiguration();
+        //this.loadConfiguration();
         //Now we can check the preference
-        appView.useBrowserHistory = preferences.prefMatches("useBrowserHistory", "true");
-        
+        //this.appView.useBrowserHistory = preferences.prefMatches("useBrowserHistory", "true");
+
         //
-        
+
         this.appView.setLayoutParams(new LinearLayout.LayoutParams(
                 ViewGroup.LayoutParams.MATCH_PARENT,
-                ViewGroup.LayoutParams.MATCH_PARENT, 
+                ViewGroup.LayoutParams.MATCH_PARENT,
                 1.0F));
 
         // Add web view but make it invisible while loading URL
         this.appView.setVisibility(View.INVISIBLE);
-        root.addView(this.appView);
-        setContentView(root);
-        
+        this.root.addView(this.appView);
+        setContentView(this.root);
+
         // Clear cancel flag
         this.cancelLoadUrl = false;
-        
     }
-    
+
     /**
      * Look at activity parameters and process them.
      * This must be called from the main UI thread.
      */
-    private void handleActivityParameters() {
+    //private void handleActivityParameters() {
 
-        // If backgroundColor
-        this.backgroundColor = this.getIntegerProperty("backgroundColor", Color.BLACK);
-        this.root.setBackgroundColor(this.backgroundColor);
+    // If backgroundColor
+    //this.backgroundColor = this.getIntegerProperty("backgroundColor", Color.BLACK);
+    //LOG.e(TAG, "Setting background color=" + this.backgroundColor);
+    //this.root.setBackgroundColor(this.backgroundColor);
 
-        // If spashscreen
-        this.splashscreen = this.getIntegerProperty("splashscreen", 0);
+    // If spashscreen
+    //this.splashscreen = this.getIntegerProperty("splashscreen", 0);
+
+    // If loadUrlTimeoutValue
+    //int timeout = this.getIntegerProperty("loadUrlTimeoutValue", 0);
+    //if (timeout > 0) {
+    //    this.loadUrlTimeoutValue = timeout;
+    //}
+
+    // If keepRunning
+    //this.keepRunning = this.getBooleanProperty("keepRunning", true);
+    //}
 
-        // If loadUrlTimeoutValue
-        int timeout = this.getIntegerProperty("loadUrlTimeoutValue", 0);
-        if (timeout > 0) {
-            this.loadUrlTimeoutValue = timeout;
-        }
-        
-        // If keepRunning
-        this.keepRunning = this.getBooleanProperty("keepRunning", true);
-    }
-    
     /**
      * Load the url into the webview.
      * 
      * @param url
      */
     public void loadUrl(String url) {
-        
-        // If first page of app, then set URL to load to be the one passed in
-        if (this.initUrl == null || (this.urls.size() > 0)) {
-            this.loadUrlIntoView(url);
-        }
-        // Otherwise use the URL specified in the activity's extras bundle
-        else {
-            this.loadUrlIntoView(this.initUrl);
+
+        // Init web view if not already done
+        if (this.appView == null) {
+            this.init();
         }
+
+        // Handle activity parameters 
+        //this.handleActivityParameters();
+
+        // If backgroundColor
+        this.backgroundColor = this.getIntegerProperty("backgroundColor", Color.BLACK);
+        LOG.e(TAG, "Setting background color=" + this.backgroundColor);
+        this.root.setBackgroundColor(this.backgroundColor);
+
+        // If keepRunning
+        this.keepRunning = this.getBooleanProperty("keepRunning", true);
+
+        // Then load the spinner
+        this.loadSpinner();
+
+        this.appView.loadUrl(url);
+
+//        // If first page of app, then set URL to load to be the one passed in
+//        if (this.initUrl == null || (this.urls.size() > 0)) {
+//            this.loadUrlIntoView(url);
+//        }
+//        // Otherwise use the URL specified in the activity's extras bundle
+//        else {
+//            this.loadUrlIntoView(this.initUrl);
+//        }
     }
-    
+
     /*
      * Load the spinner
-     * 
      */
-    void loadSpinner()
-    {
-      // If loadingDialog property, then show the App loading dialog for first page of app
-      // (This doesn't seem to actually do anything here)
-      String loading = null;
-      if (this.urls.size() == 1) {
-          loading = this.getStringProperty("loadingDialog", null);
-      }
-      else {
-          loading = this.getStringProperty("loadingPageDialog", null);                  
-      }
-      if (loading != null) {
-
-          String title = "";
-          String message = "Loading Application...";
-
-          if (loading.length() > 0) {
-              int comma = loading.indexOf(',');
-              if (comma > 0) {
-                  title = loading.substring(0, comma);
-                  message = loading.substring(comma+1);
-              }
-              else {
-                  title = "";
-                  message = loading;
-              }
-          }
-          this.spinnerStart(title, message);
-      }
+    void loadSpinner() {
+
+        // If loadingDialog property, then show the App loading dialog for first page of app
+        String loading = null;
+        if ((this.appView == null) || !this.appView.canGoBack()) {
+            loading = this.getStringProperty("loadingDialog", null);
+        }
+        else {
+            loading = this.getStringProperty("loadingPageDialog", null);
+        }
+        if (loading != null) {
+
+            String title = "";
+            String message = "Loading Application...";
+
+            if (loading.length() > 0) {
+                int comma = loading.indexOf(',');
+                if (comma > 0) {
+                    title = loading.substring(0, comma);
+                    message = loading.substring(comma + 1);
+                }
+                else {
+                    title = "";
+                    message = loading;
+                }
+            }
+            this.spinnerStart(title, message);
+        }
     }
-    
+
     /**
      * Load the url into the webview.
      * 
      * @param url
      */
-    private void loadUrlIntoView(final String url) {
-        if (!url.startsWith("javascript:")) {
-            LOG.d(TAG, "DroidGap.loadUrl(%s)", url);
-        }
-
-        if (!url.startsWith("javascript:")) {
-            LOG.d(TAG, "DroidGap: url=%s baseUrl=%s", url, baseUrl);
-        }
-        
-        // Load URL on UI thread
-        final DroidGap me = this;
-        
-        final Runnable loadError = new Runnable() {
-          public void run() {
-            me.appView.stopLoading();
-            LOG.e(TAG, "DroidGap: TIMEOUT ERROR! - calling webViewClient");
-            appView.viewClient.onReceivedError(me.appView, -6, "The connection to the server was unsuccessful.", url);
-          }
-        };
-        
-        this.runOnUiThread(new Runnable() {
-            public void run() {
+//    private void loadUrlIntoView(final String url) {
+//        if (!url.startsWith("javascript:")) {
+//            LOG.d(TAG, "DroidGap.loadUrl(%s)", url);
+//        }
+//
+//        if (!url.startsWith("javascript:")) {
+//            LOG.d(TAG, "DroidGap: url=%s baseUrl=%s", url, baseUrl);
+//        }
+//
+//        // Load URL on UI thread
+//        final DroidGap me = this;
+//
+////        final Runnable loadError = new Runnable() {
+////            public void run() {
+////                me.appView.stopLoading();
+////                LOG.e(TAG, "DroidGap: TIMEOUT ERROR! - calling webViewClient");
+////                appView.viewClient.onReceivedError(me.appView, -6, "The connection to the server was unsuccessful.", url);
+////            }
+////        };
+//
+//        this.runOnUiThread(new Runnable() {
+//            public void run() {
+//
+//                // Init web view if not already done
+//                if (me.appView == null) {
+//                    me.init();
+//                }
+//
+//                // Handle activity parameters (TODO: Somehow abstract this)
+//                me.handleActivityParameters();
+//
+//                // Then load the spinner
+//                me.loadSpinner();
+//
+////                // Create a timeout timer for loadUrl
+////                final int currentLoadUrlTimeout = me.loadUrlTimeout;
+////                Runnable runnable = new Runnable() {
+////                    public void run() {
+////                        try {
+////                            synchronized (this) {
+////                                wait(me.loadUrlTimeoutValue);
+////                            }
+////                        } catch (InterruptedException e) {
+////                            e.printStackTrace();
+////                        }
+////
+////                        // If timeout, then stop loading and handle error
+////                        if (me.loadUrlTimeout == currentLoadUrlTimeout) {
+////                            me.runOnUiThread(loadError);
+////
+////                        }
+////                    }
+////                };
+////                Thread thread = new Thread(runnable);
+////                thread.start();
+//                me.appView.loadUrl(url);
+//            }
+//        });
+//    }
 
-                // Init web view if not already done
-                if (me.appView == null) {
-                    me.init();
-                }
-
-                // Handle activity parameters (TODO: Somehow abstract this)
-                me.handleActivityParameters();
-                
-                // Then load the spinner
-                me.loadSpinner();
-
-                // Create a timeout timer for loadUrl
-                final int currentLoadUrlTimeout = me.loadUrlTimeout;
-                Runnable runnable = new Runnable() {
-                    public void run() {
-                        try {
-                            synchronized(this) {
-                                wait(me.loadUrlTimeoutValue);
-                            }
-                        } catch (InterruptedException e) {
-                            e.printStackTrace();
-                        }
-
-                        // If timeout, then stop loading and handle error
-                        if (me.loadUrlTimeout == currentLoadUrlTimeout) {
-                           me.runOnUiThread(loadError);
-                           
-                        }
-                    }
-                };
-                Thread thread = new Thread(runnable);
-                thread.start();
-                me.appView.loadUrl(url);
-            }
-        });
-    }
-    
     /**
      * Load the url into the webview after waiting for period of time.
      * This is used to display the splashscreen for certain amount of time.
@@ -444,15 +537,23 @@ public class DroidGap extends Activity implements CordovaInterface {
      * @param time              The number of ms to wait before loading webview
      */
     public void loadUrl(final String url, int time) {
-        
-        // If first page of app, then set URL to load to be the one passed in
-        if (this.initUrl == null || (this.urls.size() > 0)) {
-            this.loadUrlIntoView(url, time);
-        }
-        // Otherwise use the URL specified in the activity's extras bundle
-        else {
-            this.loadUrlIntoView(this.initUrl);
+
+        // Init web view if not already done
+        if (this.appView == null) {
+            this.init();
         }
+
+        this.splashscreenTime = time;
+        this.appView.loadUrl(url, time);
+
+//        // If first page of app, then set URL to load to be the one passed in
+//        if (this.initUrl == null || (this.urls.size() > 0)) {
+//            this.loadUrlIntoView(url, time);
+//        }
+//        // Otherwise use the URL specified in the activity's extras bundle
+//        else {
+//            this.loadUrlIntoView(this.initUrl);
+//        }
     }
 
     /**
@@ -460,36 +561,37 @@ public class DroidGap extends Activity implements CordovaInterface {
      * This is used to display the splashscreen for certain amount of time.
      * 
      * @param url
-     * @param time              The number of ms to wait before loading webview
+     * @param time              The number of ms to wait before showing webview
      */
-    private void loadUrlIntoView(final String url, final int time) {
+//    private void loadUrlIntoView(final String url, final int time) {
+//
+//        // Clear cancel flag
+//        this.cancelLoadUrl = false;
+//
+//        // If not first page of app, then load immediately
+//        if (this.urls.size() > 0) {
+//            this.loadUrlIntoView(url);
+//        }
+//
+//        if (!url.startsWith("javascript:")) {
+//            LOG.d(TAG, "DroidGap.loadUrl(%s, %d)", url, time);
+//        }
+//
+//        this.handleActivityParameters();
+//        if (this.splashscreen != 0) {
+//            this.showSplashScreen(time);
+//        }
+//        this.loadUrlIntoView(url);
+//    }
 
-        // Clear cancel flag
-        this.cancelLoadUrl = false;
-        
-        // If not first page of app, then load immediately
-        if (this.urls.size() > 0) {
-            this.loadUrlIntoView(url);
-        }
-        
-        if (!url.startsWith("javascript:")) {
-            LOG.d(TAG, "DroidGap.loadUrl(%s, %d)", url, time);
-        }
-        
-        this.handleActivityParameters();
-        if (this.splashscreen != 0) {
-            this.showSplashScreen(time);
-        }
-        this.loadUrlIntoView(url);
-    }
-    
     /**
      * Cancel loadUrl before it has been loaded.
      */
+    // TODO NO-OP
     public void cancelLoadUrl() {
         this.cancelLoadUrl = true;
     }
-    
+
     /**
      * Clear the resource cache.
      */
@@ -504,16 +606,27 @@ public class DroidGap extends Activity implements CordovaInterface {
      * Clear web history in this web view.
      */
     public void clearHistory() {
-        this.urls.clear();
         this.appView.clearHistory();
-        
-        // Leave current url on history stack
-        if (this.url != null) {
-            this.urls.push(this.url);
+//        this.urls.clear();
+//        this.appView.clearHistory();
+//
+//        // Leave current url on history stack
+//        if (this.url != null) {
+//            this.urls.push(this.url);
+//        }
+    }
+
+    /**
+     * Go to previous page in history.  (We manage our own history)
+     * 
+     * @return true if we went back, false if we are already at top
+     */
+    public boolean backHistory() {
+        if (this.appView != null) {
+            return appView.backHistory();
         }
+        return false;
     }
-    
-    
 
     @Override
     /**
@@ -525,7 +638,7 @@ public class DroidGap extends Activity implements CordovaInterface {
         //don't reload the current page when the orientation is changed
         super.onConfigurationChanged(newConfig);
     }
-    
+
     /**
      * Get boolean property for activity.
      * 
@@ -538,7 +651,7 @@ public class DroidGap extends Activity implements CordovaInterface {
         if (bundle == null) {
             return defaultValue;
         }
-        Boolean p = (Boolean)bundle.get(name);
+        Boolean p = (Boolean) bundle.get(name);
         if (p == null) {
             return defaultValue;
         }
@@ -557,7 +670,7 @@ public class DroidGap extends Activity implements CordovaInterface {
         if (bundle == null) {
             return defaultValue;
         }
-        Integer p = (Integer)bundle.get(name);
+        Integer p = (Integer) bundle.get(name);
         if (p == null) {
             return defaultValue;
         }
@@ -595,7 +708,7 @@ public class DroidGap extends Activity implements CordovaInterface {
         if (bundle == null) {
             return defaultValue;
         }
-        Double p = (Double)bundle.get(name);
+        Double p = (Double) bundle.get(name);
         if (p == null) {
             return defaultValue;
         }
@@ -611,7 +724,7 @@ public class DroidGap extends Activity implements CordovaInterface {
     public void setBooleanProperty(String name, boolean value) {
         this.getIntent().putExtra(name, value);
     }
-    
+
     /**
      * Set int property on activity.
      * 
@@ -621,7 +734,7 @@ public class DroidGap extends Activity implements CordovaInterface {
     public void setIntegerProperty(String name, int value) {
         this.getIntent().putExtra(name, value);
     }
-    
+
     /**
      * Set string property on activity.
      * 
@@ -648,7 +761,7 @@ public class DroidGap extends Activity implements CordovaInterface {
      */
     protected void onPause() {
         super.onPause();
-        
+
         // Don't process pause if shutting down, since onDestroy() will be called
         if (this.activityState == ACTIVITY_EXITING) {
             return;
@@ -662,8 +775,10 @@ public class DroidGap extends Activity implements CordovaInterface {
         this.appView.loadUrl("javascript:try{cordova.require('cordova/channel').onPause.fire();}catch(e){console.log('exception firing pause event from native');};");
 
         // Forward to plugins
-        appView.pluginManager.onPause(this.keepRunning);
-        
+        if (this.appView.pluginManager != null) {
+            this.appView.pluginManager.onPause(this.keepRunning);
+        }
+
         // If app doesn't want to run in background
         if (!this.keepRunning) {
 
@@ -680,16 +795,18 @@ public class DroidGap extends Activity implements CordovaInterface {
         super.onNewIntent(intent);
 
         //Forward to plugins
-        appView.pluginManager.onNewIntent(intent);
+        if ((this.appView != null) && (this.appView.pluginManager != null)) {
+            this.appView.pluginManager.onNewIntent(intent);
+        }
     }
-    
+
     @Override
     /**
      * Called when the activity will start interacting with the user. 
      */
     protected void onResume() {
         super.onResume();
-        
+
         if (this.activityState == ACTIVITY_STARTING) {
             this.activityState = ACTIVITY_RUNNING;
             return;
@@ -703,8 +820,10 @@ public class DroidGap extends Activity implements CordovaInterface {
         this.appView.loadUrl("javascript:try{cordova.require('cordova/channel').onResume.fire();}catch(e){console.log('exception firing resume event from native');};");
 
         // Forward to plugins
-        appView.pluginManager.onResume(this.keepRunning || this.activityResultKeepRunning);
-        
+        if (this.appView.pluginManager != null) {
+            this.appView.pluginManager.onResume(this.keepRunning || this.activityResultKeepRunning);
+        }
+
         // If app doesn't want to run in background
         if (!this.keepRunning || this.activityResultKeepRunning) {
 
@@ -715,19 +834,20 @@ public class DroidGap extends Activity implements CordovaInterface {
             }
 
             // Resume JavaScript timers (including setInterval)
-            this.appView.resumeTimers();
+            if (this.appView.pluginManager != null) {
+                this.appView.resumeTimers();
+            }
         }
     }
-    
+
     @Override
     /**
      * The final call you receive before your activity is destroyed. 
      */
     public void onDestroy() {
         super.onDestroy();
-        
-        if (this.appView != null) {
 
+        if (this.appView != null) {
 
             // Send destroy event to JavaScript
             this.appView.loadUrl("javascript:try{cordova.require('cordova/channel').onDestroy.fire();}catch(e){console.log('exception firing destroy event from native');};");
@@ -736,7 +856,9 @@ public class DroidGap extends Activity implements CordovaInterface {
             this.appView.loadUrl("about:blank");
 
             // Forward to plugins
-            appView.pluginManager.onDestroy();
+            if (this.appView.pluginManager != null) {
+                this.appView.pluginManager.onDestroy();
+            }
         }
         else {
             this.endActivity();
@@ -750,9 +872,9 @@ public class DroidGap extends Activity implements CordovaInterface {
      * @param data          The message data
      */
     public void postMessage(String id, Object data) {
-        
-        // Forward to plugins
-        appView.pluginManager.postMessage(id, data);
+        if (this.appView != null) {
+            this.appView.postMessage(id, data);
+        }
     }
 
     /**
@@ -764,11 +886,12 @@ public class DroidGap extends Activity implements CordovaInterface {
      * @param serviceType
      * @param className
      */
-    @Deprecated
     public void addService(String serviceType, String className) {
-        appView.pluginManager.addService(serviceType, className);
+        if (this.appView != null && this.appView.pluginManager != null) {
+            this.appView.pluginManager.addService(serviceType, className);
+        }
     }
-    
+
     /**
      * Send JavaScript statement back to JavaScript.
      * (This is a convenience method)
@@ -776,9 +899,10 @@ public class DroidGap extends Activity implements CordovaInterface {
      * @param message
      */
     public void sendJavascript(String statement) {
-        //We need to check for the null case on the Kindle Fire beacuse it changes the width and height on load
-        if(this.appView.callbackServer != null)
-          appView.callbackServer.sendJavascript(statement);
+        //We need to check for the null case on the Kindle Fire because it changes the width and height on load
+        if (this.appView != null && this.appView.callbackServer != null) {
+            this.appView.callbackServer.sendJavascript(statement);
+        }
     }
 
     /**
@@ -793,12 +917,12 @@ public class DroidGap extends Activity implements CordovaInterface {
             this.spinnerDialog = null;
         }
         final DroidGap me = this;
-        this.spinnerDialog = ProgressDialog.show(DroidGap.this, title , message, true, true, 
-                new DialogInterface.OnCancelListener() { 
-            public void onCancel(DialogInterface dialog) {
-                me.spinnerDialog = null;
-            }
-        });
+        this.spinnerDialog = ProgressDialog.show(DroidGap.this, title, message, true, true,
+                new DialogInterface.OnCancelListener() {
+                    public void onCancel(DialogInterface dialog) {
+                        me.spinnerDialog = null;
+                    }
+                });
     }
 
     /**
@@ -810,17 +934,25 @@ public class DroidGap extends Activity implements CordovaInterface {
             this.spinnerDialog = null;
         }
     }
-    
+
     /**
      * End this activity by calling finish for activity
      */
     public void endActivity() {
-        this.activityState = ACTIVITY_EXITING;
+        //this.activityState = ACTIVITY_EXITING;
         this.finish();
     }
-    
+
+    /**
+     * Finish for Activity has been called.
+     */
+    public void finish() {
+        this.activityState = ACTIVITY_EXITING;
+        super.finish();
+    }
+
     /**
-     * Called when a key is de-pressed. (Key UP)
+     * Called when a key is released. (Key UP)
      * 
      * @param keyCode
      * @param event
@@ -877,11 +1009,11 @@ public class DroidGap extends Activity implements CordovaInterface {
      * 
      * @throws RuntimeException
      */
-    @Override
-    public void startActivityForResult(Intent intent, int requestCode) throws RuntimeException {
-        LOG.d(TAG, "DroidGap.startActivityForResult(intent,%d)", requestCode);
-        super.startActivityForResult(intent, requestCode);
-    }
+    //@Override
+    //public void startActivityForResult(Intent intent, int requestCode) throws RuntimeException {
+    //    LOG.d(TAG, "DroidGap.startActivityForResult(intent,%d)", requestCode);
+    //    super.startActivityForResult(intent, requestCode);
+    //}
 
     /**
      * Launch an activity for which you would like a result when it finished. When this activity exits, 
@@ -894,17 +1026,17 @@ public class DroidGap extends Activity implements CordovaInterface {
     public void startActivityForResult(IPlugin command, Intent intent, int requestCode) {
         this.activityResultCallback = command;
         this.activityResultKeepRunning = this.keepRunning;
-        
+
         // If multitasking turned on, then disable it for activities that return results
         if (command != null) {
             this.keepRunning = false;
         }
-        
+
         // Start activity
         super.startActivityForResult(intent, requestCode);
     }
 
-     @Override
+    @Override
     /**
      * Called when an activity you launched exits, giving you the requestCode you started it with,
      * the resultCode it returned, and any additional data from it. 
@@ -914,143 +1046,143 @@ public class DroidGap extends Activity implements CordovaInterface {
      * @param resultCode        The integer result code returned by the child activity through its setResult().
      * @param data              An Intent, which can return result data to the caller (various data can be attached to Intent "extras").
      */
-     protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
-         super.onActivityResult(requestCode, resultCode, intent);
-         IPlugin callback = this.activityResultCallback;
-         if (callback != null) {
-             callback.onActivityResult(requestCode, resultCode, intent);
-         }        
-     }
-
-     public void setActivityResultCallback(IPlugin plugin) {
-         this.activityResultCallback = plugin;
-     }
-
-     /**
-      * Report an error to the host application. These errors are unrecoverable (i.e. the main resource is unavailable). 
-      * The errorCode parameter corresponds to one of the ERROR_* constants.
-      *
-      * @param errorCode    The error code corresponding to an ERROR_* value.
-      * @param description  A String describing the error.
-      * @param failingUrl   The url that failed to load. 
-      */
-     public void onReceivedError(final int errorCode, final String description, final String failingUrl) {
-         final DroidGap me = this;
-
-         // If errorUrl specified, then load it
-         final String errorUrl = me.getStringProperty("errorUrl", null);
-         if ((errorUrl != null) && (errorUrl.startsWith("file://") || errorUrl.indexOf(me.baseUrl) == 0 || isUrlWhiteListed(errorUrl)) && (!failingUrl.equals(errorUrl))) {
-
-             // Load URL on UI thread
-             me.runOnUiThread(new Runnable() {
-                 public void run() {
-                     me.showWebPage(errorUrl, false, true, null); 
-                 }
-             });
-         }
-         // If not, then display error dialog
-         else {
-             final boolean exit = !(errorCode == WebViewClient.ERROR_HOST_LOOKUP);
-             me.runOnUiThread(new Runnable() {
-                 public void run() {
-                     if(exit)
-                     {
-                       me.appView.setVisibility(View.GONE);
-                       me.displayError("Application Error", description + " ("+failingUrl+")", "OK", exit);
-                     }
-                 }
-             });
-         }
-     }
-
-     /**
-      * Display an error dialog and optionally exit application.
-      * 
-      * @param title
-      * @param message
-      * @param button
-      * @param exit
-      */
-     public void displayError(final String title, final String message, final String button, final boolean exit) {
-         final DroidGap me = this;
-         me.runOnUiThread(new Runnable() {
-             public void run() {
-                 AlertDialog.Builder dlg = new AlertDialog.Builder(me);
-                 dlg.setMessage(message);
-                 dlg.setTitle(title);
-                 dlg.setCancelable(false);
-                 dlg.setPositiveButton(button,
-                         new AlertDialog.OnClickListener() {
-                     public void onClick(DialogInterface dialog, int which) {
-                         dialog.dismiss();
-                         if (exit) {
-                             me.endActivity();
-                         }
-                     }
-                 });
-                 dlg.create();
-                 dlg.show();
-             }
-         });
-     }
-     
-     
+    protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
+        super.onActivityResult(requestCode, resultCode, intent);
+        IPlugin callback = this.activityResultCallback;
+        if (callback != null) {
+            callback.onActivityResult(requestCode, resultCode, intent);
+        }
+    }
+
+    public void setActivityResultCallback(IPlugin plugin) {
+        this.activityResultCallback = plugin;
+    }
+
     /**
-     * Load Cordova configuration from res/xml/cordova.xml.
-     * Approved list of URLs that can be loaded into DroidGap
-     *      <access origin="http://server regexp" subdomains="true" />
-     * Log level: ERROR, WARN, INFO, DEBUG, VERBOSE (default=ERROR)
-     *      <log level="DEBUG" />
+     * Report an error to the host application. These errors are unrecoverable (i.e. the main resource is unavailable). 
+     * The errorCode parameter corresponds to one of the ERROR_* constants.
+     *
+     * @param errorCode    The error code corresponding to an ERROR_* value.
+     * @param description  A String describing the error.
+     * @param failingUrl   The url that failed to load. 
      */
-    private void loadConfiguration() {
-        int id = getResources().getIdentifier("cordova", "xml", getPackageName());
-        if (id == 0) {
-            LOG.i("CordovaLog", "cordova.xml missing. Ignoring...");
-            return;
-        }
-        XmlResourceParser xml = getResources().getXml(id);
-        int eventType = -1;
-        while (eventType != XmlResourceParser.END_DOCUMENT) {
-            if (eventType == XmlResourceParser.START_TAG) {
-                String strNode = xml.getName();
-                if (strNode.equals("access")) {
-                    String origin = xml.getAttributeValue(null, "origin");
-                    String subdomains = xml.getAttributeValue(null, "subdomains");
-                    if (origin != null) {
-                        appView.addWhiteListEntry(origin, (subdomains != null) && (subdomains.compareToIgnoreCase("true") == 0));
-                    }
-                }
-                else if (strNode.equals("log")) {
-                    String level = xml.getAttributeValue(null, "level");
-                    LOG.i("CordovaLog", "Found log level %s", level);
-                    if (level != null) {
-                        LOG.setLogLevel(level);
-                    }
-                }
-                else if (strNode.equals("preference")) {
-                    String name = xml.getAttributeValue(null, "name");
-                    String value = xml.getAttributeValue(null, "value");
-                    String readonlyString = xml.getAttributeValue(null, "readonly");
+    public void onReceivedError(final int errorCode, final String description, final String failingUrl) {
+        final DroidGap me = this;
 
-                    boolean readonly = (readonlyString != null &&
-                                        readonlyString.equals("true"));
+        // Stop "app loading" spinner if showing
+        this.spinnerStop();
 
-                    LOG.i("CordovaLog", "Found preference for %s", name);
+        // If errorUrl specified, then load it
+        final String errorUrl = me.getStringProperty("errorUrl", null);
+        if ((errorUrl != null) && (errorUrl.startsWith("file://") || errorUrl.indexOf(me.baseUrl) == 0 || this.appView.isUrlWhiteListed(errorUrl)) && (!failingUrl.equals(errorUrl))) {
 
-                    preferences.add(new PreferenceNode(name, value, readonly));
+            // Load URL on UI thread
+            me.runOnUiThread(new Runnable() {
+                public void run() {
+                    me.appView.showWebPage(errorUrl, false, true, null);
                 }
-            }
-            try {
-                eventType = xml.next();
-            } catch (XmlPullParserException e) {
-                e.printStackTrace();
-            } catch (IOException e) {
-                e.printStackTrace();
-            }
+            });
+        }
+        // If not, then display error dialog
+        else {
+            final boolean exit = !(errorCode == WebViewClient.ERROR_HOST_LOOKUP);
+            me.runOnUiThread(new Runnable() {
+                public void run() {
+                    if (exit)
+                    {
+                        me.appView.setVisibility(View.GONE);
+                        me.displayError("Application Error", description + " (" + failingUrl + ")", "OK", exit);
+                    }
+                }
+            });
         }
     }
 
-    
+    /**
+     * Display an error dialog and optionally exit application.
+     * 
+     * @param title
+     * @param message
+     * @param button
+     * @param exit
+     */
+    public void displayError(final String title, final String message, final String button, final boolean exit) {
+        final DroidGap me = this;
+        me.runOnUiThread(new Runnable() {
+            public void run() {
+                AlertDialog.Builder dlg = new AlertDialog.Builder(me);
+                dlg.setMessage(message);
+                dlg.setTitle(title);
+                dlg.setCancelable(false);
+                dlg.setPositiveButton(button,
+                        new AlertDialog.OnClickListener() {
+                            public void onClick(DialogInterface dialog, int which) {
+                                dialog.dismiss();
+                                if (exit) {
+                                    me.endActivity();
+                                }
+                            }
+                        });
+                dlg.create();
+                dlg.show();
+            }
+        });
+    }
+
+    /**
+     * Load Cordova configuration from res/xml/cordova.xml.
+     * Approved list of URLs that can be loaded into DroidGap
+     *      <access origin="http://server regexp" subdomains="true" />
+     * Log level: ERROR, WARN, INFO, DEBUG, VERBOSE (default=ERROR)
+     *      <log level="DEBUG" />
+     */
+//    private void loadConfiguration() {
+//        int id = getResources().getIdentifier("cordova", "xml", getPackageName());
+//        if (id == 0) {
+//            LOG.i("CordovaLog", "cordova.xml missing. Ignoring...");
+//            return;
+//        }
+//        XmlResourceParser xml = getResources().getXml(id);
+//        int eventType = -1;
+//        while (eventType != XmlResourceParser.END_DOCUMENT) {
+//            if (eventType == XmlResourceParser.START_TAG) {
+//                String strNode = xml.getName();
+//                if (strNode.equals("access")) {
+//                    String origin = xml.getAttributeValue(null, "origin");
+//                    String subdomains = xml.getAttributeValue(null, "subdomains");
+//                    if (origin != null) {
+//                        appView.addWhiteListEntry(origin, (subdomains != null) && (subdomains.compareToIgnoreCase("true") == 0));
+//                    }
+//                }
+//                else if (strNode.equals("log")) {
+//                    String level = xml.getAttributeValue(null, "level");
+//                    LOG.i("CordovaLog", "Found log level %s", level);
+//                    if (level != null) {
+//                        LOG.setLogLevel(level);
+//                    }
+//                }
+//                else if (strNode.equals("preference")) {
+//                    String name = xml.getAttributeValue(null, "name");
+//                    String value = xml.getAttributeValue(null, "value");
+//                    String readonlyString = xml.getAttributeValue(null, "readonly");
+//
+//                    boolean readonly = (readonlyString != null &&
+//                            readonlyString.equals("true"));
+//
+//                    LOG.i("CordovaLog", "Found preference for %s", name);
+//
+//                    preferences.add(new PreferenceNode(name, value, readonly));
+//                }
+//            }
+//            try {
+//                eventType = xml.next();
+//            } catch (XmlPullParserException e) {
+//                e.printStackTrace();
+//            } catch (IOException e) {
+//                e.printStackTrace();
+//            }
+//        }
+//    }
 
     /**
      * Determine if URL is in approved list of URLs to load.
@@ -1060,66 +1192,79 @@ public class DroidGap extends Activity implements CordovaInterface {
      */
     public boolean isUrlWhiteListed(String url) {
         // Check to see if we have matched url previously
-        return this.appView.isUrlWhiteListed(url);
+        if (this.appView != null) {
+            return this.appView.isUrlWhiteListed(url);
+        }
+        return false;
     }
-    
-    /*
-     * URL stack manipulators
-     */
-    
-    
-    
+
     /* 
      * Hook in DroidGap for menu plugins
      * 
      */
-    
     @Override
-    public boolean onCreateOptionsMenu(Menu menu)
-    {
+    public boolean onCreateOptionsMenu(Menu menu) {
         this.postMessage("onCreateOptionsMenu", menu);
         return super.onCreateOptionsMenu(menu);
     }
-    
+
     @Override
-    public boolean onPrepareOptionsMenu(Menu menu)
-    {
+    public boolean onPrepareOptionsMenu(Menu menu) {
         this.postMessage("onPrepareOptionsMenu", menu);
         return super.onPrepareOptionsMenu(menu);
     }
-    
+
     @Override
-    public boolean onOptionsItemSelected(MenuItem item)
-    {
+    public boolean onOptionsItemSelected(MenuItem item) {
         this.postMessage("onOptionsItemSelected", item);
         return true;
     }
 
+    /**
+     * Get Activity context.
+     * 
+     * @return
+     */
     public Context getContext() {
-      return this;
+        return this.getContext();
     }
 
+    /**
+     * Override the backbutton.
+     * 
+     * @param override
+     */
     public void bindBackButton(boolean override) {
-      // TODO Auto-generated method stub
-      this.bound = override;
+        this.bound = override;
     }
 
+    /**
+     * Determine of backbutton is overridden.
+     * 
+     * @return
+     */
     public boolean isBackButtonBound() {
-      // TODO Auto-generated method stub
-      return this.bound;
+        return this.bound;
     }
 
-    public boolean backHistory() {
-      return appView.backHistory();
-    }
-
-    public void showWebPage(String url, boolean openExternal,
-        boolean clearHistory, HashMap<String, Object> params) {
-      appView.showWebPage(url, openExternal, clearHistory, params);
+    /**
+     * Load the specified URL in the Cordova webview or a new browser instance.
+     * 
+     * NOTE: If openExternal is false, only URLs listed in whitelist can be loaded.
+     *
+     * @param url           The url to load.
+     * @param openExternal  Load url in browser instead of Cordova webview.
+     * @param clearHistory  Clear the history stack, so new page becomes top of history
+     * @param params        DroidGap parameters for new app
+     */
+    public void showWebPage(String url, boolean openExternal, boolean clearHistory, HashMap<String, Object> params) {
+        if (this.appView != null) {
+            appView.showWebPage(url, openExternal, clearHistory, params);
+        }
     }
 
     protected Dialog splashDialog;
-    
+
     /**
      * Removes the Dialog that displays the splash screen
      */
@@ -1129,36 +1274,72 @@ public class DroidGap extends Activity implements CordovaInterface {
             splashDialog = null;
         }
     }
-     
+
     /**
      * Shows the splash screen over the full Activity
      */
+    @SuppressWarnings("deprecation")
     protected void showSplashScreen(int time) {
+
         // Get reference to display
         Display display = getWindowManager().getDefaultDisplay();
-        
+
         // Create the layout for the dialog
         LinearLayout root = new LinearLayout(this);
         root.setMinimumHeight(display.getHeight());
         root.setMinimumWidth(display.getWidth());
         root.setOrientation(LinearLayout.VERTICAL);
         root.setBackgroundColor(this.getIntegerProperty("backgroundColor", Color.BLACK));
-        root.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, 
+        root.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
                 ViewGroup.LayoutParams.FILL_PARENT, 0.0F));
         root.setBackgroundResource(this.splashscreen);
 
         // Create and show the dialog
-        splashDialog = new Dialog(this, android.R.style.Theme_Translucent_NoTitleBar);       
+        splashDialog = new Dialog(this, android.R.style.Theme_Translucent_NoTitleBar);
         splashDialog.setContentView(root);
         splashDialog.setCancelable(false);
         splashDialog.show();
-     
+
         // Set Runnable to remove splash screen just in case
         final Handler handler = new Handler();
         handler.postDelayed(new Runnable() {
-          public void run() {
-            removeSplashScreen();
-          }
+            public void run() {
+                removeSplashScreen();
+            }
         }, time);
     }
+
+    /**
+     * Called when a message is sent to plugin. 
+     * 
+     * @param id            The message id
+     * @param data          The message data
+     */
+    public void onMessage(String id, Object data) {
+        LOG.d(TAG, "onMessage(" + id + "," + data + ")");
+        if ("splashscreen".equals(id)) {
+            if ("hide".equals(data.toString())) {
+                this.removeSplashScreen();
+            }
+            else {
+                this.splashscreen = this.getIntegerProperty("splashscreen", 0);
+                this.showSplashScreen(this.splashscreenTime);
+            }
+        }
+        else if ("spinner".equals(id)) {
+            if ("stop".equals(data.toString())) {
+                this.spinnerStop();
+                this.appView.setVisibility(View.VISIBLE);
+            }
+        }
+        else if ("onReceivedError".equals(id)) {
+            JSONObject d = (JSONObject) data;
+            try {
+                this.onReceivedError(d.getInt("errorCode"), d.getString("description"), d.getString("url"));
+            } catch (JSONException e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-cordova-android/blob/c8fafa6b/framework/src/org/apache/cordova/FileTransfer.java
----------------------------------------------------------------------
diff --git a/framework/src/org/apache/cordova/FileTransfer.java b/framework/src/org/apache/cordova/FileTransfer.java
index 6eee05a..c3456e0 100644
--- a/framework/src/org/apache/cordova/FileTransfer.java
+++ b/framework/src/org/apache/cordova/FileTransfer.java
@@ -51,13 +51,12 @@ import android.net.Uri;
 import android.util.Log;
 import android.webkit.CookieManager;
 
-
 public class FileTransfer extends Plugin {
 
     private static final String LOG_TAG = "FileTransfer";
     private static final String LINE_START = "--";
     private static final String LINE_END = "\r\n";
-    private static final String BOUNDRY =  "*****";
+    private static final String BOUNDRY = "*****";
 
     public static int FILE_NOT_FOUND_ERR = 1;
     public static int INVALID_URL_ERR = 2;
@@ -76,8 +75,7 @@ public class FileTransfer extends Plugin {
         try {
             source = args.getString(0);
             target = args.getString(1);
-        }
-        catch (JSONException e) {
+        } catch (JSONException e) {
             Log.d(LOG_TAG, "Missing source or target");
             return new PluginResult(PluginResult.Status.JSON_EXCEPTION, "Missing source or target");
         }
@@ -151,11 +149,11 @@ public class FileTransfer extends Plugin {
             }
 
             public void checkClientTrusted(X509Certificate[] chain,
-                            String authType) throws CertificateException {
+                    String authType) throws CertificateException {
             }
 
             public void checkServerTrusted(X509Certificate[] chain,
-                            String authType) throws CertificateException {
+                    String authType) throws CertificateException {
             }
         } };
 
@@ -199,7 +197,7 @@ public class FileTransfer extends Plugin {
      */
     private String getArgument(JSONArray args, int position, String defaultString) {
         String arg = defaultString;
-        if(args.length() >= position) {
+        if (args.length() >= position) {
             arg = args.optString(position);
             if (arg == null || "null".equals(arg)) {
                 arg = defaultString;
@@ -275,26 +273,26 @@ public class FileTransfer extends Plugin {
         // Use a post method.
         conn.setRequestMethod("POST");
         conn.setRequestProperty("Connection", "Keep-Alive");
-        conn.setRequestProperty("Content-Type", "multipart/form-data;boundary="+BOUNDRY);
+        conn.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + BOUNDRY);
 
         // Handle the other headers
         try {
-          JSONObject headers = params.getJSONObject("headers");
-          for (Iterator iter = headers.keys(); iter.hasNext();)
-          {
-            String headerKey = iter.next().toString();
-            conn.setRequestProperty(headerKey, headers.getString(headerKey));
-          }
+            JSONObject headers = params.getJSONObject("headers");
+            for (@SuppressWarnings("rawtypes")
+            Iterator iter = headers.keys(); iter.hasNext();)
+            {
+                String headerKey = iter.next().toString();
+                conn.setRequestProperty(headerKey, headers.getString(headerKey));
+            }
         } catch (JSONException e1) {
-          // No headers to be manipulated!
+            // No headers to be manipulated!
         }
-        
+
         // Set the cookies on the response
         String cookie = CookieManager.getInstance().getCookie(server);
         if (cookie != null) {
             conn.setRequestProperty("Cookie", cookie);
         }
-        
 
         /*
          * Store the non-file portions of the multipart data as a string, so that we can add it 
@@ -302,42 +300,42 @@ public class FileTransfer extends Plugin {
          */
         String extraParams = "";
         try {
-            for (Iterator iter = params.keys(); iter.hasNext();) {
+            for (@SuppressWarnings("rawtypes")
+            Iterator iter = params.keys(); iter.hasNext();) {
                 Object key = iter.next();
-                if(key.toString() != "headers")
+                if (key.toString() != "headers")
                 {
-                  extraParams += LINE_START + BOUNDRY + LINE_END;
-                  extraParams += "Content-Disposition: form-data; name=\"" +  key.toString() + "\";";
-                  extraParams += LINE_END + LINE_END;
-                  extraParams += params.getString(key.toString());
-                  extraParams += LINE_END;
+                    extraParams += LINE_START + BOUNDRY + LINE_END;
+                    extraParams += "Content-Disposition: form-data; name=\"" + key.toString() + "\";";
+                    extraParams += LINE_END + LINE_END;
+                    extraParams += params.getString(key.toString());
+                    extraParams += LINE_END;
                 }
             }
         } catch (JSONException e) {
             Log.e(LOG_TAG, e.getMessage(), e);
         }
-        
+
         extraParams += LINE_START + BOUNDRY + LINE_END;
         extraParams += "Content-Disposition: form-data; name=\"" + fileKey + "\";" + " filename=\"";
-        
+
         String midParams = "\"" + LINE_END + "Content-Type: " + mimeType + LINE_END + LINE_END;
         String tailParams = LINE_END + LINE_START + BOUNDRY + LINE_START + LINE_END;
-        
+
         // Should set this up as an option
         if (chunkedMode) {
             conn.setChunkedStreamingMode(maxBufferSize);
         }
         else
         {
-          int stringLength = extraParams.length() + midParams.length() + tailParams.length() + fileName.getBytes("UTF-8").length;
-          Log.d(LOG_TAG, "String Length: " + stringLength);
-          int fixedLength = (int) fileInputStream.getChannel().size() + stringLength;
-          Log.d(LOG_TAG, "Content Length: " + fixedLength);
-          conn.setFixedLengthStreamingMode(fixedLength);
+            int stringLength = extraParams.length() + midParams.length() + tailParams.length() + fileName.getBytes("UTF-8").length;
+            Log.d(LOG_TAG, "String Length: " + stringLength);
+            int fixedLength = (int) fileInputStream.getChannel().size() + stringLength;
+            Log.d(LOG_TAG, "Content Length: " + fixedLength);
+            conn.setFixedLengthStreamingMode(fixedLength);
         }
-        
 
-        dos = new DataOutputStream( conn.getOutputStream() );
+        dos = new DataOutputStream(conn.getOutputStream());
         dos.writeBytes(extraParams);
         //We don't want to chagne encoding, we just want this to write for all Unicode.
         dos.write(fileName.getBytes("UTF-8"));
@@ -373,13 +371,13 @@ public class FileTransfer extends Plugin {
         StringBuffer responseString = new StringBuffer("");
         DataInputStream inStream;
         try {
-            inStream = new DataInputStream ( conn.getInputStream() );
-        } catch(FileNotFoundException e) {
+            inStream = new DataInputStream(conn.getInputStream());
+        } catch (FileNotFoundException e) {
             throw new IOException("Received error from server");
         }
 
         String line;
-        while (( line = inStream.readLine()) != null) {
+        while ((line = inStream.readLine()) != null) {
             responseString.append(line);
         }
         Log.d(LOG_TAG, "got response from server");
@@ -394,7 +392,7 @@ public class FileTransfer extends Plugin {
 
         // Revert back to the proper verifier and socket factories
         if (trustEveryone && url.getProtocol().toLowerCase().equals("https")) {
-            ((HttpsURLConnection)conn).setHostnameVerifier(defaultHostnameVerifier);
+            ((HttpsURLConnection) conn).setHostnameVerifier(defaultHostnameVerifier);
             HttpsURLConnection.setDefaultSSLSocketFactory(defaultSSLSocketFactory);
         }
 
@@ -416,46 +414,46 @@ public class FileTransfer extends Plugin {
             file.getParentFile().mkdirs();
 
             // connect to server
-            if(webView.isUrlWhiteListed(source))
+            if (webView.isUrlWhiteListed(source))
             {
-              URL url = new URL(source);
-              HttpURLConnection connection = (HttpURLConnection) url.openConnection();
-              connection.setRequestMethod("GET");
-              
-              //Add cookie support
-              String cookie = CookieManager.getInstance().getCookie(source);
-              if(cookie != null)
-              {
-                connection.setRequestProperty("cookie", cookie);
-              }
-              
-              connection.connect();
+                URL url = new URL(source);
+                HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+                connection.setRequestMethod("GET");
 
-              Log.d(LOG_TAG, "Download file:" + url);
+                //Add cookie support
+                String cookie = CookieManager.getInstance().getCookie(source);
+                if (cookie != null)
+                {
+                    connection.setRequestProperty("cookie", cookie);
+                }
 
-              InputStream inputStream = connection.getInputStream();
-              byte[] buffer = new byte[1024];
-              int bytesRead = 0;
+                connection.connect();
 
-              FileOutputStream outputStream = new FileOutputStream(file);
+                Log.d(LOG_TAG, "Download file:" + url);
 
-              // write bytes to file
-              while ( (bytesRead = inputStream.read(buffer)) > 0 ) {
-                outputStream.write(buffer,0, bytesRead);
-              }
+                InputStream inputStream = connection.getInputStream();
+                byte[] buffer = new byte[1024];
+                int bytesRead = 0;
+
+                FileOutputStream outputStream = new FileOutputStream(file);
+
+                // write bytes to file
+                while ((bytesRead = inputStream.read(buffer)) > 0) {
+                    outputStream.write(buffer, 0, bytesRead);
+                }
 
-              outputStream.close();
+                outputStream.close();
 
-              Log.d(LOG_TAG, "Saved file: " + target);
+                Log.d(LOG_TAG, "Saved file: " + target);
 
-              // create FileEntry object
-              FileUtils fileUtil = new FileUtils();
+                // create FileEntry object
+                FileUtils fileUtil = new FileUtils();
 
-              return fileUtil.getEntry(file);
+                return fileUtil.getEntry(file);
             }
             else
             {
-              throw new IOException("Error: Unable to connect to domain");
+                throw new IOException("Error: Unable to connect to domain");
             }
         } catch (Exception e) {
             Log.d(LOG_TAG, e.getMessage(), e);
@@ -473,7 +471,7 @@ public class FileTransfer extends Plugin {
     private InputStream getPathFromUri(String path) throws FileNotFoundException {
         if (path.startsWith("content:")) {
             Uri uri = Uri.parse(path);
-            return ctx.getContentResolver().openInputStream(uri);
+            return ctx.getActivity().getContentResolver().openInputStream(uri);
         }
         else if (path.startsWith("file://")) {
             int question = path.indexOf("?");

http://git-wip-us.apache.org/repos/asf/incubator-cordova-android/blob/c8fafa6b/framework/src/org/apache/cordova/FileUtils.java
----------------------------------------------------------------------
diff --git a/framework/src/org/apache/cordova/FileUtils.java b/framework/src/org/apache/cordova/FileUtils.java
index fb5694b..5a73c80 100755
--- a/framework/src/org/apache/cordova/FileUtils.java
+++ b/framework/src/org/apache/cordova/FileUtils.java
@@ -37,15 +37,15 @@ import org.json.JSONArray;
 import org.json.JSONException;
 import org.json.JSONObject;
 
-import android.content.Context;
+//import android.content.Context;
 import android.database.Cursor;
 import android.net.Uri;
 import android.os.Environment;
 import android.provider.MediaStore;
-import android.util.Log;
+//import android.util.Log;
 import android.webkit.MimeTypeMap;
-import android.app.Activity;
 
+//import android.app.Activity;
 
 /**
  * This class provides SD card file and directory services to JavaScript.
@@ -132,7 +132,7 @@ public class FileUtils extends Plugin {
             else if (action.equals("requestFileSystem")) {
                 long size = args.optLong(1);
                 if (size != 0) {
-                    if (size > (DirectoryManager.getFreeDiskSpace(true)*1024)) {
+                    if (size > (DirectoryManager.getFreeDiskSpace(true) * 1024)) {
                         return new PluginResult(PluginResult.Status.ERROR, FileUtils.QUOTA_EXCEEDED_ERR);
                     }
                 }
@@ -222,9 +222,9 @@ public class FileUtils extends Plugin {
      * @param filePath the path to check
      */
     private void notifyDelete(String filePath) {
-        int result = this.ctx.getContentResolver().delete(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
+        int result = this.ctx.getActivity().getContentResolver().delete(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                 MediaStore.Images.Media.DATA + " = ?",
-                new String[] {filePath});
+                new String[] { filePath });
     }
 
     /**
@@ -244,7 +244,7 @@ public class FileUtils extends Plugin {
 
         // Handle the special case where you get an Android content:// uri.
         if (decoded.startsWith("content:")) {
-            Cursor cursor = ((Activity) this.ctx).managedQuery(Uri.parse(decoded), new String[] { MediaStore.Images.Media.DATA }, null, null, null);
+            Cursor cursor = this.ctx.getActivity().managedQuery(Uri.parse(decoded), new String[] { MediaStore.Images.Media.DATA }, null, null, null);
             // Note: MediaStore.Images/Audio/Video.Media.DATA is always "_data"
             int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
             cursor.moveToFirst();
@@ -295,7 +295,7 @@ public class FileUtils extends Plugin {
 
         if (fp.isDirectory()) {
             File[] files = fp.listFiles();
-            for (int i=0; i<files.length; i++) {
+            for (int i = 0; i < files.length; i++) {
                 entries.put(getEntry(files[i]));
             }
         }
@@ -321,7 +321,6 @@ public class FileUtils extends Plugin {
         fileName = stripFileProtocol(fileName);
         newParent = stripFileProtocol(newParent);
 
-
         // Check for invalid file name
         if (newName != null && newName.contains(":")) {
             throw new EncodingException("Bad file name");
@@ -378,7 +377,7 @@ public class FileUtils extends Plugin {
         File destFile = null;
 
         // I know this looks weird but it is to work around a JSON bug.
-        if ("null".equals(newName) || "".equals(newName) ) {
+        if ("null".equals(newName) || "".equals(newName)) {
             newName = null;
         }
 
@@ -400,7 +399,7 @@ public class FileUtils extends Plugin {
      * @throws InvalidModificationException
      * @throws JSONException
      */
-    private JSONObject copyFile(File srcFile, File destFile) throws IOException, InvalidModificationException, JSONException  {
+    private JSONObject copyFile(File srcFile, File destFile) throws IOException, InvalidModificationException, JSONException {
         // Renaming a file to an existing directory should fail
         if (destFile.exists() && destFile.isDirectory()) {
             throw new InvalidModificationException("Can't rename a file to a directory");
@@ -478,7 +477,7 @@ public class FileUtils extends Plugin {
         // This weird test is to determine if we are copying or moving a directory into itself.
         // Copy /sdcard/myDir to /sdcard/myDir-backup is okay but
         // Copy /sdcard/myDir to /sdcard/myDir/backup should thow an INVALID_MODIFICATION_ERR
-        if (dest.startsWith(src) && dest.indexOf(File.separator, src.length()-1) != -1) {
+        if (dest.startsWith(src) && dest.indexOf(File.separator, src.length() - 1) != -1) {
             return true;
         }
 
@@ -727,9 +726,9 @@ public class FileUtils extends Plugin {
     private boolean atRootDirectory(String filePath) {
         filePath = stripFileProtocol(filePath);
 
-        if (filePath.equals(Environment.getExternalStorageDirectory().getAbsolutePath() + "/Android/data/" + ctx.getPackageName() + "/cache") ||
-                filePath.equals(Environment.getExternalStorageDirectory().getAbsolutePath()) || 
-                filePath.equals("/data/data/" + ctx.getPackageName())) {
+        if (filePath.equals(Environment.getExternalStorageDirectory().getAbsolutePath() + "/Android/data/" + ctx.getActivity().getPackageName() + "/cache") ||
+                filePath.equals(Environment.getExternalStorageDirectory().getAbsolutePath()) ||
+                filePath.equals("/data/data/" + ctx.getActivity().getPackageName())) {
             return true;
         }
         return false;
@@ -747,7 +746,7 @@ public class FileUtils extends Plugin {
         }
         return filePath;
     }
-    
+
     /**
      * Create a File object from the passed in path
      * 
@@ -793,7 +792,7 @@ public class FileUtils extends Plugin {
             throw new FileNotFoundException("File: " + filePath + " does not exist.");
         }
 
-         JSONObject metadata = new JSONObject();
+        JSONObject metadata = new JSONObject();
         metadata.put("size", file.length());
         metadata.put("type", getMimeType(filePath));
         metadata.put("name", file.getName());
@@ -818,16 +817,16 @@ public class FileUtils extends Plugin {
             fs.put("name", "temporary");
             if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
                 fp = new File(Environment.getExternalStorageDirectory().getAbsolutePath() +
-                    "/Android/data/" + ctx.getPackageName() + "/cache/");
+                        "/Android/data/" + ctx.getActivity().getPackageName() + "/cache/");
                 // Create the cache dir if it doesn't exist.
                 fp.mkdirs();
                 fs.put("root", getEntry(Environment.getExternalStorageDirectory().getAbsolutePath() +
-                        "/Android/data/" + ctx.getPackageName() + "/cache/"));
+                        "/Android/data/" + ctx.getActivity().getPackageName() + "/cache/"));
             } else {
-                fp = new File("/data/data/" + ctx.getPackageName() + "/cache/");
+                fp = new File("/data/data/" + ctx.getActivity().getPackageName() + "/cache/");
                 // Create the cache dir if it doesn't exist.
                 fp.mkdirs();
-                fs.put("root", getEntry("/data/data/" + ctx.getPackageName() + "/cache/"));
+                fs.put("root", getEntry("/data/data/" + ctx.getActivity().getPackageName() + "/cache/"));
             }
         }
         else if (type == PERSISTENT) {
@@ -835,13 +834,13 @@ public class FileUtils extends Plugin {
             if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
                 fs.put("root", getEntry(Environment.getExternalStorageDirectory()));
             } else {
-                fs.put("root", getEntry("/data/data/" + ctx.getPackageName()));
+                fs.put("root", getEntry("/data/data/" + ctx.getActivity().getPackageName()));
             }
         }
         else {
             throw new IOException("No filesystem of type requested");
         }
- 
+
         return fs;
     }
 
@@ -942,7 +941,7 @@ public class FileUtils extends Plugin {
         String contentType = null;
         if (filename.startsWith("content:")) {
             Uri fileUri = Uri.parse(filename);
-            contentType = this.ctx.getContentResolver().getType(fileUri);
+            contentType = this.ctx.getActivity().getContentResolver().getType(fileUri);
         }
         else {
             contentType = getMimeType(filename);
@@ -961,7 +960,7 @@ public class FileUtils extends Plugin {
      */
     public static String getMimeType(String filename) {
         MimeTypeMap map = MimeTypeMap.getSingleton();
-        return map.getMimeTypeFromExtension(map.getFileExtensionFromUrl(filename));
+        return map.getMimeTypeFromExtension(MimeTypeMap.getFileExtensionFromUrl(filename));
     }
 
     /**
@@ -975,14 +974,14 @@ public class FileUtils extends Plugin {
     /**/
     public long write(String filename, String data, int offset) throws FileNotFoundException, IOException {
         filename = stripFileProtocol(filename);
-        
+
         boolean append = false;
         if (offset > 0) {
             this.truncateFile(filename, offset);
             append = true;
         }
 
-        byte [] rawData = data.getBytes();
+        byte[] rawData = data.getBytes();
         ByteArrayInputStream in = new ByteArrayInputStream(rawData);
         FileOutputStream out = new FileOutputStream(filename, append);
         byte buff[] = new byte[rawData.length];
@@ -1007,9 +1006,9 @@ public class FileUtils extends Plugin {
         RandomAccessFile raf = new RandomAccessFile(filename, "rw");
 
         if (raf.length() >= size) {
-               FileChannel channel = raf.getChannel();
-               channel.truncate(size);
-               return size;
+            FileChannel channel = raf.getChannel();
+            channel.truncate(size);
+            return size;
         }
 
         return raf.length();
@@ -1025,7 +1024,7 @@ public class FileUtils extends Plugin {
     private InputStream getPathFromUri(String path) throws FileNotFoundException {
         if (path.startsWith("content")) {
             Uri uri = Uri.parse(path);
-            return ctx.getContentResolver().openInputStream(uri);
+            return ctx.getActivity().getContentResolver().openInputStream(uri);
         }
         else {
             path = stripFileProtocol(path);
@@ -1040,9 +1039,9 @@ public class FileUtils extends Plugin {
      * @param  ctx) the current applicaiton context
      * @return the full path to the file
      */
-    protected static String getRealPathFromURI(Uri contentUri, Activity ctx) {
+    protected static String getRealPathFromURI(Uri contentUri, CordovaInterface ctx) {
         String[] proj = { _DATA };
-        Cursor cursor =  ctx.managedQuery(contentUri, proj, null, null, null);
+        Cursor cursor = ctx.getActivity().managedQuery(contentUri, proj, null, null, null);
         int column_index = cursor.getColumnIndexOrThrow(_DATA);
         cursor.moveToFirst();
         return cursor.getString(column_index);

http://git-wip-us.apache.org/repos/asf/incubator-cordova-android/blob/c8fafa6b/framework/src/org/apache/cordova/GeoListener.java
----------------------------------------------------------------------
diff --git a/framework/src/org/apache/cordova/GeoListener.java b/framework/src/org/apache/cordova/GeoListener.java
index fdfcc42..92a2ca6 100755
--- a/framework/src/org/apache/cordova/GeoListener.java
+++ b/framework/src/org/apache/cordova/GeoListener.java
@@ -21,113 +21,114 @@ package org.apache.cordova;
 import android.content.Context;
 import android.location.Location;
 import android.location.LocationManager;
-import android.webkit.WebView;
+
+//import android.webkit.WebView;
 
 public class GeoListener {
-	public static int PERMISSION_DENIED = 1;
-	public static int POSITION_UNAVAILABLE = 2;
-	public static int TIMEOUT = 3;
+    public static int PERMISSION_DENIED = 1;
+    public static int POSITION_UNAVAILABLE = 2;
+    public static int TIMEOUT = 3;
 
-	String id;							// Listener ID
-	String successCallback;				// 
-	String failCallback;
+    String id;							// Listener ID
+    String successCallback;				// 
+    String failCallback;
     GpsListener mGps;					// GPS listener
     NetworkListener mNetwork;			// Network listener
     LocationManager mLocMan;			// Location manager
-    
+
     private GeoBroker broker;			// GeoBroker object
-	
-	int interval;
-	
-	/**
-	 * Constructor.
-	 * 
-	 * @param id			Listener id
-	 * @param ctx
-	 * @param time			Sampling period in msec
-	 * @param appView
-	 */
-	GeoListener(GeoBroker broker, String id, int time) {
-		this.id = id;
-		this.interval = time;
-		this.broker = broker;
-		this.mGps = null;
-		this.mNetwork = null;
-		this.mLocMan = (LocationManager) broker.ctx.getSystemService(Context.LOCATION_SERVICE);
-
-		// If GPS provider, then create and start GPS listener
-		if (this.mLocMan.getProvider(LocationManager.GPS_PROVIDER) != null) {
-			this.mGps = new GpsListener(broker.ctx, time, this);
-		}
-		
-		// If network provider, then create and start network listener
-		if (this.mLocMan.getProvider(LocationManager.NETWORK_PROVIDER) != null) {
-			this.mNetwork = new NetworkListener(broker.ctx, time, this);
-		}
-	}
-	
-	/**
-	 * Destroy listener.
-	 */
-	public void destroy() {
-		this.stop();
-	}
-	
-	/**
-	 * Location found.  Send location back to JavaScript.
-	 * 
-	 * @param loc
-	 */
-	void success(Location loc) {
-		
-		String params = loc.getLatitude() + "," + loc.getLongitude() + ", " + loc.getAltitude() + 
-				"," + loc.getAccuracy() + "," + loc.getBearing() +
-		 		"," + loc.getSpeed() + "," + loc.getTime();
-		
-		if (id == "global") {
-			this.stop();
-		}
-		this.broker.sendJavascript("navigator._geo.success('" + id + "'," +  params + ");");
-	}
-	
-	/**
-	 * Location failed.  Send error back to JavaScript.
-	 * 
-	 * @param code			The error code
-	 * @param msg			The error message
-	 */
-	void fail(int code, String msg) {
-		this.broker.sendJavascript("navigator._geo.fail('" + this.id + "', '" + code + "', '" + msg + "');");
-		this.stop();
-	}
-	
-	/**
-	 * Start retrieving location.
-	 * 
-	 * @param interval
-	 */
-	void start(int interval) {
-		if (this.mGps != null) {
-			this.mGps.start(interval);
-		}
-		if (this.mNetwork != null) {
-			this.mNetwork.start(interval);
-		}
-		if (this.mNetwork == null && this.mGps == null) {
-			this.fail(POSITION_UNAVAILABLE, "No location providers available.");
-		}
-	}
-	
-	/**
-	 * Stop listening for location.
-	 */
-	void stop() {
-		if (this.mGps != null) {
-			this.mGps.stop();
-		}
-		if (this.mNetwork != null) {
-			this.mNetwork.stop();
-		}
-	}
+
+    int interval;
+
+    /**
+     * Constructor.
+     * 
+     * @param id			Listener id
+     * @param ctx
+     * @param time			Sampling period in msec
+     * @param appView
+     */
+    GeoListener(GeoBroker broker, String id, int time) {
+        this.id = id;
+        this.interval = time;
+        this.broker = broker;
+        this.mGps = null;
+        this.mNetwork = null;
+        this.mLocMan = (LocationManager) broker.ctx.getActivity().getSystemService(Context.LOCATION_SERVICE);
+
+        // If GPS provider, then create and start GPS listener
+        if (this.mLocMan.getProvider(LocationManager.GPS_PROVIDER) != null) {
+            this.mGps = new GpsListener(broker.ctx, time, this);
+        }
+
+        // If network provider, then create and start network listener
+        if (this.mLocMan.getProvider(LocationManager.NETWORK_PROVIDER) != null) {
+            this.mNetwork = new NetworkListener(broker.ctx, time, this);
+        }
+    }
+
+    /**
+     * Destroy listener.
+     */
+    public void destroy() {
+        this.stop();
+    }
+
+    /**
+     * Location found.  Send location back to JavaScript.
+     * 
+     * @param loc
+     */
+    void success(Location loc) {
+
+        String params = loc.getLatitude() + "," + loc.getLongitude() + ", " + loc.getAltitude() +
+                "," + loc.getAccuracy() + "," + loc.getBearing() +
+                "," + loc.getSpeed() + "," + loc.getTime();
+
+        if (id == "global") {
+            this.stop();
+        }
+        this.broker.sendJavascript("navigator._geo.success('" + id + "'," + params + ");");
+    }
+
+    /**
+     * Location failed.  Send error back to JavaScript.
+     * 
+     * @param code			The error code
+     * @param msg			The error message
+     */
+    void fail(int code, String msg) {
+        this.broker.sendJavascript("navigator._geo.fail('" + this.id + "', '" + code + "', '" + msg + "');");
+        this.stop();
+    }
+
+    /**
+     * Start retrieving location.
+     * 
+     * @param interval
+     */
+    void start(int interval) {
+        if (this.mGps != null) {
+            this.mGps.start(interval);
+        }
+        if (this.mNetwork != null) {
+            this.mNetwork.start(interval);
+        }
+        if (this.mNetwork == null && this.mGps == null) {
+            this.fail(POSITION_UNAVAILABLE, "No location providers available.");
+        }
+    }
+
+    /**
+     * Stop listening for location.
+     */
+    void stop() {
+        if (this.mGps != null) {
+            this.mGps.stop();
+        }
+        if (this.mNetwork != null) {
+            this.mNetwork.stop();
+        }
+    }
 
 }