diff --git a/framework/assets/js/app.js b/framework/assets/js/app.js index 6ee03256..5a45cdda 100755 --- a/framework/assets/js/app.js +++ b/framework/assets/js/app.js @@ -3,7 +3,7 @@ * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text. * * Copyright (c) 2005-2010, Nitobi Software Inc. - * Copyright (c) 2010, IBM Corporation + * Copyright (c) 2010-2011, IBM Corporation */ /** @@ -63,3 +63,27 @@ App.prototype.clearHistory = function() { App.prototype.addService = function(serviceType, className) { PhoneGap.exec(null, null, "App", "addService", [serviceType, className]); }; + +/** + * Override the default behavior of the Android back button. + * If overridden, when the back button is pressed, the "backKeyDown" JavaScript event will be fired. + * + * Note: The user should not have to call this method. Instead, when the user + * registers for the "backbutton" event, this is automatically done. + * + * @param override T=override, F=cancel override + */ +App.prototype.overrideBackbutton = function(override) { + PhoneGap.exec(null, null, "App", "overrideBackbutton", [override]); +}; + +/** + * Exit and terminate the application. + */ +App.prototype.exitApp = function() { + return PhoneGap.exec(null, null, "App", "exitApp", []); +}; + +PhoneGap.addConstructor(function() { + navigator.app = window.app = new App(); +}); diff --git a/framework/assets/js/device.js b/framework/assets/js/device.js index 88d5a55d..59d57880 100755 --- a/framework/assets/js/device.js +++ b/framework/assets/js/device.js @@ -3,7 +3,7 @@ * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text. * * Copyright (c) 2005-2010, Nitobi Software Inc. - * Copyright (c) 2010, IBM Corporation + * Copyright (c) 2010-2011, IBM Corporation */ /** @@ -62,30 +62,36 @@ Device.prototype.getInfo = function(successCallback, errorCallback) { }; /* + * DEPRECATED * This is only for Android. * * You must explicitly override the back button. */ Device.prototype.overrideBackButton = function() { - BackButton.override(); + console.log("Device.overrideBackButton() is deprecated. Use App.overrideBackbutton(true)."); + app.overrideBackbutton(true); }; /* + * DEPRECATED * This is only for Android. * * This resets the back button to the default behaviour */ Device.prototype.resetBackButton = function() { - BackButton.reset(); + console.log("Device.resetBackButton() is deprecated. Use App.overrideBackbutton(false)."); + app.overrideBackbutton(false); }; /* + * DEPRECATED * This is only for Android. * * This terminates the activity! */ Device.prototype.exitApp = function() { - BackButton.exitApp(); + console.log("Device.exitApp() is deprecated. Use App.exitApp()."); + app.exitApp(); }; PhoneGap.addConstructor(function() { diff --git a/framework/assets/js/keyevent.js b/framework/assets/js/keyevent.js deleted file mode 100755 index a81bc997..00000000 --- a/framework/assets/js/keyevent.js +++ /dev/null @@ -1,32 +0,0 @@ -/* - * PhoneGap is available under *either* the terms of the modified BSD license *or* the - * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text. - * - * Copyright (c) 2005-2010, Nitobi Software Inc. - * Copyright (c) 2010, IBM Corporation - */ - -function KeyEvent() { -} - -KeyEvent.prototype.backTrigger = function() { - var e = document.createEvent('Events'); - e.initEvent('backKeyDown'); - document.dispatchEvent(e); -}; - -KeyEvent.prototype.menuTrigger = function() { - var e = document.createEvent('Events'); - e.initEvent('menuKeyDown'); - document.dispatchEvent(e); -}; - -KeyEvent.prototype.searchTrigger = function() { - var e = document.createEvent('Events'); - e.initEvent('searchKeyDown'); - document.dispatchEvent(e); -}; - -if (document.keyEvent === null || typeof document.keyEvent === 'undefined') { - window.keyEvent = document.keyEvent = new KeyEvent(); -} diff --git a/framework/assets/js/phonegap.js.base b/framework/assets/js/phonegap.js.base index 750ba752..8866a377 100755 --- a/framework/assets/js/phonegap.js.base +++ b/framework/assets/js/phonegap.js.base @@ -3,7 +3,7 @@ * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text. * * Copyright (c) 2005-2010, Nitobi Software Inc. - * Copyright (c) 2010, IBM Corporation + * Copyright (c) 2010-2011, IBM Corporation */ @@ -293,11 +293,17 @@ PhoneGap.Channel.join(function() { // Start listening for XHR callbacks setTimeout(function() { - if (CallbackServer.usePolling()) { + if (PhoneGap.UsePolling) { PhoneGap.JSCallbackPolling(); } else { - PhoneGap.JSCallback(); + var polling = prompt("usePolling", "gap_callbackServer:"); + if (polling == "true") { + PhoneGap.JSCallbackPolling(); + } + else { + PhoneGap.JSCallback(); + } } }, 1); @@ -307,6 +313,8 @@ PhoneGap.Channel.join(function() { // Fire event to notify that all objects are created PhoneGap.onPhoneGapReady.fire(); + // Fire onDeviceReady event once all constructors have run and PhoneGap info has been + // received from native side, and any user defined initialization channels. PhoneGap.Channel.join(function() { // Turn off app loading dialog @@ -320,22 +328,6 @@ PhoneGap.Channel.join(function() { }, [ PhoneGap.onDOMContentLoaded, PhoneGap.onNativeReady ]); -/** - * Fire onDeviceReady event once all constructors have run and PhoneGap info has been - * received from native side. - */ - /* -PhoneGap.Channel.join(function() { - // Turn off app loading dialog - navigator.notification.activityStop(); - - PhoneGap.onDeviceReady.fire(); - - // Fire the onresume event, since first one happens before JavaScript is loaded - PhoneGap.onResume.fire(); -}, [ PhoneGap.onPhoneGapReady, PhoneGap.onPhoneGapInfoReady]); -*/ - // Listen for DOMContentLoaded and notify our channel subscribers document.addEventListener('DOMContentLoaded', function() { PhoneGap.onDOMContentLoaded.fire(); @@ -355,11 +347,41 @@ document.addEventListener = function(evt, handler, capture) { } } else if (e === 'pause') { PhoneGap.onPause.subscribe(handler); - } else { + } + else { + // If subscribing to Android backbutton + if (e === 'backbutton') { + PhoneGap.exec(null, null, "App", "overrideBackbutton", [true]); + } + PhoneGap.m_document_addEventListener.call(document, evt, handler, capture); } }; +// Intercept calls to document.removeEventListener and watch for events that +// are generated by PhoneGap native code +PhoneGap.m_document_removeEventListener = document.removeEventListener; + +document.removeEventListener = function(evt, handler, capture) { + var e = evt.toLowerCase(); + + // If unsubscribing to Android backbutton + if (e === 'backbutton') { + PhoneGap.exec(null, null, "App", "overrideBackbutton", [false]); + } + + PhoneGap.m_document_removeEventListener.call(document, evt, handler, capture); +}; + +/** + * Method to fire event from native code + */ +PhoneGap.fireEvent = function(type) { + var e = document.createEvent('Events'); + e.initEvent(type); + document.dispatchEvent(e); +}; + /** * If JSON not included, use our own stringify. (Android 1.6) * The restriction on ours is that it must be an array of simple types. @@ -495,8 +517,7 @@ PhoneGap.exec = function(success, fail, service, action, args) { PhoneGap.callbacks[callbackId] = {success:success, fail:fail}; } - // Note: Device returns string, but for some reason emulator returns object - so convert to string. - var r = ""+PluginManager.exec(service, action, callbackId, this.stringify(args), true); + var r = prompt(this.stringify(args), "gap:"+this.stringify([service, action, callbackId, true])); // If a result was returned if (r.length > 0) { @@ -672,6 +693,13 @@ PhoneGap.JSCallbackToken = null; * Java to JavaScript. */ PhoneGap.JSCallback = function() { + + // If polling flag was changed, start using polling from now on + if (PhoneGap.UsePolling) { + PhoneGap.JSCallbackPolling(); + return; + } + var xmlhttp = new XMLHttpRequest(); // Callback function when XMLHttpRequest is ready @@ -718,7 +746,7 @@ PhoneGap.JSCallback = function() { // If error, restart callback server else { console.log("JSCallback Error: Request failed."); - CallbackServer.restartServer(); + prompt("restartServer", "gap_callbackServer:"); PhoneGap.JSCallbackPort = null; PhoneGap.JSCallbackToken = null; setTimeout(PhoneGap.JSCallback, 100); @@ -727,10 +755,10 @@ PhoneGap.JSCallback = function() { }; if (PhoneGap.JSCallbackPort === null) { - PhoneGap.JSCallbackPort = CallbackServer.getPort(); + PhoneGap.JSCallbackPort = prompt("getPort", "gap_callbackServer:"); } if (PhoneGap.JSCallbackToken === null) { - PhoneGap.JSCallbackToken = CallbackServer.getToken(); + PhoneGap.JSCallbackToken = prompt("getToken", "gap_callbackServer:"); } xmlhttp.open("GET", "http://127.0.0.1:"+PhoneGap.JSCallbackPort+"/"+PhoneGap.JSCallbackToken , true); xmlhttp.send(); @@ -742,6 +770,11 @@ PhoneGap.JSCallback = function() { */ PhoneGap.JSCallbackPollingPeriod = 50; +/** + * Flag that can be set by the user to force polling to be used or force XHR to be used. + */ +PhoneGap.UsePolling = false; // T=use polling, F=use XHR + /** * This is only for Android. * @@ -750,7 +783,14 @@ PhoneGap.JSCallbackPollingPeriod = 50; * Java to JavaScript. */ PhoneGap.JSCallbackPolling = function() { - var msg = CallbackServer.getJavascript(); + + // If polling flag was changed, stop using polling from now on + if (!PhoneGap.UsePolling) { + PhoneGap.JSCallback(); + return; + } + + var msg = prompt("", "gap_poll:"); if (msg) { setTimeout(function() { try { diff --git a/framework/src/com/phonegap/App.java b/framework/src/com/phonegap/App.java index 4f88f30a..cb3342bc 100755 --- a/framework/src/com/phonegap/App.java +++ b/framework/src/com/phonegap/App.java @@ -3,7 +3,7 @@ * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text. * * Copyright (c) 2005-2010, Nitobi Software Inc. - * Copyright (c) 2010, IBM Corporation + * Copyright (c) 2010-2011, IBM Corporation */ package com.phonegap; @@ -17,7 +17,7 @@ import com.phonegap.api.PluginResult; * This class exposes methods in DroidGap that can be called from JavaScript. */ public class App extends Plugin { - + /** * Executes the request and returns PluginResult. * @@ -45,6 +45,16 @@ public class App extends Plugin { } else if (action.equals("addService")) { this.addService(args.getString(0), args.getString(1)); + } + else if (action.equals("overrideBackbutton")) { + this.overrideBackbutton(args.getBoolean(0)); + } + else if (action.equals("isBackbuttonOverridden")) { + boolean b = this.isBackbuttonOverridden(); + return new PluginResult(status, b); + } + else if (action.equals("exitApp")) { + this.exitApp(); } return new PluginResult(status, result); } catch (JSONException e) { @@ -132,4 +142,32 @@ public class App extends Plugin { public void addService(String serviceType, String className) { this.ctx.addService(serviceType, className); } + + /** + * Override the default behavior of the Android back button. + * If overridden, when the back button is pressed, the "backKeyDown" JavaScript event will be fired. + * + * @param override T=override, F=cancel override + */ + public void overrideBackbutton(boolean override) { + System.out.println("WARNING: Back Button Default Behaviour will be overridden. The backbutton event will be fired!"); + ((DroidGap)this.ctx).bound = override; + } + + /** + * Return whether the Android back button is overridden by the user. + * + * @return boolean + */ + public boolean isBackbuttonOverridden() { + return ((DroidGap)this.ctx).bound; + } + + /** + * Exit the Android application. + */ + public void exitApp() { + ((DroidGap)this.ctx).finish(); + } + } diff --git a/framework/src/com/phonegap/BrowserKey.java b/framework/src/com/phonegap/BrowserKey.java deleted file mode 100755 index 940f641c..00000000 --- a/framework/src/com/phonegap/BrowserKey.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * PhoneGap is available under *either* the terms of the modified BSD license *or* the - * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text. - * - * Copyright (c) 2005-2010, Nitobi Software Inc. - */ -package com.phonegap; - -import android.app.Activity; -import android.util.Log; -import android.webkit.WebView; - -/* - * This class literally exists to protect DroidGap from Javascript directly. - * - * - */ - -public class BrowserKey { - - DroidGap mAction; - boolean bound; - WebView mView; - - BrowserKey(WebView view, DroidGap action) - { - bound = false; - mAction = action; - } - - public void override() - { - Log.d("PhoneGap", "WARNING: Back Button Default Behaviour will be overridden. The backKeyDown event will be fired!"); - bound = true; - } - - public boolean isBound() - { - return bound; - } - - public void reset() - { - bound = false; - } - - public void exitApp() - { - mAction.finish(); - } -} diff --git a/framework/src/com/phonegap/CallbackServer.java b/framework/src/com/phonegap/CallbackServer.java index e99692d0..2fce8a1e 100755 --- a/framework/src/com/phonegap/CallbackServer.java +++ b/framework/src/com/phonegap/CallbackServer.java @@ -69,7 +69,7 @@ public class CallbackServer implements Runnable { /** * Indicates that polling should be used instead of XHR. */ - private boolean usePolling; + private boolean usePolling = true; /** * Security token to prevent other apps from accessing this callback server via XHR diff --git a/framework/src/com/phonegap/DroidGap.java b/framework/src/com/phonegap/DroidGap.java index 88d275dd..8829dfee 100755 --- a/framework/src/com/phonegap/DroidGap.java +++ b/framework/src/com/phonegap/DroidGap.java @@ -3,11 +3,12 @@ * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text. * * Copyright (c) 2005-2010, Nitobi Software Inc. - * Copyright (c) 2010, IBM Corporation + * Copyright (c) 2010-2011, IBM Corporation */ package com.phonegap; import org.json.JSONArray; +import org.json.JSONException; import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; @@ -26,6 +27,7 @@ import android.view.Window; import android.view.WindowManager; import android.webkit.JsResult; import android.webkit.WebChromeClient; +import android.webkit.JsPromptResult; import android.webkit.WebSettings; import android.webkit.WebStorage; import android.webkit.WebView; @@ -110,7 +112,7 @@ public class DroidGap extends PhonegapActivity { protected WebViewClient webViewClient; private LinearLayout root; - private BrowserKey mKey; + boolean bound = false; public CallbackServer callbackServer; protected PluginManager pluginManager; protected boolean cancelLoadUrl = false; @@ -267,12 +269,6 @@ public class DroidGap extends PhonegapActivity { private void bindBrowser(WebView appView) { this.callbackServer = new CallbackServer(); this.pluginManager = new PluginManager(appView, this); - this.mKey = new BrowserKey(appView, this); - - // This creates the new javascript interfaces for PhoneGap - appView.addJavascriptInterface(this.pluginManager, "PluginManager"); - appView.addJavascriptInterface(this.mKey, "BackButton"); - appView.addJavascriptInterface(this.callbackServer, "CallbackServer"); this.addService("App", "com.phonegap.App"); this.addService("Geolocation", "com.phonegap.GeoBroker"); @@ -659,10 +655,6 @@ public class DroidGap extends PhonegapActivity { // Load blank page so that JavaScript onunload is called this.appView.loadUrl("about:blank"); - // Clean up objects - if (this.mKey != null) { - } - // Forward to plugins this.pluginManager.onDestroy(); @@ -764,6 +756,69 @@ public class DroidGap extends PhonegapActivity { return true; } + /** + * Tell the client to display a prompt dialog to the user. + * If the client returns true, WebView will assume that the client will + * handle the prompt dialog and call the appropriate JsPromptResult method. + * + * @param view + * @param url + * @param message + * @param defaultValue + * @param result + */ + @Override + public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) { + + // Calling PluginManager.exec() to call a native service using + // prompt(this.stringify(args), "gap:"+this.stringify([service, action, callbackId, true])); + if (defaultValue.substring(0, 4).equals("gap:")) { + JSONArray array; + try { + array = new JSONArray(defaultValue.substring(4)); + String service = array.getString(0); + String action = array.getString(1); + String callbackId = array.getString(2); + boolean async = array.getBoolean(3); + String r = pluginManager.exec(service, action, callbackId, message, async); + result.confirm(r); + } catch (JSONException e) { + e.printStackTrace(); + } + } + + // Polling for JavaScript messages + else if (defaultValue.equals("gap_poll:")) { + String r = callbackServer.getJavascript(); + result.confirm(r); + } + + // Calling into CallbackServer + else if (defaultValue.equals("gap_callbackServer:")) { + String r = ""; + if (message.equals("usePolling")) { + r = ""+callbackServer.usePolling(); + } + else if (message.equals("restartServer")) { + callbackServer.restartServer(); + } + else if (message.equals("getPort")) { + r = Integer.toString(callbackServer.getPort()); + } + else if (message.equals("getToken")) { + r = callbackServer.getToken(); + } + result.confirm(r); + } + + // Show dialog + else { + //@TODO: + result.confirm(""); + } + return true; + } + } /** @@ -1016,8 +1071,8 @@ public class DroidGap extends PhonegapActivity { if (keyCode == KeyEvent.KEYCODE_BACK) { // If back key is bound, then send event to JavaScript - if (mKey.isBound()) { - this.appView.loadUrl("javascript:document.keyEvent.backTrigger()"); + if (this.bound) { + this.appView.loadUrl("javascript:PhoneGap.fireEvent('backbutton');"); } // If not bound @@ -1037,12 +1092,12 @@ public class DroidGap extends PhonegapActivity { // If menu key else if (keyCode == KeyEvent.KEYCODE_MENU) { - appView.loadUrl("javascript:keyEvent.menuTrigger()"); + this.appView.loadUrl("javascript:PhoneGap.fireEvent('menubutton');"); } // If search key else if (keyCode == KeyEvent.KEYCODE_SEARCH) { - appView.loadUrl("javascript:keyEvent.searchTrigger()"); + this.appView.loadUrl("javascript:PhoneGap.fireEvent('searchbutton');"); } return false;