From ca5e141b5b261e1d58934a63681a8ec45ca0c6a3 Mon Sep 17 00:00:00 2001 From: macdonst Date: Fri, 27 May 2011 01:50:45 +0800 Subject: [PATCH 01/59] Changing Media class to return seconds The media commands getCurrentPosition() and getDuration() will now return seconds (float) instead of milliseconds to line up with iOS and the docs. --- framework/src/com/phonegap/AudioHandler.java | 14 ++++++------ framework/src/com/phonegap/AudioPlayer.java | 23 +++++++++++++------- 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/framework/src/com/phonegap/AudioHandler.java b/framework/src/com/phonegap/AudioHandler.java index cd16b1b9..9673b076 100755 --- a/framework/src/com/phonegap/AudioHandler.java +++ b/framework/src/com/phonegap/AudioHandler.java @@ -73,12 +73,12 @@ public class AudioHandler extends Plugin { this.stopPlayingAudio(args.getString(0)); } else if (action.equals("getCurrentPositionAudio")) { - long l = this.getCurrentPositionAudio(args.getString(0)); - return new PluginResult(status, l); + float f = this.getCurrentPositionAudio(args.getString(0)); + return new PluginResult(status, f); } else if (action.equals("getDurationAudio")) { - long l = this.getDurationAudio(args.getString(0), args.getString(1)); - return new PluginResult(status, l); + float f = this.getDurationAudio(args.getString(0), args.getString(1)); + return new PluginResult(status, f); } else if (action.equals("release")) { boolean b = this.release(args.getString(0)); @@ -230,10 +230,10 @@ public class AudioHandler extends Plugin { * @param id The id of the audio player * @return position in msec */ - public long getCurrentPositionAudio(String id) { + public float getCurrentPositionAudio(String id) { AudioPlayer audio = this.players.get(id); if (audio != null) { - return(audio.getCurrentPosition()); + return(audio.getCurrentPosition()/1000.0f); } return -1; } @@ -245,7 +245,7 @@ public class AudioHandler extends Plugin { * @param file The name of the audio file. * @return The duration in msec. */ - public long getDurationAudio(String id, String file) { + public float getDurationAudio(String id, String file) { // Get audio file AudioPlayer audio = this.players.get(id); diff --git a/framework/src/com/phonegap/AudioPlayer.java b/framework/src/com/phonegap/AudioPlayer.java index f0b3da14..8f84a90e 100755 --- a/framework/src/com/phonegap/AudioPlayer.java +++ b/framework/src/com/phonegap/AudioPlayer.java @@ -15,6 +15,7 @@ import android.media.MediaPlayer.OnErrorListener; import android.media.MediaRecorder; import android.media.MediaPlayer.OnCompletionListener; import android.media.MediaPlayer.OnPreparedListener; +import android.os.Environment; /** * This class implements the audio playback and recording capabilities used by PhoneGap. @@ -53,7 +54,7 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On private String id; // The id of this player (used to identify Media object in JavaScript) private int state = MEDIA_NONE; // State of recording or playback private String audioFile = null; // File name to play or record to - private long duration = -1; // Duration of audio + private float duration = -1; // Duration of audio private MediaRecorder recorder = null; // Audio recording object private String tempFile = null; // Temporary recording file name @@ -70,10 +71,7 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On public AudioPlayer(AudioHandler handler, String id) { this.handler = handler; this.id = id; - - // YES, I know this is bad, but I can't do it the right way because Google didn't have the - // foresight to add android.os.environment.getExternalDataDirectory until Android 2.2 - this.tempFile = "/sdcard/tmprecording.mp3"; + this.tempFile = Environment.getExternalStorageDirectory().getAbsolutePath() + "/tmprecording.mp3"; } /** @@ -209,7 +207,7 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On this.mPlayer.prepare(); // Get duration - this.duration = this.mPlayer.getDuration(); + this.duration = getDurationInSeconds(); } } catch (Exception e) { @@ -319,7 +317,7 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On * -1=can't be determined * -2=not allowed */ - public long getDuration(String file) { + public float getDuration(String file) { // Can't get duration of recording if (this.recorder != null) { @@ -362,7 +360,7 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On } // Save off duration - this.duration = this.mPlayer.getDuration(); + this.duration = getDurationInSeconds(); this.prepareOnly = false; // Send status notification to JavaScript @@ -370,6 +368,15 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On } + /** + * By default Android returns the length of audio in mills but we want seconds + * + * @return length of clip in seconds + */ + private float getDurationInSeconds() { + return (this.mPlayer.getDuration() / 1000.0f); + } + /** * Callback to be invoked when there has been an error during an asynchronous operation * (other errors will throw exceptions at method call time). From 4280fdf252eab9d092ec2a8036bda8c24707525c Mon Sep 17 00:00:00 2001 From: Fil Maj Date: Thu, 26 May 2011 12:42:44 -0700 Subject: [PATCH 02/59] Fix for ticket #58: Certain 1.5/1.6 devices would throw a FileNotFoundException when taking pictures. Patch submitted by Agustin of AVANTIC (thanks!). --- framework/src/com/phonegap/CameraLauncher.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/framework/src/com/phonegap/CameraLauncher.java b/framework/src/com/phonegap/CameraLauncher.java index 24e72d7c..ddeb9e16 100755 --- a/framework/src/com/phonegap/CameraLauncher.java +++ b/framework/src/com/phonegap/CameraLauncher.java @@ -166,7 +166,14 @@ public class CameraLauncher extends Plugin { if (resultCode == Activity.RESULT_OK) { try { // Read in bitmap of captured image - Bitmap bitmap = android.provider.MediaStore.Images.Media.getBitmap(this.ctx.getContentResolver(), imageUri); + Bitmap bitmap; + try { + bitmap = android.provider.MediaStore.Images.Media.getBitmap(this.ctx.getContentResolver(), imageUri); + } catch (FileNotFoundException e) { + Uri uri = intent.getData(); + android.content.ContentResolver resolver = this.ctx.getContentResolver(); + bitmap = android.graphics.BitmapFactory.decodeStream(resolver.openInputStream(uri)); + } // If sending base64 image back if (destType == DATA_URL) { From 9036eb8fcce4c10fb3ddbf182d993ea7fcdab3c6 Mon Sep 17 00:00:00 2001 From: macdonst Date: Mon, 30 May 2011 15:12:31 -0400 Subject: [PATCH 03/59] Issue #94: feature request: Event for Keyboard show/hide --- framework/src/com/phonegap/DroidGap.java | 67 +++++++++++++++++++++++- 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/framework/src/com/phonegap/DroidGap.java b/framework/src/com/phonegap/DroidGap.java index 91eeb173..e0d42d66 100755 --- a/framework/src/com/phonegap/DroidGap.java +++ b/framework/src/com/phonegap/DroidGap.java @@ -9,6 +9,8 @@ package com.phonegap; import org.json.JSONArray; import org.json.JSONException; + +import android.app.Activity; import android.app.AlertDialog; import android.widget.EditText; import android.content.Context; @@ -17,6 +19,7 @@ import android.content.Intent; import android.content.res.Configuration; import android.graphics.Bitmap; import android.graphics.Color; +import android.graphics.Rect; import android.media.AudioManager; import android.net.Uri; import android.os.Bundle; @@ -168,7 +171,7 @@ public class DroidGap extends PhonegapActivity { 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! - root = new LinearLayout(this); + root = new LinearLayoutSoftKeyboardDetect(this); root.setOrientation(LinearLayout.VERTICAL); root.setBackgroundColor(Color.BLACK); root.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, @@ -1276,4 +1279,66 @@ public class DroidGap extends PhonegapActivity { } }); } + + /** + * We are providing this class to detect when the soft keyboard is shown + * and hidden in the web view. + */ + class LinearLayoutSoftKeyboardDetect extends LinearLayout { + + private static final String LOG_TAG = "SoftKeyboardDetect"; + + private int oldHeight = 0; // Need to save the old height as not to send redundant events + + public LinearLayoutSoftKeyboardDetect(Context context) { + super(context); + } + + @Override + /** + * Start listening to new measurement events. Fire events when the height + * gets smaller fire a show keyboard event and when height gets bigger fire + * a hide keyboard event. + * + * Note: We are using callbackServer.sendJavascript() instead of + * this.appView.loadUrl() as changing the URL of the app would cause the + * soft keyboard to go away. + * + * @param widthMeasureSpec + * @param heightMeasureSpec + */ + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + + Log.d(LOG_TAG, "We are in our onMeasure method"); + + // Get the current height of the visible part of the screen. + // This height will not included the status bar. + int height = MeasureSpec.getSize(heightMeasureSpec); + + Log.d(LOG_TAG, "Old Height = " + oldHeight); + Log.d(LOG_TAG, "Height = " + height); + + // If the oldHeight = 0 then this is the first measure event as the app starts up. + // If oldHeight == height then we got a measurement change that doesn't affect us. + if (oldHeight == 0 || oldHeight == height) { + Log.d(LOG_TAG, "Ignore this event"); + } + // If the height as gotten bigger then we will assume the soft keyboard has + // gone away. + else if (height > oldHeight) { + Log.d(LOG_TAG, "Throw hide keyboard event"); + callbackServer.sendJavascript("PhoneGap.fireEvent('hidekeyboard');"); + } + // If the height as gotten smaller then we will assume the soft keyboard has + // been displayed. + else if (height < oldHeight) { + Log.d(LOG_TAG, "Throw show keyboard event"); + callbackServer.sendJavascript("PhoneGap.fireEvent('showkeyboard');"); + } + + // Update the old height for the next event + oldHeight = height; + } + } } From 10e1808c5671ae15b11fa5bb9eb48f3228b2d8f8 Mon Sep 17 00:00:00 2001 From: Bryce Curtis Date: Tue, 31 May 2011 15:11:02 -0500 Subject: [PATCH 04/59] Clean up CallbackServer when about:blank page has loaded. This fixes errors when shutting down. --- framework/src/com/phonegap/DroidGap.java | 30 ++++++++++++++---------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/framework/src/com/phonegap/DroidGap.java b/framework/src/com/phonegap/DroidGap.java index 91eeb173..fae84535 100755 --- a/framework/src/com/phonegap/DroidGap.java +++ b/framework/src/com/phonegap/DroidGap.java @@ -658,21 +658,18 @@ public class DroidGap extends PhonegapActivity { if (this.appView != null) { - // Make sure pause event is sent if onPause hasn't been called before onDestroy - this.appView.loadUrl("javascript:try{PhoneGap.onPause.fire();}catch(e){};"); + // Make sure pause event is sent if onPause hasn't been called before onDestroy + this.appView.loadUrl("javascript:try{PhoneGap.onPause.fire();}catch(e){};"); - // Send destroy event to JavaScript - this.appView.loadUrl("javascript:try{PhoneGap.onDestroy.fire();}catch(e){};"); + // Send destroy event to JavaScript + this.appView.loadUrl("javascript:try{PhoneGap.onDestroy.fire();}catch(e){};"); - // Load blank page so that JavaScript onunload is called - this.appView.loadUrl("about:blank"); + // Load blank page so that JavaScript onunload is called + this.appView.loadUrl("about:blank"); - // Forward to plugins - this.pluginManager.onDestroy(); + // Forward to plugins + this.pluginManager.onDestroy(); - if (this.callbackServer != null) { - this.callbackServer.destroy(); - } } } @@ -1065,7 +1062,9 @@ public class DroidGap extends PhonegapActivity { // Try firing the onNativeReady event in JS. If it fails because the JS is // not loaded yet then just set a flag so that the onNativeReady can be fired // from the JS side when the JS gets to that code. - appView.loadUrl("javascript:try{ PhoneGap.onNativeReady.fire();}catch(e){_nativeReady = true;}"); + if (!url.equals("about:blank")) { + appView.loadUrl("javascript:try{ PhoneGap.onNativeReady.fire();}catch(e){_nativeReady = true;}"); + } // Make app view visible appView.setVisibility(View.VISIBLE); @@ -1081,6 +1080,13 @@ public class DroidGap extends PhonegapActivity { this.ctx.clearHistory = false; this.ctx.appView.clearHistory(); } + + // Shutdown if blank loaded + if (url.equals("about:blank")) { + if (this.ctx.callbackServer != null) { + this.ctx.callbackServer.destroy(); + } + } } /** From 39ec9c095d12ee1e2afbfcae294ef760770242ca Mon Sep 17 00:00:00 2001 From: Bryce Curtis Date: Tue, 31 May 2011 15:13:54 -0500 Subject: [PATCH 05/59] Need to unregister for network intent receiver on shutdown to prevent leaks. --- .../src/com/phonegap/NetworkManager.java | 40 ++++++++++++++----- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/framework/src/com/phonegap/NetworkManager.java b/framework/src/com/phonegap/NetworkManager.java index 779e1c71..0a2fdb1b 100755 --- a/framework/src/com/phonegap/NetworkManager.java +++ b/framework/src/com/phonegap/NetworkManager.java @@ -57,13 +57,15 @@ public class NetworkManager extends Plugin { private static final String LOG_TAG = "NetworkManager"; private String connectionCallbackId; - ConnectivityManager sockMan; - TelephonyManager telephonyManager; + ConnectivityManager sockMan; + TelephonyManager telephonyManager; + BroadcastReceiver receiver; /** * Constructor. */ public NetworkManager() { + this.receiver = null; } /** @@ -76,18 +78,23 @@ public class NetworkManager extends Plugin { super.setContext(ctx); this.sockMan = (ConnectivityManager) ctx.getSystemService(Context.CONNECTIVITY_SERVICE); this.telephonyManager = ((TelephonyManager) ctx.getSystemService(Context.TELEPHONY_SERVICE)); - + this.connectionCallbackId = null; + // We need to listen to connectivity events to update navigator.connection IntentFilter intentFilter = new IntentFilter() ; intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); - ctx.registerReceiver(new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - updateConnectionInfo((NetworkInfo) intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO)); - } - }, intentFilter); - } + if (this.receiver == null) { + this.receiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + updateConnectionInfo((NetworkInfo) intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO)); + } + }; + ctx.registerReceiver(this.receiver, intentFilter); + } + } + /** * Executes the request and returns PluginResult. * @@ -135,6 +142,19 @@ public class NetworkManager extends Plugin { // All methods take a while, so always use async return false; } + + /** + * Stop network receiver. + */ + public void onDestroy() { + if (this.receiver != null) { + try { + this.ctx.unregisterReceiver(this.receiver); + } catch (Exception e) { + System.out.println("Error unregistering network receiver: " + e.getMessage()); + } + } + } //-------------------------------------------------------------------------- // LOCAL METHODS From 431c80782ee002dafb7af09e1b37e59116410e4d Mon Sep 17 00:00:00 2001 From: Joe Bowser Date: Tue, 31 May 2011 15:38:03 -0700 Subject: [PATCH 06/59] Changing the layout class so it has the screen dimensions to take into account Device Orientation --- framework/src/com/phonegap/DroidGap.java | 33 ++++++++++++++++++++---- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/framework/src/com/phonegap/DroidGap.java b/framework/src/com/phonegap/DroidGap.java index e0d42d66..a9a06b4c 100755 --- a/framework/src/com/phonegap/DroidGap.java +++ b/framework/src/com/phonegap/DroidGap.java @@ -24,6 +24,7 @@ import android.media.AudioManager; import android.net.Uri; import android.os.Bundle; import android.util.Log; +import android.view.Display; import android.view.KeyEvent; import android.view.View; import android.view.ViewGroup; @@ -171,7 +172,11 @@ public class DroidGap extends PhonegapActivity { 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! - root = new LinearLayoutSoftKeyboardDetect(this); + Display display = getWindowManager().getDefaultDisplay(); + int width = display.getWidth(); + int height = display.getHeight(); + + root = new LinearLayoutSoftKeyboardDetect(this, width, height); root.setOrientation(LinearLayout.VERTICAL); root.setBackgroundColor(Color.BLACK); root.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, @@ -1289,9 +1294,14 @@ public class DroidGap extends PhonegapActivity { private static final String LOG_TAG = "SoftKeyboardDetect"; private int oldHeight = 0; // Need to save the old height as not to send redundant events - - public LinearLayoutSoftKeyboardDetect(Context context) { - super(context); + private int oldWidth = 0; // Need to save old width for orientation change + private int screenWidth = 0; + private int screenHeight = 0; + + public LinearLayoutSoftKeyboardDetect(Context context, int width, int height) { + super(context); + screenWidth = width; + screenHeight = height; } @Override @@ -1315,15 +1325,27 @@ public class DroidGap extends PhonegapActivity { // Get the current height of the visible part of the screen. // This height will not included the status bar. int height = MeasureSpec.getSize(heightMeasureSpec); + int width = MeasureSpec.getSize(widthMeasureSpec); Log.d(LOG_TAG, "Old Height = " + oldHeight); - Log.d(LOG_TAG, "Height = " + height); + Log.d(LOG_TAG, "Height = " + height); + Log.d(LOG_TAG, "Old Width = " + oldWidth); + Log.d(LOG_TAG, "Width = " + width); + // If the oldHeight = 0 then this is the first measure event as the app starts up. // If oldHeight == height then we got a measurement change that doesn't affect us. if (oldHeight == 0 || oldHeight == height) { Log.d(LOG_TAG, "Ignore this event"); } + // Account for orientation change and ignore this event/Fire orientation change + else if(screenHeight == width) + { + int tmp_var = screenHeight; + screenHeight = screenWidth; + screenWidth = tmp_var; + Log.d(LOG_TAG, "Orientation Change"); + } // If the height as gotten bigger then we will assume the soft keyboard has // gone away. else if (height > oldHeight) { @@ -1339,6 +1361,7 @@ public class DroidGap extends PhonegapActivity { // Update the old height for the next event oldHeight = height; + oldWidth = width; } } } From 992ee0bca4e8a6485e5a4c5e2a0c106f6196072b Mon Sep 17 00:00:00 2001 From: macdonst Date: Wed, 1 Jun 2011 02:54:18 +0800 Subject: [PATCH 07/59] Issue #80: Unable to open large json files on android 2.2 + phonegap 0.9.5 I could not get rid of the url encoding and decoding without hampering some users ability to pass non-ascii characters back to JavaScript. However, I was able to reduce the amount of data being passed from Java to JavaScript by 40% by decoding common characters that occur in JSON and XML. These characters will survive the round trip just fine and don't need to be encoded. This is the best solution I could come up with. You won't be able to read files as large as you could in 0.9.4 but it will get close and it will support non-ascii characters. --- framework/assets/js/phonegap.js.base | 4 +- .../src/com/phonegap/CallbackServer.java | 46 ++++++++++++++++++- 2 files changed, 46 insertions(+), 4 deletions(-) diff --git a/framework/assets/js/phonegap.js.base b/framework/assets/js/phonegap.js.base index 068b46e5..5d06bf99 100755 --- a/framework/assets/js/phonegap.js.base +++ b/framework/assets/js/phonegap.js.base @@ -767,8 +767,8 @@ PhoneGap.JSCallback = function() { // If callback has JavaScript statement to execute if (xmlhttp.status === 200) { - // Need to url decode the response and replace %20 with a space - var msg = decodeURIComponent(xmlhttp.responseText.replace(/\+/g, '%20')); + // Need to url decode the response + var msg = decodeURIComponent(xmlhttp.responseText); setTimeout(function() { try { var t = eval(msg); diff --git a/framework/src/com/phonegap/CallbackServer.java b/framework/src/com/phonegap/CallbackServer.java index 859ac40a..66c282ec 100755 --- a/framework/src/com/phonegap/CallbackServer.java +++ b/framework/src/com/phonegap/CallbackServer.java @@ -11,11 +11,14 @@ import java.io.BufferedReader; import java.io.DataOutputStream; import java.io.IOException; import java.io.InputStreamReader; +import java.io.UnsupportedEncodingException; import java.net.ServerSocket; import java.net.Socket; import java.net.URLEncoder; import java.util.LinkedList; +import android.util.Log; + /** * This class provides a way for Java to run JavaScript in the web page that has loaded PhoneGap. * The CallbackServer class implements an XHR server and a polling server with a list of JavaScript @@ -42,6 +45,8 @@ import java.util.LinkedList; */ public class CallbackServer implements Runnable { + private static final String LOG_TAG = "CallbackServer"; + /** * The list of JavaScript statements to be sent to JavaScript. */ @@ -224,8 +229,9 @@ public class CallbackServer implements Runnable { //System.out.println("CallbackServer -- sending item"); response = "HTTP/1.1 200 OK\r\n\r\n"; String js = this.getJavascript(); - if (js != null) - response += URLEncoder.encode(js, "UTF-8"); + if (js != null) { + response += encode(js); + } } } else { @@ -321,4 +327,40 @@ public class CallbackServer implements Runnable { } } + /** + * This will encode the return value to JavaScript. We revert the encoding for + * common characters that don't require encoding to reduce the size of the string + * being passed to JavaScript. + * + * @param value to be encoded + * @return encoded string + */ + public static String encode(String value) { + String encoded = null; + try { + encoded = URLEncoder.encode(value, "UTF-8") + .replaceAll("\\+", " ") + .replaceAll("\\%21", "!") + .replaceAll("\\%22", "\"") + .replaceAll("\\%27", "'") + .replaceAll("\\%28", "(") + .replaceAll("\\%29", ")") + .replaceAll("\\%2C", ",") + .replaceAll("\\%3C", "<") + .replaceAll("\\%3D", "=") + .replaceAll("\\%3E", ">") + .replaceAll("\\%3F", "?") + .replaceAll("\\%40", "@") + .replaceAll("\\%5B", "[") + .replaceAll("\\%5D", "]") + .replaceAll("\\%7B", "{") + .replaceAll("\\%7D", "}") + .replaceAll("\\%3A", ":") + .replaceAll("\\%7E", "~"); + } catch (UnsupportedEncodingException e) { + Log.e(LOG_TAG, e.getMessage(), e); + } + return encoded; + } + } From 7d41646a35b6c50ac2584a9ea2ea8c7e67a4e008 Mon Sep 17 00:00:00 2001 From: macdonst Date: Thu, 2 Jun 2011 01:46:27 +0800 Subject: [PATCH 08/59] Improve performance of our encoding Move from using String.replaceAll() to a modified version or URLEncoder.encode(). --- .../src/com/phonegap/CallbackServer.java | 113 ++++++++++++------ 1 file changed, 77 insertions(+), 36 deletions(-) diff --git a/framework/src/com/phonegap/CallbackServer.java b/framework/src/com/phonegap/CallbackServer.java index 66c282ec..2c2a9c84 100755 --- a/framework/src/com/phonegap/CallbackServer.java +++ b/framework/src/com/phonegap/CallbackServer.java @@ -230,7 +230,7 @@ public class CallbackServer implements Runnable { response = "HTTP/1.1 200 OK\r\n\r\n"; String js = this.getJavascript(); if (js != null) { - response += encode(js); + response += encode(js, "UTF-8"); } } } @@ -327,40 +327,81 @@ public class CallbackServer implements Runnable { } } - /** - * This will encode the return value to JavaScript. We revert the encoding for - * common characters that don't require encoding to reduce the size of the string - * being passed to JavaScript. - * - * @param value to be encoded - * @return encoded string - */ - public static String encode(String value) { - String encoded = null; - try { - encoded = URLEncoder.encode(value, "UTF-8") - .replaceAll("\\+", " ") - .replaceAll("\\%21", "!") - .replaceAll("\\%22", "\"") - .replaceAll("\\%27", "'") - .replaceAll("\\%28", "(") - .replaceAll("\\%29", ")") - .replaceAll("\\%2C", ",") - .replaceAll("\\%3C", "<") - .replaceAll("\\%3D", "=") - .replaceAll("\\%3E", ">") - .replaceAll("\\%3F", "?") - .replaceAll("\\%40", "@") - .replaceAll("\\%5B", "[") - .replaceAll("\\%5D", "]") - .replaceAll("\\%7B", "{") - .replaceAll("\\%7D", "}") - .replaceAll("\\%3A", ":") - .replaceAll("\\%7E", "~"); - } catch (UnsupportedEncodingException e) { - Log.e(LOG_TAG, e.getMessage(), e); - } - return encoded; - } + /* The Following code has been modified from original implementation of URLEncoder */ + /* start */ + + /* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + static final String digits = "0123456789ABCDEF"; + + /** + * This will encode the return value to JavaScript. We revert the encoding for + * common characters that don't require encoding to reduce the size of the string + * being passed to JavaScript. + * + * @param s to be encoded + * @param enc encoding type + * @return encoded string + */ + public static String encode(String s, String enc) throws UnsupportedEncodingException { + if (s == null || enc == null) { + throw new NullPointerException(); + } + // check for UnsupportedEncodingException + "".getBytes(enc); + + // Guess a bit bigger for encoded form + StringBuilder buf = new StringBuilder(s.length() + 16); + int start = -1; + for (int i = 0; i < s.length(); i++) { + char ch = s.charAt(i); + if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') + || (ch >= '0' && ch <= '9') + || " .-*_'(),<>=?@[]{}:~\"\\/;!".indexOf(ch) > -1) { + if (start >= 0) { + convert(s.substring(start, i), buf, enc); + start = -1; + } + if (ch != ' ') { + buf.append(ch); + } else { + buf.append(' '); + } + } else { + if (start < 0) { + start = i; + } + } + } + if (start >= 0) { + convert(s.substring(start, s.length()), buf, enc); + } + return buf.toString(); + } + + private static void convert(String s, StringBuilder buf, String enc) throws UnsupportedEncodingException { + byte[] bytes = s.getBytes(enc); + for (int j = 0; j < bytes.length; j++) { + buf.append('%'); + buf.append(digits.charAt((bytes[j] & 0xf0) >> 4)); + buf.append(digits.charAt(bytes[j] & 0xf)); + } + } + + /* end */ } From 8d1722ad67cccc9957bb63bf12b2ac04a70c97b3 Mon Sep 17 00:00:00 2001 From: Nitobi Date: Fri, 3 Jun 2011 10:59:48 -0700 Subject: [PATCH 09/59] Fixed droidgap update command --- lib/update.rb | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/lib/update.rb b/lib/update.rb index aa1459a1..23e57a70 100755 --- a/lib/update.rb +++ b/lib/update.rb @@ -33,12 +33,19 @@ class Update # TODO need to allow for www import inc icon def copy_libs puts "Copying over libraries and assets..." - - FileUtils.mkdir_p File.join(@path, "libs") - FileUtils.cp File.join(@framework_dir, "phonegap.jar"), File.join(@path, "libs") + version = IO.read(File.join(@framework_dir, '../VERSION')) - FileUtils.mkdir_p File.join(@path, "assets", "www") - FileUtils.cp File.join(@framework_dir, "assets", "www", "phonegap.js"), File.join(@path, "assets", "www") + FileUtils.cp File.join(@framework_dir, "phonegap.#{ version }.jar"), File.join(@path, "libs") + + # concat JS and put into www folder. this can be overridden in the config.xml via @app_js_dir + js_dir = File.join(@framework_dir, "assets", "js") + phonegapjs = IO.read(File.join(js_dir, 'phonegap.js.base')) + Dir.new(js_dir).entries.each do |script| + next if script[0].chr == "." or script == "phonegap.js.base" + phonegapjs << IO.read(File.join(js_dir, script)) + phonegapjs << "\n\n" + end + File.open(File.join(@path, "assets", "www", "phonegap.#{ version }.js"), 'w') {|f| f.write(phonegapjs) } end # end \ No newline at end of file From 3ce0fc4897901c172ffc7a9055708d29d96905e8 Mon Sep 17 00:00:00 2001 From: macdonst Date: Fri, 3 Jun 2011 01:11:51 +0800 Subject: [PATCH 10/59] Updating Connection object to conform with recently released spec - Replacing currentNW and homeNW with networkName. - Changing Connection constants to strings instead of ints. - Firing online/offline events on network change. --- framework/AndroidManifest.xml | 3 + framework/assets/js/network.js | 49 +++++++++++---- .../src/com/phonegap/NetworkManager.java | 59 +++++++++++-------- 3 files changed, 73 insertions(+), 38 deletions(-) diff --git a/framework/AndroidManifest.xml b/framework/AndroidManifest.xml index ea2a6636..2807d65b 100644 --- a/framework/AndroidManifest.xml +++ b/framework/AndroidManifest.xml @@ -14,15 +14,18 @@ + + + diff --git a/framework/assets/js/network.js b/framework/assets/js/network.js index 2e092f23..d93c95ba 100755 --- a/framework/assets/js/network.js +++ b/framework/assets/js/network.js @@ -65,29 +65,52 @@ Network.prototype.isReachable = function(uri, callback, options) { */ var Connection = function() { this.type = null; - this.homeNW = null; - this.currentNW = null; + this.networkName = null; + this._firstRun = true; + this._timer = null; + this.timeout = 500; var me = this; this.getInfo( function(info) { - me.type = info.type; - me.homeNW = info.homeNW; - me.currentNW = info.currentNW; - PhoneGap.onPhoneGapConnectionReady.fire(); + // Need to send events if we are on or offline + if (info.type == "none") { + // set a timer if still offline at the end of timer send the offline event + me._timer = setTimeout(function(){ + me.type = info.type; + me.networkName = info.networkName; + PhoneGap.fireEvent('offline'); + me._timer = null; + }, me.timeout); + } else { + // If there is a current offline event pending clear it + if (me._timer != null) { + clearTimeout(me._timer); + me._timer = null; + } + me.type = info.type; + me.networkName = info.networkName; + PhoneGap.fireEvent('online'); + } + + // should only fire this once + if (me._firstRun) { + me._firstRun = false; + PhoneGap.onPhoneGapConnectionReady.fire(); + } }, function(e) { console.log("Error initializing Network Connection: " + e); }); }; -Connection.UNKNOWN = 0; -Connection.ETHERNET = 1; -Connection.WIFI = 2; -Connection.CELL_2G = 3; -Connection.CELL_3G = 4; -Connection.CELL_4G = 5; -Connection.NONE = 20; +Connection.UNKNOWN = "unknown"; +Connection.ETHERNET = "ethernet"; +Connection.WIFI = "wifi"; +Connection.CELL_2G = "2g"; +Connection.CELL_3G = "3g"; +Connection.CELL_4G = "4g"; +Connection.NONE = "none"; /** * Get connection info diff --git a/framework/src/com/phonegap/NetworkManager.java b/framework/src/com/phonegap/NetworkManager.java index 0a2fdb1b..f05d089c 100755 --- a/framework/src/com/phonegap/NetworkManager.java +++ b/framework/src/com/phonegap/NetworkManager.java @@ -22,12 +22,14 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.net.*; +import android.net.wifi.WifiInfo; +import android.net.wifi.WifiManager; import android.telephony.TelephonyManager; import android.util.Log; public class NetworkManager extends Plugin { - public static int NOT_REACHABLE = 0; + public static int NOT_REACHABLE = 0; public static int REACHABLE_VIA_CARRIER_DATA_NETWORK = 1; public static int REACHABLE_VIA_WIFI_NETWORK = 2; @@ -46,19 +48,23 @@ public class NetworkManager extends Plugin { public static final String LTE = "lte"; public static final String UMB = "umb"; // return types - public static final int TYPE_UNKNOWN = 0; - public static final int TYPE_ETHERNET = 1; - public static final int TYPE_WIFI = 2; - public static final int TYPE_2G = 3; - public static final int TYPE_3G = 4; - public static final int TYPE_4G = 5; - public static final int TYPE_NONE = 20; + public static final String TYPE_UNKNOWN = "unknown"; + public static final String TYPE_ETHERNET = "ethernet"; + public static final String TYPE_WIFI = "wifi"; + public static final String TYPE_2G = "2g"; + public static final String TYPE_3G = "3g"; + public static final String TYPE_4G = "4g"; + public static final String TYPE_NONE = "none"; private static final String LOG_TAG = "NetworkManager"; + private static final String NETWORK_NAME = "networkName"; + private static final String TYPE = "type"; + private String connectionCallbackId; ConnectivityManager sockMan; TelephonyManager telephonyManager; + WifiManager wifiManager; BroadcastReceiver receiver; /** @@ -78,8 +84,9 @@ public class NetworkManager extends Plugin { super.setContext(ctx); this.sockMan = (ConnectivityManager) ctx.getSystemService(Context.CONNECTIVITY_SERVICE); this.telephonyManager = ((TelephonyManager) ctx.getSystemService(Context.TELEPHONY_SERVICE)); + this.wifiManager = ((WifiManager) ctx.getSystemService(Context.WIFI_SERVICE)); this.connectionCallbackId = null; - + // We need to listen to connectivity events to update navigator.connection IntentFilter intentFilter = new IntentFilter() ; intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); @@ -151,7 +158,7 @@ public class NetworkManager extends Plugin { try { this.ctx.unregisterReceiver(this.receiver); } catch (Exception e) { - System.out.println("Error unregistering network receiver: " + e.getMessage()); + Log.e(LOG_TAG, "Error unregistering network receiver: " + e.getMessage(), e); } } } @@ -167,11 +174,11 @@ public class NetworkManager extends Plugin { * @param info the current active network info * @return */ - private void updateConnectionInfo(NetworkInfo info) { - JSONObject connection = this.getConnectionInfo(info); - - // send update to javascript "navigator.connection" - sendUpdate(connection); + private void updateConnectionInfo(NetworkInfo info) { + JSONObject connection = this.getConnectionInfo(info); + + // send update to javascript "navigator.network.connection" + sendUpdate(connection); } /** @@ -187,24 +194,26 @@ public class NetworkManager extends Plugin { if (info != null) { // If we are not connected to any network set type to none if (!info.isConnected()) { - connection.put("type", TYPE_NONE); - connection.put("homeNW", null); - connection.put("currentNW", null); + connection.put(TYPE, TYPE_NONE); + connection.put(NETWORK_NAME, null); } else { // If we are connected check which type // First off is wifi if (info.getTypeName().toLowerCase().equals(WIFI)) { - connection.put("type", TYPE_WIFI); - connection.put("homeNW", null); - connection.put("currentNW", null); + connection.put(TYPE, TYPE_WIFI); + WifiInfo wifiInfo = this.wifiManager.getConnectionInfo(); + if (wifiInfo != null) { + connection.put(NETWORK_NAME, wifiInfo.getSSID()); + } else { + connection.put(NETWORK_NAME, null); + } } // Otherwise it must be one of the mobile network protocols else { // Determine the correct type, 2G, 3G, 4G - connection.put("type", getType(info)); - connection.put("homeNW", telephonyManager.getSimOperatorName()); - connection.put("currentNW", telephonyManager.getNetworkOperatorName()); + connection.put(TYPE, getType(info)); + connection.put(NETWORK_NAME, telephonyManager.getNetworkOperatorName()); } } } @@ -233,7 +242,7 @@ public class NetworkManager extends Plugin { * @param info the network info so we can determine connection type. * @return the type of mobile network we are on */ - private int getType(NetworkInfo info) { + private String getType(NetworkInfo info) { if (info != null) { String type = info.getTypeName(); From 740e50ce61ef2445841f5e45f4d14e753a78e144 Mon Sep 17 00:00:00 2001 From: macdonst Date: Tue, 7 Jun 2011 02:37:29 +0800 Subject: [PATCH 11/59] Issue #104: Bug in FileUtils.copyDirectory & moveDirectory Adding better test to see if a directory is being moved/copied 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 --- framework/src/com/phonegap/FileUtils.java | 26 ++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/framework/src/com/phonegap/FileUtils.java b/framework/src/com/phonegap/FileUtils.java index a2ed7a11..a53884ef 100755 --- a/framework/src/com/phonegap/FileUtils.java +++ b/framework/src/com/phonegap/FileUtils.java @@ -411,7 +411,7 @@ public class FileUtils extends Plugin { } // Check to make sure we are not copying the directory into itself - if (destinationDir.getAbsolutePath().startsWith(srcDir.getAbsolutePath())) { + if (isCopyOnItself(srcDir.getAbsolutePath(), destinationDir.getAbsolutePath())) { throw new InvalidModificationException("Can't copy itself into itself"); } @@ -435,6 +435,26 @@ public class FileUtils extends Plugin { return getEntry(destinationDir); } + /** + * Check to see if the user attempted to copy an entry into its parent without changing its name, + * or attempted to copy a directory into a directory that it contains directly or indirectly. + * + * @param srcDir + * @param destinationDir + * @return + */ + private boolean isCopyOnItself(String src, String dest) { + + // 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) { + return true; + } + + return false; + } + /** * Move a file * @@ -480,8 +500,8 @@ public class FileUtils extends Plugin { } // Check to make sure we are not copying the directory into itself - if (destinationDir.getAbsolutePath().startsWith(srcDir.getAbsolutePath())) { - throw new InvalidModificationException("Can't copy itself into itself"); + if (isCopyOnItself(srcDir.getAbsolutePath(), destinationDir.getAbsolutePath())) { + throw new InvalidModificationException("Can't move itself into itself"); } // If the destination directory already exists and is empty then delete it. This is according to spec. From 8da5ad8eca7876bdc2019be8d53626a9a9281fa2 Mon Sep 17 00:00:00 2001 From: macdonst Date: Tue, 7 Jun 2011 22:03:16 +0800 Subject: [PATCH 12/59] Issue #106: Typo in LocalFileSystem.prototype._castDate --- framework/assets/js/file.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/assets/js/file.js b/framework/assets/js/file.js index d2f21cd3..11229b35 100755 --- a/framework/assets/js/file.js +++ b/framework/assets/js/file.js @@ -1044,7 +1044,7 @@ LocalFileSystem.prototype._castDate = function(pluginResult) { file.type = pluginResult.message.type; file.name = pluginResult.message.name; file.fullPath = pluginResult.message.fullPath; - file.lastModifedDate = new Date(pluginResult.message.lastModifiedDate); + file.lastModifiedDate = new Date(pluginResult.message.lastModifiedDate); pluginResult.message = file; } return pluginResult; From 315b5a59b3d1dc74de29bbd3b4122e0a7c577548 Mon Sep 17 00:00:00 2001 From: Bryce Curtis Date: Tue, 7 Jun 2011 14:18:18 -0500 Subject: [PATCH 13/59] Set PhoneGap.UsePolling flag based upon result from CallbackServer. --- framework/assets/js/phonegap.js.base | 3 +++ 1 file changed, 3 insertions(+) diff --git a/framework/assets/js/phonegap.js.base b/framework/assets/js/phonegap.js.base index 5d06bf99..e48fbf55 100755 --- a/framework/assets/js/phonegap.js.base +++ b/framework/assets/js/phonegap.js.base @@ -344,10 +344,13 @@ PhoneGap.Channel.join(function() { } else { var polling = prompt("usePolling", "gap_callbackServer:"); + PhoneGap.UsePolling = polling; if (polling == "true") { + PhoneGap.UsePolling = true; PhoneGap.JSCallbackPolling(); } else { + PhoneGap.UsePolling = false; PhoneGap.JSCallback(); } } From a67aeed5711fbd5ec6571d1d515a2221060f7ff8 Mon Sep 17 00:00:00 2001 From: macdonst Date: Thu, 9 Jun 2011 03:03:03 +0800 Subject: [PATCH 14/59] Updating Network Connection API to match spec released on June 7th --- framework/AndroidManifest.xml | 2 - framework/assets/js/network.js | 11 ++- framework/src/com/phonegap/Device.java | 32 ++------- .../src/com/phonegap/NetworkManager.java | 70 +++++-------------- 4 files changed, 27 insertions(+), 88 deletions(-) diff --git a/framework/AndroidManifest.xml b/framework/AndroidManifest.xml index 2807d65b..53d7afb5 100644 --- a/framework/AndroidManifest.xml +++ b/framework/AndroidManifest.xml @@ -14,8 +14,6 @@ - - diff --git a/framework/assets/js/network.js b/framework/assets/js/network.js index d93c95ba..be94402b 100755 --- a/framework/assets/js/network.js +++ b/framework/assets/js/network.js @@ -65,20 +65,18 @@ Network.prototype.isReachable = function(uri, callback, options) { */ var Connection = function() { this.type = null; - this.networkName = null; this._firstRun = true; this._timer = null; this.timeout = 500; var me = this; this.getInfo( - function(info) { + function(type) { // Need to send events if we are on or offline - if (info.type == "none") { + if (type == "none") { // set a timer if still offline at the end of timer send the offline event me._timer = setTimeout(function(){ - me.type = info.type; - me.networkName = info.networkName; + me.type = type; PhoneGap.fireEvent('offline'); me._timer = null; }, me.timeout); @@ -88,8 +86,7 @@ var Connection = function() { clearTimeout(me._timer); me._timer = null; } - me.type = info.type; - me.networkName = info.networkName; + me.type = type; PhoneGap.fireEvent('online'); } diff --git a/framework/src/com/phonegap/Device.java b/framework/src/com/phonegap/Device.java index ac0a8ecc..035058fe 100755 --- a/framework/src/com/phonegap/Device.java +++ b/framework/src/com/phonegap/Device.java @@ -14,9 +14,7 @@ import org.json.JSONObject; import com.phonegap.api.PhonegapActivity; import com.phonegap.api.Plugin; import com.phonegap.api.PluginResult; -import android.content.Context; import android.provider.Settings; -import android.telephony.TelephonyManager; public class Device extends Plugin { @@ -116,34 +114,13 @@ public class Device extends Plugin { public String getPhonegapVersion() { return Device.phonegapVersion; } - - public String getLine1Number(){ - TelephonyManager operator = (TelephonyManager)this.ctx.getSystemService(Context.TELEPHONY_SERVICE); - return operator.getLine1Number(); - } - public String getDeviceId(){ - TelephonyManager operator = (TelephonyManager)this.ctx.getSystemService(Context.TELEPHONY_SERVICE); - return operator.getDeviceId(); - } - - public String getSimSerialNumber(){ - TelephonyManager operator = (TelephonyManager)this.ctx.getSystemService(Context.TELEPHONY_SERVICE); - return operator.getSimSerialNumber(); - } - - public String getSubscriberId(){ - TelephonyManager operator = (TelephonyManager)this.ctx.getSystemService(Context.TELEPHONY_SERVICE); - return operator.getSubscriberId(); - } - - public String getModel() - { + public String getModel() { String model = android.os.Build.MODEL; return model; } - public String getProductName() - { + + public String getProductName() { String productname = android.os.Build.PRODUCT; return productname; } @@ -158,8 +135,7 @@ public class Device extends Plugin { return osversion; } - public String getSDKVersion() - { + public String getSDKVersion() { String sdkversion = android.os.Build.VERSION.SDK; return sdkversion; } diff --git a/framework/src/com/phonegap/NetworkManager.java b/framework/src/com/phonegap/NetworkManager.java index f05d089c..c2b756bc 100755 --- a/framework/src/com/phonegap/NetworkManager.java +++ b/framework/src/com/phonegap/NetworkManager.java @@ -11,7 +11,6 @@ import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.DefaultHttpClient; import org.json.JSONArray; import org.json.JSONException; -import org.json.JSONObject; import com.phonegap.api.PhonegapActivity; import com.phonegap.api.Plugin; @@ -21,10 +20,8 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.net.*; -import android.net.wifi.WifiInfo; -import android.net.wifi.WifiManager; -import android.telephony.TelephonyManager; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; import android.util.Log; public class NetworkManager extends Plugin { @@ -57,14 +54,10 @@ public class NetworkManager extends Plugin { public static final String TYPE_NONE = "none"; private static final String LOG_TAG = "NetworkManager"; - private static final String NETWORK_NAME = "networkName"; - private static final String TYPE = "type"; private String connectionCallbackId; ConnectivityManager sockMan; - TelephonyManager telephonyManager; - WifiManager wifiManager; BroadcastReceiver receiver; /** @@ -83,8 +76,6 @@ public class NetworkManager extends Plugin { public void setContext(PhonegapActivity ctx) { super.setContext(ctx); this.sockMan = (ConnectivityManager) ctx.getSystemService(Context.CONNECTIVITY_SERVICE); - this.telephonyManager = ((TelephonyManager) ctx.getSystemService(Context.TELEPHONY_SERVICE)); - this.wifiManager = ((WifiManager) ctx.getSystemService(Context.WIFI_SERVICE)); this.connectionCallbackId = null; // We need to listen to connectivity events to update navigator.connection @@ -175,10 +166,8 @@ public class NetworkManager extends Plugin { * @return */ private void updateConnectionInfo(NetworkInfo info) { - JSONObject connection = this.getConnectionInfo(info); - // send update to javascript "navigator.network.connection" - sendUpdate(connection); + sendUpdate(this.getConnectionInfo(info)); } /** @@ -187,42 +176,18 @@ public class NetworkManager extends Plugin { * @param info the current active network info * @return a JSONObject that represents the network info */ - private JSONObject getConnectionInfo(NetworkInfo info) { - JSONObject connection = new JSONObject(); - - try { - if (info != null) { - // If we are not connected to any network set type to none - if (!info.isConnected()) { - connection.put(TYPE, TYPE_NONE); - connection.put(NETWORK_NAME, null); - } - else { - // If we are connected check which type - // First off is wifi - if (info.getTypeName().toLowerCase().equals(WIFI)) { - connection.put(TYPE, TYPE_WIFI); - WifiInfo wifiInfo = this.wifiManager.getConnectionInfo(); - if (wifiInfo != null) { - connection.put(NETWORK_NAME, wifiInfo.getSSID()); - } else { - connection.put(NETWORK_NAME, null); - } - } - // Otherwise it must be one of the mobile network protocols - else { - // Determine the correct type, 2G, 3G, 4G - connection.put(TYPE, getType(info)); - connection.put(NETWORK_NAME, telephonyManager.getNetworkOperatorName()); - } - } + private String getConnectionInfo(NetworkInfo info) { + String type = TYPE_NONE; + if (info != null) { + // If we are not connected to any network set type to none + if (!info.isConnected()) { + type = TYPE_NONE; + } + else { + type = getType(info); } } - catch (JSONException e) { - // this should never happen - Log.e(LOG_TAG, e.getMessage(), e); - } - return connection; + return type; } /** @@ -230,8 +195,8 @@ public class NetworkManager extends Plugin { * * @param connection the network info to set as navigator.connection */ - private void sendUpdate(JSONObject connection) { - PluginResult result = new PluginResult(PluginResult.Status.OK, connection); + private void sendUpdate(String type) { + PluginResult result = new PluginResult(PluginResult.Status.OK, type); result.setKeepCallback(true); this.success(result, this.connectionCallbackId); } @@ -246,7 +211,10 @@ public class NetworkManager extends Plugin { if (info != null) { String type = info.getTypeName(); - if (type.toLowerCase().equals(MOBILE)) { + if (type.toLowerCase().equals(WIFI)) { + return TYPE_WIFI; + } + else if (type.toLowerCase().equals(MOBILE)) { type = info.getSubtypeName(); if (type.toLowerCase().equals(GSM) || type.toLowerCase().equals(GPRS) || From a89c8bf482cf5f5e5d75358f317203f5bd266d36 Mon Sep 17 00:00:00 2001 From: Benjamin Weingarten Date: Sun, 12 Jun 2011 12:24:25 +0300 Subject: [PATCH 15/59] Fix bug where isreachable doesn't return correct results for https (http secure) url protocol. --- framework/src/com/phonegap/NetworkManager.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/src/com/phonegap/NetworkManager.java b/framework/src/com/phonegap/NetworkManager.java index c2b756bc..62140f31 100755 --- a/framework/src/com/phonegap/NetworkManager.java +++ b/framework/src/com/phonegap/NetworkManager.java @@ -275,7 +275,7 @@ public class NetworkManager extends Plugin { public int isReachable(String uri, boolean isIpAddress) { int reachable = NOT_REACHABLE; - if (uri.indexOf("http://") == -1) { + if (uri.indexOf("http://") == -1 && uri.indexOf("https://") == -1) { uri = "http://" + uri; } @@ -298,4 +298,4 @@ public class NetworkManager extends Plugin { return reachable; } -} \ No newline at end of file +} From 33bfb44f670bad34e0b552dc119809733f9eb807 Mon Sep 17 00:00:00 2001 From: Bryce Curtis Date: Mon, 13 Jun 2011 15:16:08 -0500 Subject: [PATCH 16/59] Fix security vulnerability - make sure any requests to run native code only come from url currently loaded into webview. --- framework/src/com/phonegap/DroidGap.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/framework/src/com/phonegap/DroidGap.java b/framework/src/com/phonegap/DroidGap.java index fae84535..7dc2aed0 100755 --- a/framework/src/com/phonegap/DroidGap.java +++ b/framework/src/com/phonegap/DroidGap.java @@ -779,10 +779,14 @@ public class DroidGap extends PhonegapActivity { */ @Override public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) { + boolean reqOk = false; + if (((DroidGap)(this.ctx)).url.equals(url)) { + reqOk = true; + } // Calling PluginManager.exec() to call a native service using // prompt(this.stringify(args), "gap:"+this.stringify([service, action, callbackId, true])); - if (defaultValue != null && defaultValue.length() > 3 && defaultValue.substring(0, 4).equals("gap:")) { + if (reqOk && defaultValue != null && defaultValue.length() > 3 && defaultValue.substring(0, 4).equals("gap:")) { JSONArray array; try { array = new JSONArray(defaultValue.substring(4)); @@ -798,13 +802,13 @@ public class DroidGap extends PhonegapActivity { } // Polling for JavaScript messages - else if (defaultValue.equals("gap_poll:")) { + else if (reqOk && defaultValue.equals("gap_poll:")) { String r = callbackServer.getJavascript(); result.confirm(r); } // Calling into CallbackServer - else if (defaultValue.equals("gap_callbackServer:")) { + else if (reqOk && defaultValue.equals("gap_callbackServer:")) { String r = ""; if (message.equals("usePolling")) { r = ""+callbackServer.usePolling(); From d1448e90732fa95ba4840d2d4e917f917e69853f Mon Sep 17 00:00:00 2001 From: Bryce Curtis Date: Wed, 15 Jun 2011 13:01:03 -0500 Subject: [PATCH 17/59] Issue 112: PhoneGap.Channel: replace instanceof Function with typeof === 'Function' --- framework/assets/js/phonegap.js.base | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/framework/assets/js/phonegap.js.base b/framework/assets/js/phonegap.js.base index e48fbf55..4f26fd8d 100755 --- a/framework/assets/js/phonegap.js.base +++ b/framework/assets/js/phonegap.js.base @@ -99,7 +99,7 @@ PhoneGap.Channel.prototype.subscribe = function(f, c, g) { if (f === null) { return; } var func = f; - if (typeof c === "object" && f instanceof Function) { func = PhoneGap.close(c, f); } + if (typeof c === "object" && typeof f === "function") { func = PhoneGap.close(c, f); } g = g || func.observer_guid || f.observer_guid || this.guid++; func.observer_guid = g; @@ -120,7 +120,7 @@ PhoneGap.Channel.prototype.subscribeOnce = function(f, c) { _this.unsubscribe(g); }; if (this.fired) { - if (typeof c === "object" && f instanceof Function) { f = PhoneGap.close(c, f); } + if (typeof c === "object" && typeof f === "function") { f = PhoneGap.close(c, f); } f.apply(this, this.fireArgs); } else { g = this.subscribe(m); @@ -132,7 +132,7 @@ PhoneGap.Channel.prototype.subscribeOnce = function(f, c) { * Unsubscribes the function with the given guid from the channel. */ PhoneGap.Channel.prototype.unsubscribe = function(g) { - if (g instanceof Function) { g = g.observer_guid; } + if (typeof g === "function") { g = g.observer_guid; } this.handlers[g] = null; delete this.handlers[g]; }; @@ -147,7 +147,7 @@ PhoneGap.Channel.prototype.fire = function(e) { for (item in this.handlers) { if (this.handlers.hasOwnProperty(item)) { handler = this.handlers[item]; - if (handler instanceof Function) { + if (typeof handler === "function") { rv = (handler.apply(this, arguments) === false); fail = fail || rv; } @@ -510,7 +510,7 @@ PhoneGap.clone = function(obj) { return retVal; } - if (obj instanceof Function) { + if (typeof obj === "function") { return obj; } From c978341d837764073c33ad5e7f8114362cfb2615 Mon Sep 17 00:00:00 2001 From: Kevin Griffin Date: Tue, 21 Jun 2011 11:32:18 -0400 Subject: [PATCH 18/59] made the mistake of opening anything in finder --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 5c53ca30..afa78b8d 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ local.properties framework/phonegap.jar framework/bin framework/assets/www/.DS_Store +.DS_Store \ No newline at end of file From 327bda49a0d228da83e3ab94c183e8f1be4386f6 Mon Sep 17 00:00:00 2001 From: Kevin Griffin Date: Tue, 21 Jun 2011 11:32:22 -0400 Subject: [PATCH 19/59] Sending pause/resume notifcations to plugins regardless of 'keepRunning' state. Not sure why you wouldn't want to send them Added a OnNewIntent override for Plugins to use. --- framework/src/com/phonegap/DroidGap.java | 38 ++++++++++++------- framework/src/com/phonegap/api/Plugin.java | 6 +++ .../src/com/phonegap/api/PluginManager.java | 11 ++++++ 3 files changed, 42 insertions(+), 13 deletions(-) diff --git a/framework/src/com/phonegap/DroidGap.java b/framework/src/com/phonegap/DroidGap.java index 7dc2aed0..fefbc144 100755 --- a/framework/src/com/phonegap/DroidGap.java +++ b/framework/src/com/phonegap/DroidGap.java @@ -9,13 +9,12 @@ package com.phonegap; import org.json.JSONArray; import org.json.JSONException; + import android.app.AlertDialog; -import android.widget.EditText; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.res.Configuration; -import android.graphics.Bitmap; import android.graphics.Color; import android.media.AudioManager; import android.net.Uri; @@ -26,19 +25,21 @@ import android.view.View; import android.view.ViewGroup; import android.view.Window; import android.view.WindowManager; +import android.webkit.GeolocationPermissions.Callback; +import android.webkit.JsPromptResult; import android.webkit.JsResult; import android.webkit.WebChromeClient; -import android.webkit.JsPromptResult; import android.webkit.WebSettings; +import android.webkit.WebSettings.LayoutAlgorithm; import android.webkit.WebStorage; import android.webkit.WebView; import android.webkit.WebViewClient; -import android.webkit.GeolocationPermissions.Callback; -import android.webkit.WebSettings.LayoutAlgorithm; +import android.widget.EditText; import android.widget.LinearLayout; + +import com.phonegap.api.PhonegapActivity; import com.phonegap.api.Plugin; import com.phonegap.api.PluginManager; -import com.phonegap.api.PhonegapActivity; /** * This class is the main Android activity that represents the PhoneGap @@ -608,17 +609,28 @@ public class DroidGap extends PhonegapActivity { // Send pause event to JavaScript this.appView.loadUrl("javascript:try{PhoneGap.onPause.fire();}catch(e){};"); + // Forward to plugins + this.pluginManager.onPause(); + // If app doesn't want to run in background if (!this.keepRunning) { - - // Forward to plugins - this.pluginManager.onPause(); // Pause JavaScript timers (including setInterval) this.appView.pauseTimers(); } } - + + @Override + /** + * Called when the activity receives a new intent + **/ + protected void onNewIntent(Intent intent) { + super.onNewIntent(intent); + + //Forward to plugins + this.pluginManager.onNewIntent(intent); + } + @Override /** * Called when the activity will start interacting with the user. @@ -632,6 +644,9 @@ public class DroidGap extends PhonegapActivity { // Send resume event to JavaScript this.appView.loadUrl("javascript:try{PhoneGap.onResume.fire();}catch(e){};"); + // Forward to plugins + this.pluginManager.onResume(); + // If app doesn't want to run in background if (!this.keepRunning || this.activityResultKeepRunning) { @@ -641,9 +656,6 @@ public class DroidGap extends PhonegapActivity { this.activityResultKeepRunning = false; } - // Forward to plugins - this.pluginManager.onResume(); - // Resume JavaScript timers (including setInterval) this.appView.resumeTimers(); } diff --git a/framework/src/com/phonegap/api/Plugin.java b/framework/src/com/phonegap/api/Plugin.java index a527159a..39da4b89 100755 --- a/framework/src/com/phonegap/api/Plugin.java +++ b/framework/src/com/phonegap/api/Plugin.java @@ -74,6 +74,12 @@ public abstract class Plugin implements IPlugin { public void onResume() { } + /** + * Called when the activity receives a new intent. + */ + public void onNewIntent(Intent intent) { + } + /** * The final call you receive before your activity is destroyed. */ diff --git a/framework/src/com/phonegap/api/PluginManager.java b/framework/src/com/phonegap/api/PluginManager.java index ad1cf20b..616ee856 100755 --- a/framework/src/com/phonegap/api/PluginManager.java +++ b/framework/src/com/phonegap/api/PluginManager.java @@ -13,6 +13,7 @@ import java.util.Map.Entry; import org.json.JSONArray; import org.json.JSONException; +import android.content.Intent; import android.webkit.WebView; /** @@ -265,4 +266,14 @@ public final class PluginManager { plugin.onDestroy(); } } + + public void onNewIntent(Intent intent) { + java.util.Set> s = this.plugins.entrySet(); + java.util.Iterator> it = s.iterator(); + while(it.hasNext()) { + Entry entry = it.next(); + Plugin plugin = entry.getValue(); + plugin.onNewIntent(intent); + } + } } \ No newline at end of file From 517b5e0db9ea547bf79d1f4a136b1a8c4832c3f6 Mon Sep 17 00:00:00 2001 From: Kevin Griffin Date: Tue, 21 Jun 2011 11:46:54 -0400 Subject: [PATCH 20/59] formattage --- framework/src/com/phonegap/DroidGap.java | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/framework/src/com/phonegap/DroidGap.java b/framework/src/com/phonegap/DroidGap.java index fefbc144..cafc0ef0 100755 --- a/framework/src/com/phonegap/DroidGap.java +++ b/framework/src/com/phonegap/DroidGap.java @@ -620,16 +620,16 @@ public class DroidGap extends PhonegapActivity { } } - @Override - /** - * Called when the activity receives a new intent - **/ - protected void onNewIntent(Intent intent) { - super.onNewIntent(intent); - - //Forward to plugins - this.pluginManager.onNewIntent(intent); - } + @Override + /** + * Called when the activity receives a new intent + **/ + protected void onNewIntent(Intent intent) { + super.onNewIntent(intent); + + //Forward to plugins + this.pluginManager.onNewIntent(intent); + } @Override /** From afa85a74b3a1c3047972968f45456d8b8950e8b9 Mon Sep 17 00:00:00 2001 From: Joe Bowser Date: Tue, 21 Jun 2011 10:08:42 -0700 Subject: [PATCH 21/59] Adding SSL dev code --- framework/src/com/phonegap/DroidGap.java | 26 ++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/framework/src/com/phonegap/DroidGap.java b/framework/src/com/phonegap/DroidGap.java index 7dc2aed0..01e226b0 100755 --- a/framework/src/com/phonegap/DroidGap.java +++ b/framework/src/com/phonegap/DroidGap.java @@ -14,11 +14,15 @@ import android.widget.EditText; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Configuration; import android.graphics.Bitmap; import android.graphics.Color; import android.media.AudioManager; import android.net.Uri; +import android.net.http.SslError; import android.os.Bundle; import android.util.Log; import android.view.KeyEvent; @@ -27,6 +31,7 @@ import android.view.ViewGroup; import android.view.Window; import android.view.WindowManager; import android.webkit.JsResult; +import android.webkit.SslErrorHandler; import android.webkit.WebChromeClient; import android.webkit.JsPromptResult; import android.webkit.WebSettings; @@ -1115,6 +1120,27 @@ public class DroidGap extends PhonegapActivity { // Handle error this.ctx.onReceivedError(errorCode, description, failingUrl); } + + public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { + + final String packageName = this.ctx.getPackageName(); + final PackageManager pm = this.ctx.getPackageManager(); + ApplicationInfo appInfo; + try { + appInfo = pm.getApplicationInfo(packageName, PackageManager.GET_META_DATA); + if ((appInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) { + // debug = true + handler.proceed(); + return; + } else { + // debug = false + super.onReceivedSslError(view, handler, error); + } + } catch (NameNotFoundException e) { + // When it doubt, lock it out! + super.onReceivedSslError(view, handler, error); + } + } } /** From 9b52827744f8df0c76e157c7744e83dd59f65d57 Mon Sep 17 00:00:00 2001 From: Bryce Curtis Date: Tue, 21 Jun 2011 22:50:53 -0500 Subject: [PATCH 22/59] Urls with same path and file but different # or ? should compare to same url. --- framework/src/com/phonegap/DroidGap.java | 28 +++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/framework/src/com/phonegap/DroidGap.java b/framework/src/com/phonegap/DroidGap.java index 7dc2aed0..914d74bb 100755 --- a/framework/src/com/phonegap/DroidGap.java +++ b/framework/src/com/phonegap/DroidGap.java @@ -119,8 +119,13 @@ public class DroidGap extends PhonegapActivity { protected boolean clearHistory = false; // The initial URL for our app + // ie http://server/path/index.html#abc?query private String url; + // The initial URL for our app up to and including the file name + // ie http://server/path/index.html + private String urlFile; + // The base of the initial URL for our app private String baseUrl; @@ -330,6 +335,7 @@ public class DroidGap extends PhonegapActivity { */ public void loadUrl(final String url) { System.out.println("loadUrl("+url+")"); + this.urlFile = this.getUrlFile(url); this.url = url; int i = url.lastIndexOf('/'); if (i > 0) { @@ -693,13 +699,29 @@ public class DroidGap extends PhonegapActivity { this.callbackServer.sendJavascript(statement); } + /** + * Return up to file part of url. + * If url = http://server/page.html#abc, then return http://server/page.html + * + * @param url + * @return + */ + private String getUrlFile(String url) { + int p1 = url.indexOf("#"); + int p2 = url.indexOf("?"); + if (p1 < 0) p1 = url.length(); + if (p2 < 0) p2 = url.length(); + int p3 = (p1 < p2) ? p1 : p2; + return url.substring(0, p3); + } + /** * Provides a hook for calling "alert" from javascript. Useful for * debugging your javascript. */ public class GapClient extends WebChromeClient { - private Context ctx; + private DroidGap ctx; /** * Constructor. @@ -707,7 +729,7 @@ public class DroidGap extends PhonegapActivity { * @param ctx */ public GapClient(Context ctx) { - this.ctx = ctx; + this.ctx = (DroidGap)ctx; } /** @@ -780,7 +802,7 @@ public class DroidGap extends PhonegapActivity { @Override public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) { boolean reqOk = false; - if (((DroidGap)(this.ctx)).url.equals(url)) { + if (this.ctx.urlFile.equals(this.ctx.getUrlFile(url))) { reqOk = true; } From a67dfdb75ee0dfdd4b3b64a1f648a9d384058a21 Mon Sep 17 00:00:00 2001 From: Bryce Curtis Date: Tue, 21 Jun 2011 22:53:45 -0500 Subject: [PATCH 23/59] Return true when handling key events, indicating that no further processing is necessary. --- framework/src/com/phonegap/DroidGap.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/framework/src/com/phonegap/DroidGap.java b/framework/src/com/phonegap/DroidGap.java index 914d74bb..c3a9d357 100755 --- a/framework/src/com/phonegap/DroidGap.java +++ b/framework/src/com/phonegap/DroidGap.java @@ -1157,6 +1157,7 @@ public class DroidGap extends PhonegapActivity { // If back key is bound, then send event to JavaScript if (this.bound) { this.appView.loadUrl("javascript:PhoneGap.fireEvent('backbutton');"); + return true; } // If not bound @@ -1165,6 +1166,7 @@ public class DroidGap extends PhonegapActivity { // Go to previous page in webview if it is possible to go back if (this.appView.canGoBack()) { this.appView.goBack(); + return true; } // If not, then invoke behavior of super class @@ -1177,11 +1179,13 @@ public class DroidGap extends PhonegapActivity { // If menu key else if (keyCode == KeyEvent.KEYCODE_MENU) { this.appView.loadUrl("javascript:PhoneGap.fireEvent('menubutton');"); + return true; } // If search key else if (keyCode == KeyEvent.KEYCODE_SEARCH) { this.appView.loadUrl("javascript:PhoneGap.fireEvent('searchbutton');"); + return true; } return false; From c15971253fa891d5d360bd97307059a8adc6946f Mon Sep 17 00:00:00 2001 From: Kevin Griffin Date: Wed, 22 Jun 2011 11:05:33 -0400 Subject: [PATCH 24/59] formatting - sigh --- framework/src/com/phonegap/DroidGap.java | 24 +++++++++---------- framework/src/com/phonegap/api/Plugin.java | 4 ++-- .../src/com/phonegap/api/PluginManager.java | 3 +++ 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/framework/src/com/phonegap/DroidGap.java b/framework/src/com/phonegap/DroidGap.java index cafc0ef0..042f83d8 100755 --- a/framework/src/com/phonegap/DroidGap.java +++ b/framework/src/com/phonegap/DroidGap.java @@ -619,18 +619,18 @@ public class DroidGap extends PhonegapActivity { this.appView.pauseTimers(); } } - - @Override - /** - * Called when the activity receives a new intent - **/ - protected void onNewIntent(Intent intent) { - super.onNewIntent(intent); - - //Forward to plugins - this.pluginManager.onNewIntent(intent); - } - + + @Override + /** + * Called when the activity receives a new intent + **/ + protected void onNewIntent(Intent intent) { + super.onNewIntent(intent); + + //Forward to plugins + this.pluginManager.onNewIntent(intent); + } + @Override /** * Called when the activity will start interacting with the user. diff --git a/framework/src/com/phonegap/api/Plugin.java b/framework/src/com/phonegap/api/Plugin.java index 39da4b89..502552c6 100755 --- a/framework/src/com/phonegap/api/Plugin.java +++ b/framework/src/com/phonegap/api/Plugin.java @@ -77,8 +77,8 @@ public abstract class Plugin implements IPlugin { /** * Called when the activity receives a new intent. */ - public void onNewIntent(Intent intent) { - } + public void onNewIntent(Intent intent) { + } /** * The final call you receive before your activity is destroyed. diff --git a/framework/src/com/phonegap/api/PluginManager.java b/framework/src/com/phonegap/api/PluginManager.java index 616ee856..7dafa564 100755 --- a/framework/src/com/phonegap/api/PluginManager.java +++ b/framework/src/com/phonegap/api/PluginManager.java @@ -267,6 +267,9 @@ public final class PluginManager { } } + /** + * Called when the activity receives a new intent. + */ public void onNewIntent(Intent intent) { java.util.Set> s = this.plugins.entrySet(); java.util.Iterator> it = s.iterator(); From 1c3ea54dcbe695a5caa3920b00e85ca956030a43 Mon Sep 17 00:00:00 2001 From: Bryce Curtis Date: Thu, 23 Jun 2011 23:22:48 -0500 Subject: [PATCH 25/59] Always call plugin's onPause/onResume with multitasking flag when these lifecycle events occur in activity. It is up to the plugin to handle as necessary. --- framework/src/com/phonegap/DroidGap.java | 4 ++-- framework/src/com/phonegap/api/IPlugin.java | 8 ++++++-- framework/src/com/phonegap/api/Plugin.java | 8 ++++++-- framework/src/com/phonegap/api/PluginManager.java | 12 ++++++++---- 4 files changed, 22 insertions(+), 10 deletions(-) diff --git a/framework/src/com/phonegap/DroidGap.java b/framework/src/com/phonegap/DroidGap.java index 78160743..99bf63f5 100755 --- a/framework/src/com/phonegap/DroidGap.java +++ b/framework/src/com/phonegap/DroidGap.java @@ -616,7 +616,7 @@ public class DroidGap extends PhonegapActivity { this.appView.loadUrl("javascript:try{PhoneGap.onPause.fire();}catch(e){};"); // Forward to plugins - this.pluginManager.onPause(); + this.pluginManager.onPause(this.keepRunning); // If app doesn't want to run in background if (!this.keepRunning) { @@ -651,7 +651,7 @@ public class DroidGap extends PhonegapActivity { this.appView.loadUrl("javascript:try{PhoneGap.onResume.fire();}catch(e){};"); // Forward to plugins - this.pluginManager.onResume(); + this.pluginManager.onResume(this.keepRunning || this.activityResultKeepRunning); // If app doesn't want to run in background if (!this.keepRunning || this.activityResultKeepRunning) { diff --git a/framework/src/com/phonegap/api/IPlugin.java b/framework/src/com/phonegap/api/IPlugin.java index b3b3a1da..b9c316e0 100755 --- a/framework/src/com/phonegap/api/IPlugin.java +++ b/framework/src/com/phonegap/api/IPlugin.java @@ -54,13 +54,17 @@ public interface IPlugin { /** * Called when the system is about to start resuming a previous activity. + * + * @param multitasking Flag indicating if multitasking is turned on for app */ - void onPause(); + void onPause(boolean multitasking); /** * Called when the activity will start interacting with the user. + * + * @param multitasking Flag indicating if multitasking is turned on for app */ - void onResume(); + void onResume(boolean multitasking); /** * The final call you receive before your activity is destroyed. diff --git a/framework/src/com/phonegap/api/Plugin.java b/framework/src/com/phonegap/api/Plugin.java index 502552c6..746bd408 100755 --- a/framework/src/com/phonegap/api/Plugin.java +++ b/framework/src/com/phonegap/api/Plugin.java @@ -64,14 +64,18 @@ public abstract class Plugin implements IPlugin { /** * Called when the system is about to start resuming a previous activity. + * + * @param multitasking Flag indicating if multitasking is turned on for app */ - public void onPause() { + public void onPause(boolean multitasking) { } /** * Called when the activity will start interacting with the user. + * + * @param multitasking Flag indicating if multitasking is turned on for app */ - public void onResume() { + public void onResume(boolean multitasking) { } /** diff --git a/framework/src/com/phonegap/api/PluginManager.java b/framework/src/com/phonegap/api/PluginManager.java index 7dafa564..393c6a29 100755 --- a/framework/src/com/phonegap/api/PluginManager.java +++ b/framework/src/com/phonegap/api/PluginManager.java @@ -230,27 +230,31 @@ public final class PluginManager { /** * Called when the system is about to start resuming a previous activity. + * + * @param multitasking Flag indicating if multitasking is turned on for app */ - public void onPause() { + public void onPause(boolean multitasking) { java.util.Set> s = this.plugins.entrySet(); java.util.Iterator> it = s.iterator(); while(it.hasNext()) { Entry entry = it.next(); Plugin plugin = entry.getValue(); - plugin.onPause(); + plugin.onPause(multitasking); } } /** * Called when the activity will start interacting with the user. + * + * @param multitasking Flag indicating if multitasking is turned on for app */ - public void onResume() { + public void onResume(boolean multitasking) { java.util.Set> s = this.plugins.entrySet(); java.util.Iterator> it = s.iterator(); while(it.hasNext()) { Entry entry = it.next(); Plugin plugin = entry.getValue(); - plugin.onResume(); + plugin.onResume(multitasking); } } From 08c44f5c5dc82f9ef3e896aba507c0c060276416 Mon Sep 17 00:00:00 2001 From: macdonst Date: Sat, 25 Jun 2011 04:29:25 +0800 Subject: [PATCH 26/59] Issue #121: Problem with resolveLocalFileSystemURI if file name has spaces --- framework/src/com/phonegap/FileUtils.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/framework/src/com/phonegap/FileUtils.java b/framework/src/com/phonegap/FileUtils.java index a53884ef..08ff7cce 100755 --- a/framework/src/com/phonegap/FileUtils.java +++ b/framework/src/com/phonegap/FileUtils.java @@ -10,6 +10,7 @@ package com.phonegap; import java.io.*; import java.net.MalformedURLException; import java.net.URL; +import java.net.URLDecoder; import java.nio.channels.FileChannel; import org.apache.commons.codec.binary.Base64; @@ -229,15 +230,16 @@ public class FileUtils extends Plugin { * @throws JSONException */ private JSONObject resolveLocalFileSystemURI(String url) throws IOException, JSONException { + String decoded = URLDecoder.decode(url, "UTF-8"); // Test to see if this is a valid URL first - @SuppressWarnings("unused") - URL testUrl = new URL(url); - + @SuppressWarnings("unused") + URL testUrl = new URL(decoded); + File fp = null; - if (url.startsWith("file://")) { - fp = new File(url.substring(7, url.length())); + if (decoded.startsWith("file://")) { + fp = new File(decoded.substring(7, decoded.length())); } else { - fp = new File(url); + fp = new File(decoded); } if (!fp.exists()) { throw new FileNotFoundException(); From af18a8e1aa142421af32576be22ba9c7b26a2721 Mon Sep 17 00:00:00 2001 From: macdonst Date: Wed, 1 Jun 2011 02:54:18 +0800 Subject: [PATCH 27/59] Issue #80: Unable to open large json files on android 2.2 + phonegap 0.9.5 I could not get rid of the url encoding and decoding without hampering some users ability to pass non-ascii characters back to JavaScript. However, I was able to reduce the amount of data being passed from Java to JavaScript by 40% by decoding common characters that occur in JSON and XML. These characters will survive the round trip just fine and don't need to be encoded. This is the best solution I could come up with. You won't be able to read files as large as you could in 0.9.4 but it will get close and it will support non-ascii characters. --- framework/assets/js/phonegap.js.base | 4 +- .../src/com/phonegap/CallbackServer.java | 46 ++++++++++++++++++- 2 files changed, 46 insertions(+), 4 deletions(-) diff --git a/framework/assets/js/phonegap.js.base b/framework/assets/js/phonegap.js.base index 068b46e5..5d06bf99 100755 --- a/framework/assets/js/phonegap.js.base +++ b/framework/assets/js/phonegap.js.base @@ -767,8 +767,8 @@ PhoneGap.JSCallback = function() { // If callback has JavaScript statement to execute if (xmlhttp.status === 200) { - // Need to url decode the response and replace %20 with a space - var msg = decodeURIComponent(xmlhttp.responseText.replace(/\+/g, '%20')); + // Need to url decode the response + var msg = decodeURIComponent(xmlhttp.responseText); setTimeout(function() { try { var t = eval(msg); diff --git a/framework/src/com/phonegap/CallbackServer.java b/framework/src/com/phonegap/CallbackServer.java index 859ac40a..66c282ec 100755 --- a/framework/src/com/phonegap/CallbackServer.java +++ b/framework/src/com/phonegap/CallbackServer.java @@ -11,11 +11,14 @@ import java.io.BufferedReader; import java.io.DataOutputStream; import java.io.IOException; import java.io.InputStreamReader; +import java.io.UnsupportedEncodingException; import java.net.ServerSocket; import java.net.Socket; import java.net.URLEncoder; import java.util.LinkedList; +import android.util.Log; + /** * This class provides a way for Java to run JavaScript in the web page that has loaded PhoneGap. * The CallbackServer class implements an XHR server and a polling server with a list of JavaScript @@ -42,6 +45,8 @@ import java.util.LinkedList; */ public class CallbackServer implements Runnable { + private static final String LOG_TAG = "CallbackServer"; + /** * The list of JavaScript statements to be sent to JavaScript. */ @@ -224,8 +229,9 @@ public class CallbackServer implements Runnable { //System.out.println("CallbackServer -- sending item"); response = "HTTP/1.1 200 OK\r\n\r\n"; String js = this.getJavascript(); - if (js != null) - response += URLEncoder.encode(js, "UTF-8"); + if (js != null) { + response += encode(js); + } } } else { @@ -321,4 +327,40 @@ public class CallbackServer implements Runnable { } } + /** + * This will encode the return value to JavaScript. We revert the encoding for + * common characters that don't require encoding to reduce the size of the string + * being passed to JavaScript. + * + * @param value to be encoded + * @return encoded string + */ + public static String encode(String value) { + String encoded = null; + try { + encoded = URLEncoder.encode(value, "UTF-8") + .replaceAll("\\+", " ") + .replaceAll("\\%21", "!") + .replaceAll("\\%22", "\"") + .replaceAll("\\%27", "'") + .replaceAll("\\%28", "(") + .replaceAll("\\%29", ")") + .replaceAll("\\%2C", ",") + .replaceAll("\\%3C", "<") + .replaceAll("\\%3D", "=") + .replaceAll("\\%3E", ">") + .replaceAll("\\%3F", "?") + .replaceAll("\\%40", "@") + .replaceAll("\\%5B", "[") + .replaceAll("\\%5D", "]") + .replaceAll("\\%7B", "{") + .replaceAll("\\%7D", "}") + .replaceAll("\\%3A", ":") + .replaceAll("\\%7E", "~"); + } catch (UnsupportedEncodingException e) { + Log.e(LOG_TAG, e.getMessage(), e); + } + return encoded; + } + } From 088c3421989cd441167e6d23d9bfdb7d3fdd1cb2 Mon Sep 17 00:00:00 2001 From: macdonst Date: Thu, 2 Jun 2011 01:46:27 +0800 Subject: [PATCH 28/59] Improve performance of our encoding Move from using String.replaceAll() to a modified version or URLEncoder.encode(). --- .../src/com/phonegap/CallbackServer.java | 113 ++++++++++++------ 1 file changed, 77 insertions(+), 36 deletions(-) diff --git a/framework/src/com/phonegap/CallbackServer.java b/framework/src/com/phonegap/CallbackServer.java index 66c282ec..2c2a9c84 100755 --- a/framework/src/com/phonegap/CallbackServer.java +++ b/framework/src/com/phonegap/CallbackServer.java @@ -230,7 +230,7 @@ public class CallbackServer implements Runnable { response = "HTTP/1.1 200 OK\r\n\r\n"; String js = this.getJavascript(); if (js != null) { - response += encode(js); + response += encode(js, "UTF-8"); } } } @@ -327,40 +327,81 @@ public class CallbackServer implements Runnable { } } - /** - * This will encode the return value to JavaScript. We revert the encoding for - * common characters that don't require encoding to reduce the size of the string - * being passed to JavaScript. - * - * @param value to be encoded - * @return encoded string - */ - public static String encode(String value) { - String encoded = null; - try { - encoded = URLEncoder.encode(value, "UTF-8") - .replaceAll("\\+", " ") - .replaceAll("\\%21", "!") - .replaceAll("\\%22", "\"") - .replaceAll("\\%27", "'") - .replaceAll("\\%28", "(") - .replaceAll("\\%29", ")") - .replaceAll("\\%2C", ",") - .replaceAll("\\%3C", "<") - .replaceAll("\\%3D", "=") - .replaceAll("\\%3E", ">") - .replaceAll("\\%3F", "?") - .replaceAll("\\%40", "@") - .replaceAll("\\%5B", "[") - .replaceAll("\\%5D", "]") - .replaceAll("\\%7B", "{") - .replaceAll("\\%7D", "}") - .replaceAll("\\%3A", ":") - .replaceAll("\\%7E", "~"); - } catch (UnsupportedEncodingException e) { - Log.e(LOG_TAG, e.getMessage(), e); - } - return encoded; - } + /* The Following code has been modified from original implementation of URLEncoder */ + /* start */ + + /* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + static final String digits = "0123456789ABCDEF"; + + /** + * This will encode the return value to JavaScript. We revert the encoding for + * common characters that don't require encoding to reduce the size of the string + * being passed to JavaScript. + * + * @param s to be encoded + * @param enc encoding type + * @return encoded string + */ + public static String encode(String s, String enc) throws UnsupportedEncodingException { + if (s == null || enc == null) { + throw new NullPointerException(); + } + // check for UnsupportedEncodingException + "".getBytes(enc); + + // Guess a bit bigger for encoded form + StringBuilder buf = new StringBuilder(s.length() + 16); + int start = -1; + for (int i = 0; i < s.length(); i++) { + char ch = s.charAt(i); + if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') + || (ch >= '0' && ch <= '9') + || " .-*_'(),<>=?@[]{}:~\"\\/;!".indexOf(ch) > -1) { + if (start >= 0) { + convert(s.substring(start, i), buf, enc); + start = -1; + } + if (ch != ' ') { + buf.append(ch); + } else { + buf.append(' '); + } + } else { + if (start < 0) { + start = i; + } + } + } + if (start >= 0) { + convert(s.substring(start, s.length()), buf, enc); + } + return buf.toString(); + } + + private static void convert(String s, StringBuilder buf, String enc) throws UnsupportedEncodingException { + byte[] bytes = s.getBytes(enc); + for (int j = 0; j < bytes.length; j++) { + buf.append('%'); + buf.append(digits.charAt((bytes[j] & 0xf0) >> 4)); + buf.append(digits.charAt(bytes[j] & 0xf)); + } + } + + /* end */ } From 3c90a9a23cfea9938bd150a674bbd5d28e7adeef Mon Sep 17 00:00:00 2001 From: Nitobi Date: Fri, 3 Jun 2011 10:59:48 -0700 Subject: [PATCH 29/59] Fixed droidgap update command --- lib/update.rb | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/lib/update.rb b/lib/update.rb index aa1459a1..23e57a70 100755 --- a/lib/update.rb +++ b/lib/update.rb @@ -33,12 +33,19 @@ class Update # TODO need to allow for www import inc icon def copy_libs puts "Copying over libraries and assets..." - - FileUtils.mkdir_p File.join(@path, "libs") - FileUtils.cp File.join(@framework_dir, "phonegap.jar"), File.join(@path, "libs") + version = IO.read(File.join(@framework_dir, '../VERSION')) - FileUtils.mkdir_p File.join(@path, "assets", "www") - FileUtils.cp File.join(@framework_dir, "assets", "www", "phonegap.js"), File.join(@path, "assets", "www") + FileUtils.cp File.join(@framework_dir, "phonegap.#{ version }.jar"), File.join(@path, "libs") + + # concat JS and put into www folder. this can be overridden in the config.xml via @app_js_dir + js_dir = File.join(@framework_dir, "assets", "js") + phonegapjs = IO.read(File.join(js_dir, 'phonegap.js.base')) + Dir.new(js_dir).entries.each do |script| + next if script[0].chr == "." or script == "phonegap.js.base" + phonegapjs << IO.read(File.join(js_dir, script)) + phonegapjs << "\n\n" + end + File.open(File.join(@path, "assets", "www", "phonegap.#{ version }.js"), 'w') {|f| f.write(phonegapjs) } end # end \ No newline at end of file From 0280d5dd8207c0e22ede3cc7539cfdc14d456411 Mon Sep 17 00:00:00 2001 From: macdonst Date: Fri, 3 Jun 2011 01:11:51 +0800 Subject: [PATCH 30/59] Updating Connection object to conform with recently released spec - Replacing currentNW and homeNW with networkName. - Changing Connection constants to strings instead of ints. - Firing online/offline events on network change. --- framework/AndroidManifest.xml | 3 + framework/assets/js/network.js | 49 +++++++++++---- .../src/com/phonegap/NetworkManager.java | 59 +++++++++++-------- 3 files changed, 73 insertions(+), 38 deletions(-) diff --git a/framework/AndroidManifest.xml b/framework/AndroidManifest.xml index ea2a6636..2807d65b 100644 --- a/framework/AndroidManifest.xml +++ b/framework/AndroidManifest.xml @@ -14,15 +14,18 @@ + + + diff --git a/framework/assets/js/network.js b/framework/assets/js/network.js index 2e092f23..d93c95ba 100755 --- a/framework/assets/js/network.js +++ b/framework/assets/js/network.js @@ -65,29 +65,52 @@ Network.prototype.isReachable = function(uri, callback, options) { */ var Connection = function() { this.type = null; - this.homeNW = null; - this.currentNW = null; + this.networkName = null; + this._firstRun = true; + this._timer = null; + this.timeout = 500; var me = this; this.getInfo( function(info) { - me.type = info.type; - me.homeNW = info.homeNW; - me.currentNW = info.currentNW; - PhoneGap.onPhoneGapConnectionReady.fire(); + // Need to send events if we are on or offline + if (info.type == "none") { + // set a timer if still offline at the end of timer send the offline event + me._timer = setTimeout(function(){ + me.type = info.type; + me.networkName = info.networkName; + PhoneGap.fireEvent('offline'); + me._timer = null; + }, me.timeout); + } else { + // If there is a current offline event pending clear it + if (me._timer != null) { + clearTimeout(me._timer); + me._timer = null; + } + me.type = info.type; + me.networkName = info.networkName; + PhoneGap.fireEvent('online'); + } + + // should only fire this once + if (me._firstRun) { + me._firstRun = false; + PhoneGap.onPhoneGapConnectionReady.fire(); + } }, function(e) { console.log("Error initializing Network Connection: " + e); }); }; -Connection.UNKNOWN = 0; -Connection.ETHERNET = 1; -Connection.WIFI = 2; -Connection.CELL_2G = 3; -Connection.CELL_3G = 4; -Connection.CELL_4G = 5; -Connection.NONE = 20; +Connection.UNKNOWN = "unknown"; +Connection.ETHERNET = "ethernet"; +Connection.WIFI = "wifi"; +Connection.CELL_2G = "2g"; +Connection.CELL_3G = "3g"; +Connection.CELL_4G = "4g"; +Connection.NONE = "none"; /** * Get connection info diff --git a/framework/src/com/phonegap/NetworkManager.java b/framework/src/com/phonegap/NetworkManager.java index 0a2fdb1b..f05d089c 100755 --- a/framework/src/com/phonegap/NetworkManager.java +++ b/framework/src/com/phonegap/NetworkManager.java @@ -22,12 +22,14 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.net.*; +import android.net.wifi.WifiInfo; +import android.net.wifi.WifiManager; import android.telephony.TelephonyManager; import android.util.Log; public class NetworkManager extends Plugin { - public static int NOT_REACHABLE = 0; + public static int NOT_REACHABLE = 0; public static int REACHABLE_VIA_CARRIER_DATA_NETWORK = 1; public static int REACHABLE_VIA_WIFI_NETWORK = 2; @@ -46,19 +48,23 @@ public class NetworkManager extends Plugin { public static final String LTE = "lte"; public static final String UMB = "umb"; // return types - public static final int TYPE_UNKNOWN = 0; - public static final int TYPE_ETHERNET = 1; - public static final int TYPE_WIFI = 2; - public static final int TYPE_2G = 3; - public static final int TYPE_3G = 4; - public static final int TYPE_4G = 5; - public static final int TYPE_NONE = 20; + public static final String TYPE_UNKNOWN = "unknown"; + public static final String TYPE_ETHERNET = "ethernet"; + public static final String TYPE_WIFI = "wifi"; + public static final String TYPE_2G = "2g"; + public static final String TYPE_3G = "3g"; + public static final String TYPE_4G = "4g"; + public static final String TYPE_NONE = "none"; private static final String LOG_TAG = "NetworkManager"; + private static final String NETWORK_NAME = "networkName"; + private static final String TYPE = "type"; + private String connectionCallbackId; ConnectivityManager sockMan; TelephonyManager telephonyManager; + WifiManager wifiManager; BroadcastReceiver receiver; /** @@ -78,8 +84,9 @@ public class NetworkManager extends Plugin { super.setContext(ctx); this.sockMan = (ConnectivityManager) ctx.getSystemService(Context.CONNECTIVITY_SERVICE); this.telephonyManager = ((TelephonyManager) ctx.getSystemService(Context.TELEPHONY_SERVICE)); + this.wifiManager = ((WifiManager) ctx.getSystemService(Context.WIFI_SERVICE)); this.connectionCallbackId = null; - + // We need to listen to connectivity events to update navigator.connection IntentFilter intentFilter = new IntentFilter() ; intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); @@ -151,7 +158,7 @@ public class NetworkManager extends Plugin { try { this.ctx.unregisterReceiver(this.receiver); } catch (Exception e) { - System.out.println("Error unregistering network receiver: " + e.getMessage()); + Log.e(LOG_TAG, "Error unregistering network receiver: " + e.getMessage(), e); } } } @@ -167,11 +174,11 @@ public class NetworkManager extends Plugin { * @param info the current active network info * @return */ - private void updateConnectionInfo(NetworkInfo info) { - JSONObject connection = this.getConnectionInfo(info); - - // send update to javascript "navigator.connection" - sendUpdate(connection); + private void updateConnectionInfo(NetworkInfo info) { + JSONObject connection = this.getConnectionInfo(info); + + // send update to javascript "navigator.network.connection" + sendUpdate(connection); } /** @@ -187,24 +194,26 @@ public class NetworkManager extends Plugin { if (info != null) { // If we are not connected to any network set type to none if (!info.isConnected()) { - connection.put("type", TYPE_NONE); - connection.put("homeNW", null); - connection.put("currentNW", null); + connection.put(TYPE, TYPE_NONE); + connection.put(NETWORK_NAME, null); } else { // If we are connected check which type // First off is wifi if (info.getTypeName().toLowerCase().equals(WIFI)) { - connection.put("type", TYPE_WIFI); - connection.put("homeNW", null); - connection.put("currentNW", null); + connection.put(TYPE, TYPE_WIFI); + WifiInfo wifiInfo = this.wifiManager.getConnectionInfo(); + if (wifiInfo != null) { + connection.put(NETWORK_NAME, wifiInfo.getSSID()); + } else { + connection.put(NETWORK_NAME, null); + } } // Otherwise it must be one of the mobile network protocols else { // Determine the correct type, 2G, 3G, 4G - connection.put("type", getType(info)); - connection.put("homeNW", telephonyManager.getSimOperatorName()); - connection.put("currentNW", telephonyManager.getNetworkOperatorName()); + connection.put(TYPE, getType(info)); + connection.put(NETWORK_NAME, telephonyManager.getNetworkOperatorName()); } } } @@ -233,7 +242,7 @@ public class NetworkManager extends Plugin { * @param info the network info so we can determine connection type. * @return the type of mobile network we are on */ - private int getType(NetworkInfo info) { + private String getType(NetworkInfo info) { if (info != null) { String type = info.getTypeName(); From 85eb6e49972e658ff86d31eb1fdcf8ed664f8c74 Mon Sep 17 00:00:00 2001 From: macdonst Date: Tue, 7 Jun 2011 02:37:29 +0800 Subject: [PATCH 31/59] Issue #104: Bug in FileUtils.copyDirectory & moveDirectory Adding better test to see if a directory is being moved/copied 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 --- framework/src/com/phonegap/FileUtils.java | 26 ++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/framework/src/com/phonegap/FileUtils.java b/framework/src/com/phonegap/FileUtils.java index a2ed7a11..a53884ef 100755 --- a/framework/src/com/phonegap/FileUtils.java +++ b/framework/src/com/phonegap/FileUtils.java @@ -411,7 +411,7 @@ public class FileUtils extends Plugin { } // Check to make sure we are not copying the directory into itself - if (destinationDir.getAbsolutePath().startsWith(srcDir.getAbsolutePath())) { + if (isCopyOnItself(srcDir.getAbsolutePath(), destinationDir.getAbsolutePath())) { throw new InvalidModificationException("Can't copy itself into itself"); } @@ -435,6 +435,26 @@ public class FileUtils extends Plugin { return getEntry(destinationDir); } + /** + * Check to see if the user attempted to copy an entry into its parent without changing its name, + * or attempted to copy a directory into a directory that it contains directly or indirectly. + * + * @param srcDir + * @param destinationDir + * @return + */ + private boolean isCopyOnItself(String src, String dest) { + + // 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) { + return true; + } + + return false; + } + /** * Move a file * @@ -480,8 +500,8 @@ public class FileUtils extends Plugin { } // Check to make sure we are not copying the directory into itself - if (destinationDir.getAbsolutePath().startsWith(srcDir.getAbsolutePath())) { - throw new InvalidModificationException("Can't copy itself into itself"); + if (isCopyOnItself(srcDir.getAbsolutePath(), destinationDir.getAbsolutePath())) { + throw new InvalidModificationException("Can't move itself into itself"); } // If the destination directory already exists and is empty then delete it. This is according to spec. From ff7de25b625f4b42967b25d1923c299f29afac30 Mon Sep 17 00:00:00 2001 From: macdonst Date: Tue, 7 Jun 2011 22:03:16 +0800 Subject: [PATCH 32/59] Issue #106: Typo in LocalFileSystem.prototype._castDate --- framework/assets/js/file.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/assets/js/file.js b/framework/assets/js/file.js index d2f21cd3..11229b35 100755 --- a/framework/assets/js/file.js +++ b/framework/assets/js/file.js @@ -1044,7 +1044,7 @@ LocalFileSystem.prototype._castDate = function(pluginResult) { file.type = pluginResult.message.type; file.name = pluginResult.message.name; file.fullPath = pluginResult.message.fullPath; - file.lastModifedDate = new Date(pluginResult.message.lastModifiedDate); + file.lastModifiedDate = new Date(pluginResult.message.lastModifiedDate); pluginResult.message = file; } return pluginResult; From 66f3018767ec8eebf4540b33c8c115e57575b24f Mon Sep 17 00:00:00 2001 From: Bryce Curtis Date: Tue, 7 Jun 2011 14:18:18 -0500 Subject: [PATCH 33/59] Set PhoneGap.UsePolling flag based upon result from CallbackServer. --- framework/assets/js/phonegap.js.base | 3 +++ 1 file changed, 3 insertions(+) diff --git a/framework/assets/js/phonegap.js.base b/framework/assets/js/phonegap.js.base index 5d06bf99..e48fbf55 100755 --- a/framework/assets/js/phonegap.js.base +++ b/framework/assets/js/phonegap.js.base @@ -344,10 +344,13 @@ PhoneGap.Channel.join(function() { } else { var polling = prompt("usePolling", "gap_callbackServer:"); + PhoneGap.UsePolling = polling; if (polling == "true") { + PhoneGap.UsePolling = true; PhoneGap.JSCallbackPolling(); } else { + PhoneGap.UsePolling = false; PhoneGap.JSCallback(); } } From 8a1ab69235195188ff7d997aba446d7318ece0a0 Mon Sep 17 00:00:00 2001 From: macdonst Date: Thu, 9 Jun 2011 03:03:03 +0800 Subject: [PATCH 34/59] Updating Network Connection API to match spec released on June 7th --- framework/AndroidManifest.xml | 2 - framework/assets/js/network.js | 11 ++- framework/src/com/phonegap/Device.java | 32 ++------- .../src/com/phonegap/NetworkManager.java | 70 +++++-------------- 4 files changed, 27 insertions(+), 88 deletions(-) diff --git a/framework/AndroidManifest.xml b/framework/AndroidManifest.xml index 2807d65b..53d7afb5 100644 --- a/framework/AndroidManifest.xml +++ b/framework/AndroidManifest.xml @@ -14,8 +14,6 @@ - - diff --git a/framework/assets/js/network.js b/framework/assets/js/network.js index d93c95ba..be94402b 100755 --- a/framework/assets/js/network.js +++ b/framework/assets/js/network.js @@ -65,20 +65,18 @@ Network.prototype.isReachable = function(uri, callback, options) { */ var Connection = function() { this.type = null; - this.networkName = null; this._firstRun = true; this._timer = null; this.timeout = 500; var me = this; this.getInfo( - function(info) { + function(type) { // Need to send events if we are on or offline - if (info.type == "none") { + if (type == "none") { // set a timer if still offline at the end of timer send the offline event me._timer = setTimeout(function(){ - me.type = info.type; - me.networkName = info.networkName; + me.type = type; PhoneGap.fireEvent('offline'); me._timer = null; }, me.timeout); @@ -88,8 +86,7 @@ var Connection = function() { clearTimeout(me._timer); me._timer = null; } - me.type = info.type; - me.networkName = info.networkName; + me.type = type; PhoneGap.fireEvent('online'); } diff --git a/framework/src/com/phonegap/Device.java b/framework/src/com/phonegap/Device.java index ac0a8ecc..035058fe 100755 --- a/framework/src/com/phonegap/Device.java +++ b/framework/src/com/phonegap/Device.java @@ -14,9 +14,7 @@ import org.json.JSONObject; import com.phonegap.api.PhonegapActivity; import com.phonegap.api.Plugin; import com.phonegap.api.PluginResult; -import android.content.Context; import android.provider.Settings; -import android.telephony.TelephonyManager; public class Device extends Plugin { @@ -116,34 +114,13 @@ public class Device extends Plugin { public String getPhonegapVersion() { return Device.phonegapVersion; } - - public String getLine1Number(){ - TelephonyManager operator = (TelephonyManager)this.ctx.getSystemService(Context.TELEPHONY_SERVICE); - return operator.getLine1Number(); - } - public String getDeviceId(){ - TelephonyManager operator = (TelephonyManager)this.ctx.getSystemService(Context.TELEPHONY_SERVICE); - return operator.getDeviceId(); - } - - public String getSimSerialNumber(){ - TelephonyManager operator = (TelephonyManager)this.ctx.getSystemService(Context.TELEPHONY_SERVICE); - return operator.getSimSerialNumber(); - } - - public String getSubscriberId(){ - TelephonyManager operator = (TelephonyManager)this.ctx.getSystemService(Context.TELEPHONY_SERVICE); - return operator.getSubscriberId(); - } - - public String getModel() - { + public String getModel() { String model = android.os.Build.MODEL; return model; } - public String getProductName() - { + + public String getProductName() { String productname = android.os.Build.PRODUCT; return productname; } @@ -158,8 +135,7 @@ public class Device extends Plugin { return osversion; } - public String getSDKVersion() - { + public String getSDKVersion() { String sdkversion = android.os.Build.VERSION.SDK; return sdkversion; } diff --git a/framework/src/com/phonegap/NetworkManager.java b/framework/src/com/phonegap/NetworkManager.java index f05d089c..c2b756bc 100755 --- a/framework/src/com/phonegap/NetworkManager.java +++ b/framework/src/com/phonegap/NetworkManager.java @@ -11,7 +11,6 @@ import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.DefaultHttpClient; import org.json.JSONArray; import org.json.JSONException; -import org.json.JSONObject; import com.phonegap.api.PhonegapActivity; import com.phonegap.api.Plugin; @@ -21,10 +20,8 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.net.*; -import android.net.wifi.WifiInfo; -import android.net.wifi.WifiManager; -import android.telephony.TelephonyManager; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; import android.util.Log; public class NetworkManager extends Plugin { @@ -57,14 +54,10 @@ public class NetworkManager extends Plugin { public static final String TYPE_NONE = "none"; private static final String LOG_TAG = "NetworkManager"; - private static final String NETWORK_NAME = "networkName"; - private static final String TYPE = "type"; private String connectionCallbackId; ConnectivityManager sockMan; - TelephonyManager telephonyManager; - WifiManager wifiManager; BroadcastReceiver receiver; /** @@ -83,8 +76,6 @@ public class NetworkManager extends Plugin { public void setContext(PhonegapActivity ctx) { super.setContext(ctx); this.sockMan = (ConnectivityManager) ctx.getSystemService(Context.CONNECTIVITY_SERVICE); - this.telephonyManager = ((TelephonyManager) ctx.getSystemService(Context.TELEPHONY_SERVICE)); - this.wifiManager = ((WifiManager) ctx.getSystemService(Context.WIFI_SERVICE)); this.connectionCallbackId = null; // We need to listen to connectivity events to update navigator.connection @@ -175,10 +166,8 @@ public class NetworkManager extends Plugin { * @return */ private void updateConnectionInfo(NetworkInfo info) { - JSONObject connection = this.getConnectionInfo(info); - // send update to javascript "navigator.network.connection" - sendUpdate(connection); + sendUpdate(this.getConnectionInfo(info)); } /** @@ -187,42 +176,18 @@ public class NetworkManager extends Plugin { * @param info the current active network info * @return a JSONObject that represents the network info */ - private JSONObject getConnectionInfo(NetworkInfo info) { - JSONObject connection = new JSONObject(); - - try { - if (info != null) { - // If we are not connected to any network set type to none - if (!info.isConnected()) { - connection.put(TYPE, TYPE_NONE); - connection.put(NETWORK_NAME, null); - } - else { - // If we are connected check which type - // First off is wifi - if (info.getTypeName().toLowerCase().equals(WIFI)) { - connection.put(TYPE, TYPE_WIFI); - WifiInfo wifiInfo = this.wifiManager.getConnectionInfo(); - if (wifiInfo != null) { - connection.put(NETWORK_NAME, wifiInfo.getSSID()); - } else { - connection.put(NETWORK_NAME, null); - } - } - // Otherwise it must be one of the mobile network protocols - else { - // Determine the correct type, 2G, 3G, 4G - connection.put(TYPE, getType(info)); - connection.put(NETWORK_NAME, telephonyManager.getNetworkOperatorName()); - } - } + private String getConnectionInfo(NetworkInfo info) { + String type = TYPE_NONE; + if (info != null) { + // If we are not connected to any network set type to none + if (!info.isConnected()) { + type = TYPE_NONE; + } + else { + type = getType(info); } } - catch (JSONException e) { - // this should never happen - Log.e(LOG_TAG, e.getMessage(), e); - } - return connection; + return type; } /** @@ -230,8 +195,8 @@ public class NetworkManager extends Plugin { * * @param connection the network info to set as navigator.connection */ - private void sendUpdate(JSONObject connection) { - PluginResult result = new PluginResult(PluginResult.Status.OK, connection); + private void sendUpdate(String type) { + PluginResult result = new PluginResult(PluginResult.Status.OK, type); result.setKeepCallback(true); this.success(result, this.connectionCallbackId); } @@ -246,7 +211,10 @@ public class NetworkManager extends Plugin { if (info != null) { String type = info.getTypeName(); - if (type.toLowerCase().equals(MOBILE)) { + if (type.toLowerCase().equals(WIFI)) { + return TYPE_WIFI; + } + else if (type.toLowerCase().equals(MOBILE)) { type = info.getSubtypeName(); if (type.toLowerCase().equals(GSM) || type.toLowerCase().equals(GPRS) || From eb3b1f91d4949a66ff91fdfe48475ab9f8b60d18 Mon Sep 17 00:00:00 2001 From: Benjamin Weingarten Date: Sun, 12 Jun 2011 12:24:25 +0300 Subject: [PATCH 35/59] Fix bug where isreachable doesn't return correct results for https (http secure) url protocol. --- framework/src/com/phonegap/NetworkManager.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/src/com/phonegap/NetworkManager.java b/framework/src/com/phonegap/NetworkManager.java index c2b756bc..62140f31 100755 --- a/framework/src/com/phonegap/NetworkManager.java +++ b/framework/src/com/phonegap/NetworkManager.java @@ -275,7 +275,7 @@ public class NetworkManager extends Plugin { public int isReachable(String uri, boolean isIpAddress) { int reachable = NOT_REACHABLE; - if (uri.indexOf("http://") == -1) { + if (uri.indexOf("http://") == -1 && uri.indexOf("https://") == -1) { uri = "http://" + uri; } @@ -298,4 +298,4 @@ public class NetworkManager extends Plugin { return reachable; } -} \ No newline at end of file +} From d35e8cd44b51a00e781432b2020fa40febacb9e4 Mon Sep 17 00:00:00 2001 From: Bryce Curtis Date: Mon, 13 Jun 2011 15:16:08 -0500 Subject: [PATCH 36/59] Fix security vulnerability - make sure any requests to run native code only come from url currently loaded into webview. --- framework/src/com/phonegap/DroidGap.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/framework/src/com/phonegap/DroidGap.java b/framework/src/com/phonegap/DroidGap.java index d09165e7..93dbe552 100755 --- a/framework/src/com/phonegap/DroidGap.java +++ b/framework/src/com/phonegap/DroidGap.java @@ -787,10 +787,14 @@ public class DroidGap extends PhonegapActivity { */ @Override public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) { + boolean reqOk = false; + if (((DroidGap)(this.ctx)).url.equals(url)) { + reqOk = true; + } // Calling PluginManager.exec() to call a native service using // prompt(this.stringify(args), "gap:"+this.stringify([service, action, callbackId, true])); - if (defaultValue != null && defaultValue.length() > 3 && defaultValue.substring(0, 4).equals("gap:")) { + if (reqOk && defaultValue != null && defaultValue.length() > 3 && defaultValue.substring(0, 4).equals("gap:")) { JSONArray array; try { array = new JSONArray(defaultValue.substring(4)); @@ -806,13 +810,13 @@ public class DroidGap extends PhonegapActivity { } // Polling for JavaScript messages - else if (defaultValue.equals("gap_poll:")) { + else if (reqOk && defaultValue.equals("gap_poll:")) { String r = callbackServer.getJavascript(); result.confirm(r); } // Calling into CallbackServer - else if (defaultValue.equals("gap_callbackServer:")) { + else if (reqOk && defaultValue.equals("gap_callbackServer:")) { String r = ""; if (message.equals("usePolling")) { r = ""+callbackServer.usePolling(); From 80e66d87a9508adc1b4ea7aa1ce42da5a6ec610b Mon Sep 17 00:00:00 2001 From: Bryce Curtis Date: Wed, 15 Jun 2011 13:01:03 -0500 Subject: [PATCH 37/59] Issue 112: PhoneGap.Channel: replace instanceof Function with typeof === 'Function' --- framework/assets/js/phonegap.js.base | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/framework/assets/js/phonegap.js.base b/framework/assets/js/phonegap.js.base index e48fbf55..4f26fd8d 100755 --- a/framework/assets/js/phonegap.js.base +++ b/framework/assets/js/phonegap.js.base @@ -99,7 +99,7 @@ PhoneGap.Channel.prototype.subscribe = function(f, c, g) { if (f === null) { return; } var func = f; - if (typeof c === "object" && f instanceof Function) { func = PhoneGap.close(c, f); } + if (typeof c === "object" && typeof f === "function") { func = PhoneGap.close(c, f); } g = g || func.observer_guid || f.observer_guid || this.guid++; func.observer_guid = g; @@ -120,7 +120,7 @@ PhoneGap.Channel.prototype.subscribeOnce = function(f, c) { _this.unsubscribe(g); }; if (this.fired) { - if (typeof c === "object" && f instanceof Function) { f = PhoneGap.close(c, f); } + if (typeof c === "object" && typeof f === "function") { f = PhoneGap.close(c, f); } f.apply(this, this.fireArgs); } else { g = this.subscribe(m); @@ -132,7 +132,7 @@ PhoneGap.Channel.prototype.subscribeOnce = function(f, c) { * Unsubscribes the function with the given guid from the channel. */ PhoneGap.Channel.prototype.unsubscribe = function(g) { - if (g instanceof Function) { g = g.observer_guid; } + if (typeof g === "function") { g = g.observer_guid; } this.handlers[g] = null; delete this.handlers[g]; }; @@ -147,7 +147,7 @@ PhoneGap.Channel.prototype.fire = function(e) { for (item in this.handlers) { if (this.handlers.hasOwnProperty(item)) { handler = this.handlers[item]; - if (handler instanceof Function) { + if (typeof handler === "function") { rv = (handler.apply(this, arguments) === false); fail = fail || rv; } @@ -510,7 +510,7 @@ PhoneGap.clone = function(obj) { return retVal; } - if (obj instanceof Function) { + if (typeof obj === "function") { return obj; } From a5039f021d344b7d38d642d4973a838508550420 Mon Sep 17 00:00:00 2001 From: Kevin Griffin Date: Tue, 21 Jun 2011 11:32:18 -0400 Subject: [PATCH 38/59] made the mistake of opening anything in finder --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 5c53ca30..afa78b8d 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ local.properties framework/phonegap.jar framework/bin framework/assets/www/.DS_Store +.DS_Store \ No newline at end of file From 7bc0d624acd8c09f18b8fe72b5cd29ed1285e2e7 Mon Sep 17 00:00:00 2001 From: Joe Bowser Date: Fri, 24 Jun 2011 14:08:26 -0700 Subject: [PATCH 39/59] Fixing conflict --- framework/src/com/phonegap/DroidGap.java | 37 ++++++++++++------- framework/src/com/phonegap/api/Plugin.java | 6 +++ .../src/com/phonegap/api/PluginManager.java | 11 ++++++ 3 files changed, 41 insertions(+), 13 deletions(-) diff --git a/framework/src/com/phonegap/DroidGap.java b/framework/src/com/phonegap/DroidGap.java index 93dbe552..d3984fa3 100755 --- a/framework/src/com/phonegap/DroidGap.java +++ b/framework/src/com/phonegap/DroidGap.java @@ -12,12 +12,10 @@ import org.json.JSONException; import android.app.Activity; import android.app.AlertDialog; -import android.widget.EditText; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.res.Configuration; -import android.graphics.Bitmap; import android.graphics.Color; import android.graphics.Rect; import android.media.AudioManager; @@ -30,19 +28,21 @@ import android.view.View; import android.view.ViewGroup; import android.view.Window; import android.view.WindowManager; +import android.webkit.GeolocationPermissions.Callback; +import android.webkit.JsPromptResult; import android.webkit.JsResult; import android.webkit.WebChromeClient; -import android.webkit.JsPromptResult; import android.webkit.WebSettings; +import android.webkit.WebSettings.LayoutAlgorithm; import android.webkit.WebStorage; import android.webkit.WebView; import android.webkit.WebViewClient; -import android.webkit.GeolocationPermissions.Callback; -import android.webkit.WebSettings.LayoutAlgorithm; +import android.widget.EditText; import android.widget.LinearLayout; + +import com.phonegap.api.PhonegapActivity; import com.phonegap.api.Plugin; import com.phonegap.api.PluginManager; -import com.phonegap.api.PhonegapActivity; /** * This class is the main Android activity that represents the PhoneGap @@ -616,17 +616,28 @@ public class DroidGap extends PhonegapActivity { // Send pause event to JavaScript this.appView.loadUrl("javascript:try{PhoneGap.onPause.fire();}catch(e){};"); + // Forward to plugins + this.pluginManager.onPause(); + // If app doesn't want to run in background if (!this.keepRunning) { - - // Forward to plugins - this.pluginManager.onPause(); // Pause JavaScript timers (including setInterval) this.appView.pauseTimers(); } } - + + @Override + /** + * Called when the activity receives a new intent + **/ + protected void onNewIntent(Intent intent) { + super.onNewIntent(intent); + + //Forward to plugins + this.pluginManager.onNewIntent(intent); + } + @Override /** * Called when the activity will start interacting with the user. @@ -640,6 +651,9 @@ public class DroidGap extends PhonegapActivity { // Send resume event to JavaScript this.appView.loadUrl("javascript:try{PhoneGap.onResume.fire();}catch(e){};"); + // Forward to plugins + this.pluginManager.onResume(); + // If app doesn't want to run in background if (!this.keepRunning || this.activityResultKeepRunning) { @@ -649,9 +663,6 @@ public class DroidGap extends PhonegapActivity { this.activityResultKeepRunning = false; } - // Forward to plugins - this.pluginManager.onResume(); - // Resume JavaScript timers (including setInterval) this.appView.resumeTimers(); } diff --git a/framework/src/com/phonegap/api/Plugin.java b/framework/src/com/phonegap/api/Plugin.java index a527159a..39da4b89 100755 --- a/framework/src/com/phonegap/api/Plugin.java +++ b/framework/src/com/phonegap/api/Plugin.java @@ -74,6 +74,12 @@ public abstract class Plugin implements IPlugin { public void onResume() { } + /** + * Called when the activity receives a new intent. + */ + public void onNewIntent(Intent intent) { + } + /** * The final call you receive before your activity is destroyed. */ diff --git a/framework/src/com/phonegap/api/PluginManager.java b/framework/src/com/phonegap/api/PluginManager.java index ad1cf20b..616ee856 100755 --- a/framework/src/com/phonegap/api/PluginManager.java +++ b/framework/src/com/phonegap/api/PluginManager.java @@ -13,6 +13,7 @@ import java.util.Map.Entry; import org.json.JSONArray; import org.json.JSONException; +import android.content.Intent; import android.webkit.WebView; /** @@ -265,4 +266,14 @@ public final class PluginManager { plugin.onDestroy(); } } + + public void onNewIntent(Intent intent) { + java.util.Set> s = this.plugins.entrySet(); + java.util.Iterator> it = s.iterator(); + while(it.hasNext()) { + Entry entry = it.next(); + Plugin plugin = entry.getValue(); + plugin.onNewIntent(intent); + } + } } \ No newline at end of file From 7d53eb8e3e2ce8c52010a54831ace4ce07b63445 Mon Sep 17 00:00:00 2001 From: Kevin Griffin Date: Tue, 21 Jun 2011 11:46:54 -0400 Subject: [PATCH 40/59] formattage --- framework/src/com/phonegap/DroidGap.java | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/framework/src/com/phonegap/DroidGap.java b/framework/src/com/phonegap/DroidGap.java index d3984fa3..211dbde5 100755 --- a/framework/src/com/phonegap/DroidGap.java +++ b/framework/src/com/phonegap/DroidGap.java @@ -627,16 +627,16 @@ public class DroidGap extends PhonegapActivity { } } - @Override - /** - * Called when the activity receives a new intent - **/ - protected void onNewIntent(Intent intent) { - super.onNewIntent(intent); - - //Forward to plugins - this.pluginManager.onNewIntent(intent); - } + @Override + /** + * Called when the activity receives a new intent + **/ + protected void onNewIntent(Intent intent) { + super.onNewIntent(intent); + + //Forward to plugins + this.pluginManager.onNewIntent(intent); + } @Override /** From 44aa0aeb0ffd4360e304afc9a8fdedfb10ebb42a Mon Sep 17 00:00:00 2001 From: Joe Bowser Date: Tue, 21 Jun 2011 10:08:42 -0700 Subject: [PATCH 41/59] Adding SSL dev code --- framework/src/com/phonegap/DroidGap.java | 26 ++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/framework/src/com/phonegap/DroidGap.java b/framework/src/com/phonegap/DroidGap.java index 211dbde5..c106468c 100755 --- a/framework/src/com/phonegap/DroidGap.java +++ b/framework/src/com/phonegap/DroidGap.java @@ -15,11 +15,15 @@ import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Configuration; import android.graphics.Color; import android.graphics.Rect; import android.media.AudioManager; import android.net.Uri; +import android.net.http.SslError; import android.os.Bundle; import android.util.Log; import android.view.Display; @@ -31,6 +35,7 @@ import android.view.WindowManager; import android.webkit.GeolocationPermissions.Callback; import android.webkit.JsPromptResult; import android.webkit.JsResult; +import android.webkit.SslErrorHandler; import android.webkit.WebChromeClient; import android.webkit.WebSettings; import android.webkit.WebSettings.LayoutAlgorithm; @@ -1134,6 +1139,27 @@ public class DroidGap extends PhonegapActivity { // Handle error this.ctx.onReceivedError(errorCode, description, failingUrl); } + + public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { + + final String packageName = this.ctx.getPackageName(); + final PackageManager pm = this.ctx.getPackageManager(); + ApplicationInfo appInfo; + try { + appInfo = pm.getApplicationInfo(packageName, PackageManager.GET_META_DATA); + if ((appInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) { + // debug = true + handler.proceed(); + return; + } else { + // debug = false + super.onReceivedSslError(view, handler, error); + } + } catch (NameNotFoundException e) { + // When it doubt, lock it out! + super.onReceivedSslError(view, handler, error); + } + } } /** From 54fdcbfd46165ce4932a8495f366cc94b3443d82 Mon Sep 17 00:00:00 2001 From: Bryce Curtis Date: Tue, 21 Jun 2011 22:50:53 -0500 Subject: [PATCH 42/59] Urls with same path and file but different # or ? should compare to same url. --- framework/src/com/phonegap/DroidGap.java | 28 +++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/framework/src/com/phonegap/DroidGap.java b/framework/src/com/phonegap/DroidGap.java index c106468c..b89de591 100755 --- a/framework/src/com/phonegap/DroidGap.java +++ b/framework/src/com/phonegap/DroidGap.java @@ -128,8 +128,13 @@ public class DroidGap extends PhonegapActivity { protected boolean clearHistory = false; // The initial URL for our app + // ie http://server/path/index.html#abc?query private String url; + // The initial URL for our app up to and including the file name + // ie http://server/path/index.html + private String urlFile; + // The base of the initial URL for our app private String baseUrl; @@ -343,6 +348,7 @@ public class DroidGap extends PhonegapActivity { */ public void loadUrl(final String url) { System.out.println("loadUrl("+url+")"); + this.urlFile = this.getUrlFile(url); this.url = url; int i = url.lastIndexOf('/'); if (i > 0) { @@ -717,13 +723,29 @@ public class DroidGap extends PhonegapActivity { this.callbackServer.sendJavascript(statement); } + /** + * Return up to file part of url. + * If url = http://server/page.html#abc, then return http://server/page.html + * + * @param url + * @return + */ + private String getUrlFile(String url) { + int p1 = url.indexOf("#"); + int p2 = url.indexOf("?"); + if (p1 < 0) p1 = url.length(); + if (p2 < 0) p2 = url.length(); + int p3 = (p1 < p2) ? p1 : p2; + return url.substring(0, p3); + } + /** * Provides a hook for calling "alert" from javascript. Useful for * debugging your javascript. */ public class GapClient extends WebChromeClient { - private Context ctx; + private DroidGap ctx; /** * Constructor. @@ -731,7 +753,7 @@ public class DroidGap extends PhonegapActivity { * @param ctx */ public GapClient(Context ctx) { - this.ctx = ctx; + this.ctx = (DroidGap)ctx; } /** @@ -804,7 +826,7 @@ public class DroidGap extends PhonegapActivity { @Override public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) { boolean reqOk = false; - if (((DroidGap)(this.ctx)).url.equals(url)) { + if (this.ctx.urlFile.equals(this.ctx.getUrlFile(url))) { reqOk = true; } From 53de070a4122d3adec739650654a6f0a3020ff5e Mon Sep 17 00:00:00 2001 From: Bryce Curtis Date: Tue, 21 Jun 2011 22:53:45 -0500 Subject: [PATCH 43/59] Return true when handling key events, indicating that no further processing is necessary. --- framework/src/com/phonegap/DroidGap.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/framework/src/com/phonegap/DroidGap.java b/framework/src/com/phonegap/DroidGap.java index b89de591..d1b9b808 100755 --- a/framework/src/com/phonegap/DroidGap.java +++ b/framework/src/com/phonegap/DroidGap.java @@ -1202,6 +1202,7 @@ public class DroidGap extends PhonegapActivity { // If back key is bound, then send event to JavaScript if (this.bound) { this.appView.loadUrl("javascript:PhoneGap.fireEvent('backbutton');"); + return true; } // If not bound @@ -1210,6 +1211,7 @@ public class DroidGap extends PhonegapActivity { // Go to previous page in webview if it is possible to go back if (this.appView.canGoBack()) { this.appView.goBack(); + return true; } // If not, then invoke behavior of super class @@ -1222,11 +1224,13 @@ public class DroidGap extends PhonegapActivity { // If menu key else if (keyCode == KeyEvent.KEYCODE_MENU) { this.appView.loadUrl("javascript:PhoneGap.fireEvent('menubutton');"); + return true; } // If search key else if (keyCode == KeyEvent.KEYCODE_SEARCH) { this.appView.loadUrl("javascript:PhoneGap.fireEvent('searchbutton');"); + return true; } return false; From 435c903baf281ce835bdf19c51ea7c5dfa9823af Mon Sep 17 00:00:00 2001 From: Kevin Griffin Date: Wed, 22 Jun 2011 11:05:33 -0400 Subject: [PATCH 44/59] formatting - sigh --- framework/src/com/phonegap/DroidGap.java | 24 +++++++++---------- framework/src/com/phonegap/api/Plugin.java | 4 ++-- .../src/com/phonegap/api/PluginManager.java | 3 +++ 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/framework/src/com/phonegap/DroidGap.java b/framework/src/com/phonegap/DroidGap.java index d1b9b808..2da3fd37 100755 --- a/framework/src/com/phonegap/DroidGap.java +++ b/framework/src/com/phonegap/DroidGap.java @@ -637,18 +637,18 @@ public class DroidGap extends PhonegapActivity { this.appView.pauseTimers(); } } - - @Override - /** - * Called when the activity receives a new intent - **/ - protected void onNewIntent(Intent intent) { - super.onNewIntent(intent); - - //Forward to plugins - this.pluginManager.onNewIntent(intent); - } - + + @Override + /** + * Called when the activity receives a new intent + **/ + protected void onNewIntent(Intent intent) { + super.onNewIntent(intent); + + //Forward to plugins + this.pluginManager.onNewIntent(intent); + } + @Override /** * Called when the activity will start interacting with the user. diff --git a/framework/src/com/phonegap/api/Plugin.java b/framework/src/com/phonegap/api/Plugin.java index 39da4b89..502552c6 100755 --- a/framework/src/com/phonegap/api/Plugin.java +++ b/framework/src/com/phonegap/api/Plugin.java @@ -77,8 +77,8 @@ public abstract class Plugin implements IPlugin { /** * Called when the activity receives a new intent. */ - public void onNewIntent(Intent intent) { - } + public void onNewIntent(Intent intent) { + } /** * The final call you receive before your activity is destroyed. diff --git a/framework/src/com/phonegap/api/PluginManager.java b/framework/src/com/phonegap/api/PluginManager.java index 616ee856..7dafa564 100755 --- a/framework/src/com/phonegap/api/PluginManager.java +++ b/framework/src/com/phonegap/api/PluginManager.java @@ -267,6 +267,9 @@ public final class PluginManager { } } + /** + * Called when the activity receives a new intent. + */ public void onNewIntent(Intent intent) { java.util.Set> s = this.plugins.entrySet(); java.util.Iterator> it = s.iterator(); From 05eacf4792278ccad1117fc66427a3a3298bc6e2 Mon Sep 17 00:00:00 2001 From: Bryce Curtis Date: Thu, 23 Jun 2011 23:22:48 -0500 Subject: [PATCH 45/59] Always call plugin's onPause/onResume with multitasking flag when these lifecycle events occur in activity. It is up to the plugin to handle as necessary. --- framework/src/com/phonegap/DroidGap.java | 4 ++-- framework/src/com/phonegap/api/IPlugin.java | 8 ++++++-- framework/src/com/phonegap/api/Plugin.java | 8 ++++++-- framework/src/com/phonegap/api/PluginManager.java | 12 ++++++++---- 4 files changed, 22 insertions(+), 10 deletions(-) diff --git a/framework/src/com/phonegap/DroidGap.java b/framework/src/com/phonegap/DroidGap.java index 2da3fd37..b1323817 100755 --- a/framework/src/com/phonegap/DroidGap.java +++ b/framework/src/com/phonegap/DroidGap.java @@ -628,7 +628,7 @@ public class DroidGap extends PhonegapActivity { this.appView.loadUrl("javascript:try{PhoneGap.onPause.fire();}catch(e){};"); // Forward to plugins - this.pluginManager.onPause(); + this.pluginManager.onPause(this.keepRunning); // If app doesn't want to run in background if (!this.keepRunning) { @@ -663,7 +663,7 @@ public class DroidGap extends PhonegapActivity { this.appView.loadUrl("javascript:try{PhoneGap.onResume.fire();}catch(e){};"); // Forward to plugins - this.pluginManager.onResume(); + this.pluginManager.onResume(this.keepRunning || this.activityResultKeepRunning); // If app doesn't want to run in background if (!this.keepRunning || this.activityResultKeepRunning) { diff --git a/framework/src/com/phonegap/api/IPlugin.java b/framework/src/com/phonegap/api/IPlugin.java index b3b3a1da..b9c316e0 100755 --- a/framework/src/com/phonegap/api/IPlugin.java +++ b/framework/src/com/phonegap/api/IPlugin.java @@ -54,13 +54,17 @@ public interface IPlugin { /** * Called when the system is about to start resuming a previous activity. + * + * @param multitasking Flag indicating if multitasking is turned on for app */ - void onPause(); + void onPause(boolean multitasking); /** * Called when the activity will start interacting with the user. + * + * @param multitasking Flag indicating if multitasking is turned on for app */ - void onResume(); + void onResume(boolean multitasking); /** * The final call you receive before your activity is destroyed. diff --git a/framework/src/com/phonegap/api/Plugin.java b/framework/src/com/phonegap/api/Plugin.java index 502552c6..746bd408 100755 --- a/framework/src/com/phonegap/api/Plugin.java +++ b/framework/src/com/phonegap/api/Plugin.java @@ -64,14 +64,18 @@ public abstract class Plugin implements IPlugin { /** * Called when the system is about to start resuming a previous activity. + * + * @param multitasking Flag indicating if multitasking is turned on for app */ - public void onPause() { + public void onPause(boolean multitasking) { } /** * Called when the activity will start interacting with the user. + * + * @param multitasking Flag indicating if multitasking is turned on for app */ - public void onResume() { + public void onResume(boolean multitasking) { } /** diff --git a/framework/src/com/phonegap/api/PluginManager.java b/framework/src/com/phonegap/api/PluginManager.java index 7dafa564..393c6a29 100755 --- a/framework/src/com/phonegap/api/PluginManager.java +++ b/framework/src/com/phonegap/api/PluginManager.java @@ -230,27 +230,31 @@ public final class PluginManager { /** * Called when the system is about to start resuming a previous activity. + * + * @param multitasking Flag indicating if multitasking is turned on for app */ - public void onPause() { + public void onPause(boolean multitasking) { java.util.Set> s = this.plugins.entrySet(); java.util.Iterator> it = s.iterator(); while(it.hasNext()) { Entry entry = it.next(); Plugin plugin = entry.getValue(); - plugin.onPause(); + plugin.onPause(multitasking); } } /** * Called when the activity will start interacting with the user. + * + * @param multitasking Flag indicating if multitasking is turned on for app */ - public void onResume() { + public void onResume(boolean multitasking) { java.util.Set> s = this.plugins.entrySet(); java.util.Iterator> it = s.iterator(); while(it.hasNext()) { Entry entry = it.next(); Plugin plugin = entry.getValue(); - plugin.onResume(); + plugin.onResume(multitasking); } } From ac2e92321fbfda36224d3d66f399011643438c92 Mon Sep 17 00:00:00 2001 From: macdonst Date: Sat, 25 Jun 2011 04:29:25 +0800 Subject: [PATCH 46/59] Issue #121: Problem with resolveLocalFileSystemURI if file name has spaces --- framework/src/com/phonegap/FileUtils.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/framework/src/com/phonegap/FileUtils.java b/framework/src/com/phonegap/FileUtils.java index a53884ef..08ff7cce 100755 --- a/framework/src/com/phonegap/FileUtils.java +++ b/framework/src/com/phonegap/FileUtils.java @@ -10,6 +10,7 @@ package com.phonegap; import java.io.*; import java.net.MalformedURLException; import java.net.URL; +import java.net.URLDecoder; import java.nio.channels.FileChannel; import org.apache.commons.codec.binary.Base64; @@ -229,15 +230,16 @@ public class FileUtils extends Plugin { * @throws JSONException */ private JSONObject resolveLocalFileSystemURI(String url) throws IOException, JSONException { + String decoded = URLDecoder.decode(url, "UTF-8"); // Test to see if this is a valid URL first - @SuppressWarnings("unused") - URL testUrl = new URL(url); - + @SuppressWarnings("unused") + URL testUrl = new URL(decoded); + File fp = null; - if (url.startsWith("file://")) { - fp = new File(url.substring(7, url.length())); + if (decoded.startsWith("file://")) { + fp = new File(decoded.substring(7, decoded.length())); } else { - fp = new File(url); + fp = new File(decoded); } if (!fp.exists()) { throw new FileNotFoundException(); From 8ef93ff0e514ead90d9b40fa739630c6e3886398 Mon Sep 17 00:00:00 2001 From: macdonst Date: Tue, 28 Jun 2011 00:30:53 +0800 Subject: [PATCH 47/59] Issue 123: Fixing problem where name object is not specified --- framework/src/com/phonegap/ContactAccessor.java | 10 ++++++---- framework/src/com/phonegap/ContactAccessorSdk5.java | 2 -- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/framework/src/com/phonegap/ContactAccessor.java b/framework/src/com/phonegap/ContactAccessor.java index 1cc49644..fdbb996f 100644 --- a/framework/src/com/phonegap/ContactAccessor.java +++ b/framework/src/com/phonegap/ContactAccessor.java @@ -178,11 +178,13 @@ public abstract class ContactAccessor { protected String getJsonString(JSONObject obj, String property) { String value = null; try { + if (obj != null) { value = obj.getString(property); - if (value.equals("null")) { - Log.d(LOG_TAG, property + " is string called 'null'"); - value = null; - } + if (value.equals("null")) { + Log.d(LOG_TAG, property + " is string called 'null'"); + value = null; + } + } } catch (JSONException e) { Log.d(LOG_TAG, "Could not get = " + e.getMessage()); diff --git a/framework/src/com/phonegap/ContactAccessorSdk5.java b/framework/src/com/phonegap/ContactAccessorSdk5.java index a1e7d428..e8b34ae8 100644 --- a/framework/src/com/phonegap/ContactAccessorSdk5.java +++ b/framework/src/com/phonegap/ContactAccessorSdk5.java @@ -1420,7 +1420,6 @@ public class ContactAccessorSdk5 extends ContactAccessor { .build()); } - // Add urls JSONArray websites = null; try { @@ -1473,7 +1472,6 @@ public class ContactAccessorSdk5 extends ContactAccessor { Log.e(LOG_TAG, e.getMessage(), e); retVal = false; } - return retVal; } From 8cb71673c28babeb5647a574879adb09efba8e01 Mon Sep 17 00:00:00 2001 From: Joe Bowser Date: Mon, 27 Jun 2011 10:22:57 -0700 Subject: [PATCH 48/59] Changing default target to the highest for maximum compatibility --- framework/default.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/default.properties b/framework/default.properties index c5d5335e..aa2fcdf7 100644 --- a/framework/default.properties +++ b/framework/default.properties @@ -10,5 +10,5 @@ # Indicates whether an apk should be generated for each density. split.density=false # Project target. -target=android-8 +target=android-12 apk-configurations= From c96c9b00b9bda3f810ab032d576e91a8a3c00017 Mon Sep 17 00:00:00 2001 From: Bryce Curtis Date: Mon, 27 Jun 2011 13:48:02 -0500 Subject: [PATCH 49/59] Revert to polling if there are any errors with callback server. This addresses various problems with proxies set by carriers. --- framework/assets/js/phonegap.js.base | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/framework/assets/js/phonegap.js.base b/framework/assets/js/phonegap.js.base index 4f26fd8d..d1daa555 100755 --- a/framework/assets/js/phonegap.js.base +++ b/framework/assets/js/phonegap.js.base @@ -805,13 +805,11 @@ PhoneGap.JSCallback = function() { console.log("JSCallback Error: Bad request. Stopping callbacks."); } - // If error, restart callback server + // If error, revert to polling else { console.log("JSCallback Error: Request failed."); - prompt("restartServer", "gap_callbackServer:"); - PhoneGap.JSCallbackPort = null; - PhoneGap.JSCallbackToken = null; - setTimeout(PhoneGap.JSCallback, 100); + PhoneGap.UsePolling = true; + PhoneGap.JSCallbackPolling(); } } }; From 85dab52cf75dedadedee5cba9f881cefeca7961c Mon Sep 17 00:00:00 2001 From: Bryce Curtis Date: Mon, 27 Jun 2011 13:49:24 -0500 Subject: [PATCH 50/59] Handle errors when adding a service. --- framework/assets/js/phonegap.js.base | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/framework/assets/js/phonegap.js.base b/framework/assets/js/phonegap.js.base index d1daa555..a1e8fe33 100755 --- a/framework/assets/js/phonegap.js.base +++ b/framework/assets/js/phonegap.js.base @@ -936,7 +936,11 @@ PhoneGap.includeJavascript = function(jsfile, successCallback) { */ var PluginManager = { addService: function(serviceType, className) { - navigator.app.addService(serviceType, className); + try { + navigator.app.addService(serviceType, className); + } catch (e) { + console.log("Error adding service "+serviceType+": "+e); + } } }; From f3d7ce8fc368609e73923ca17913f0129f3620cd Mon Sep 17 00:00:00 2001 From: Joe Bowser Date: Mon, 27 Jun 2011 16:01:43 -0700 Subject: [PATCH 51/59] VERSION: --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 03834411..85b7c695 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.9.5 \ No newline at end of file +0.9.6 From b5dc62d9efce3fd613100427209e9409e80cfc2a Mon Sep 17 00:00:00 2001 From: Fil Maj Date: Tue, 28 Jun 2011 15:20:13 -0700 Subject: [PATCH 52/59] fix to build script: gotta strip out new lines from read in version string --- lib/classic.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/classic.rb b/lib/classic.rb index ba6a47f6..4eeb7e53 100755 --- a/lib/classic.rb +++ b/lib/classic.rb @@ -88,7 +88,7 @@ class Classic # copies stuff from src directory into the android project directory (@path) def copy_libs - version = IO.read(File.join(@framework_dir, '../VERSION')) + version = IO.read(File.join(@framework_dir, '../VERSION')).lstrip.rstrip framework_res_dir = File.join(@framework_dir, "res") app_res_dir = File.join(@path, "res") # copies in the jar From fc1bea494783560b5ab6e3a2e3f955fa1fe6a1c0 Mon Sep 17 00:00:00 2001 From: Bryce Curtis Date: Wed, 29 Jun 2011 16:43:52 -0500 Subject: [PATCH 53/59] Update version to 0.9.6. --- framework/src/com/phonegap/Device.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/src/com/phonegap/Device.java b/framework/src/com/phonegap/Device.java index 035058fe..ce69dff1 100755 --- a/framework/src/com/phonegap/Device.java +++ b/framework/src/com/phonegap/Device.java @@ -18,7 +18,7 @@ import android.provider.Settings; public class Device extends Plugin { - public static String phonegapVersion = "0.9.5"; // PhoneGap version + public static String phonegapVersion = "0.9.6"; // PhoneGap version public static String platform = "Android"; // Device OS public static String uuid; // Device UUID From 1e3422ae7092a068efa1cadf9f1613cc256660c9 Mon Sep 17 00:00:00 2001 From: Bryce Curtis Date: Wed, 29 Jun 2011 18:23:20 -0500 Subject: [PATCH 54/59] Load new urls in new DroidGap activity - not same webview as initial url. --- framework/AndroidManifest.xml | 7 +- framework/src/com/phonegap/App.java | 35 +++++++--- framework/src/com/phonegap/DroidGap.java | 83 +++++++++++++++++++----- 3 files changed, 97 insertions(+), 28 deletions(-) diff --git a/framework/AndroidManifest.xml b/framework/AndroidManifest.xml index 53d7afb5..5b813bce 100644 --- a/framework/AndroidManifest.xml +++ b/framework/AndroidManifest.xml @@ -37,8 +37,11 @@ + + + + - - + diff --git a/framework/src/com/phonegap/App.java b/framework/src/com/phonegap/App.java index cb3342bc..13aaaee5 100755 --- a/framework/src/com/phonegap/App.java +++ b/framework/src/com/phonegap/App.java @@ -12,6 +12,7 @@ import org.json.JSONException; import org.json.JSONObject; import com.phonegap.api.Plugin; import com.phonegap.api.PluginResult; +import java.util.HashMap; /** * This class exposes methods in DroidGap that can be called from JavaScript. @@ -83,8 +84,11 @@ public class App extends Plugin { public void loadUrl(String url, JSONObject props) throws JSONException { System.out.println("App.loadUrl("+url+","+props+")"); int wait = 0; - + boolean usePhoneGap = true; + boolean clearPrev = false; + // If there are properties, then set them on the Activity + HashMap params = new HashMap(); if (props != null) { JSONArray keys = props.names(); for (int i=0; i 0) { - ((DroidGap)this.ctx).loadUrl(url, wait); - } - else { - ((DroidGap)this.ctx).loadUrl(url); + try { + synchronized(this) { + this.wait(wait); + } + } catch (InterruptedException e) { + e.printStackTrace(); + } } + ((DroidGap)this.ctx).showWebPage(url, usePhoneGap, clearPrev, params); } /** diff --git a/framework/src/com/phonegap/DroidGap.java b/framework/src/com/phonegap/DroidGap.java index b1323817..1245d7c5 100755 --- a/framework/src/com/phonegap/DroidGap.java +++ b/framework/src/com/phonegap/DroidGap.java @@ -7,6 +7,8 @@ */ package com.phonegap; +import java.util.HashMap; +import java.util.Map.Entry; import org.json.JSONArray; import org.json.JSONException; @@ -136,7 +138,7 @@ public class DroidGap extends PhonegapActivity { private String urlFile; // The base of the initial URL for our app - private String baseUrl; + private String baseUrl = null; // Plugin to call when activity result is received protected Plugin activityResultCallback = null; @@ -350,12 +352,14 @@ public class DroidGap extends PhonegapActivity { System.out.println("loadUrl("+url+")"); this.urlFile = this.getUrlFile(url); this.url = url; - int i = url.lastIndexOf('/'); - if (i > 0) { - this.baseUrl = url.substring(0, i); - } - else { - this.baseUrl = this.url; + if (this.baseUrl == null) { + int i = url.lastIndexOf('/'); + if (i > 0) { + this.baseUrl = url.substring(0, i+1); + } + else { + this.baseUrl = this.url + "/"; + } } System.out.println("url="+url+" baseUrl="+baseUrl); @@ -738,6 +742,56 @@ public class DroidGap extends PhonegapActivity { int p3 = (p1 < p2) ? p1 : p2; return url.substring(0, p3); } + + /** + * Display a new browser with the specified URL. + * + * NOTE: If usePhoneGap is set, only trusted PhoneGap URLs should be loaded, + * since any PhoneGap API can be called by the loaded HTML page. + * + * @param url The url to load. + * @param usePhoneGap Load url in PhoneGap webview. + * @return "" if ok, or error message. + */ + public void showWebPage(String url, boolean usePhoneGap, HashMap params) { + try { + Intent intent = null; + if (usePhoneGap) { + intent = new Intent().setClass(this, com.phonegap.DroidGap.class); + intent.putExtra("url", url); + + // Add parameters + if (params != null) { + java.util.Set> s = params.entrySet(); + java.util.Iterator> it = s.iterator(); + while(it.hasNext()) { + Entry entry = it.next(); + String key = entry.getKey(); + Object value = entry.getValue(); + if (value == null) { + } + else if (value.getClass().equals(String.class)) { + intent.putExtra(key, (String)value); + } + else if (value.getClass().equals(Boolean.class)) { + intent.putExtra(key, (Boolean)value); + } + else if (value.getClass().equals(Integer.class)) { + intent.putExtra(key, (Integer)value); + } + } + + } + } + else { + intent = new Intent(Intent.ACTION_VIEW); + intent.setData(Uri.parse(url)); + } + this.startActivity(intent); + } catch (android.content.ActivityNotFoundException e) { + System.out.println("DroidGap.showWebPage: Error loading url "+url+":"+ e.toString()); + } + } /** * Provides a hook for calling "alert" from javascript. Useful for @@ -826,7 +880,7 @@ public class DroidGap extends PhonegapActivity { @Override public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) { boolean reqOk = false; - if (this.ctx.urlFile.equals(this.ctx.getUrlFile(url))) { + if (url.indexOf(this.ctx.baseUrl) == 0) { reqOk = true; } @@ -1069,17 +1123,14 @@ public class DroidGap extends PhonegapActivity { // All else else { - int i = url.lastIndexOf('/'); - String newBaseUrl = url; - if (i > 0) { - newBaseUrl = url.substring(0, i); - } - // If our app or file:, then load into our webview // NOTE: This replaces our app with new URL. When BACK is pressed, // our app is reloaded and restarted. All state is lost. - if (this.ctx.loadInWebView || url.startsWith("file://") || this.ctx.baseUrl.equals(newBaseUrl)) { - this.ctx.appView.loadUrl(url); + if (this.ctx.loadInWebView || url.startsWith("file://") || url.indexOf(this.ctx.baseUrl) == 0) { + HashMap params = new HashMap(); + params.put("loadingDialog", ""); + params.put("hideLoadingDialogOnPageLoad", true); + this.ctx.showWebPage(url, true, params); } // If not our application, let default viewer handle From 96433145539d68c809be99fd219a4418f63c1d93 Mon Sep 17 00:00:00 2001 From: Bryce Curtis Date: Wed, 29 Jun 2011 18:25:49 -0500 Subject: [PATCH 55/59] Add more control over how url is loaded. --- framework/src/com/phonegap/DroidGap.java | 89 +++++++++++++----------- 1 file changed, 48 insertions(+), 41 deletions(-) diff --git a/framework/src/com/phonegap/DroidGap.java b/framework/src/com/phonegap/DroidGap.java index 1245d7c5..57421649 100755 --- a/framework/src/com/phonegap/DroidGap.java +++ b/framework/src/com/phonegap/DroidGap.java @@ -751,46 +751,49 @@ public class DroidGap extends PhonegapActivity { * * @param url The url to load. * @param usePhoneGap Load url in PhoneGap webview. - * @return "" if ok, or error message. + * @param clearPrev Clear the activity stack, so new app becomes top of stack + * @param params DroidGap parameters for new app + * @throws android.content.ActivityNotFoundException */ - public void showWebPage(String url, boolean usePhoneGap, HashMap params) { - try { - Intent intent = null; - if (usePhoneGap) { - intent = new Intent().setClass(this, com.phonegap.DroidGap.class); - intent.putExtra("url", url); + public void showWebPage(String url, boolean usePhoneGap, boolean clearPrev, HashMap params) throws android.content.ActivityNotFoundException { + Intent intent = null; + if (usePhoneGap) { + intent = new Intent().setClass(this, com.phonegap.DroidGap.class); + intent.putExtra("url", url); - // Add parameters - if (params != null) { - java.util.Set> s = params.entrySet(); - java.util.Iterator> it = s.iterator(); - while(it.hasNext()) { - Entry entry = it.next(); - String key = entry.getKey(); - Object value = entry.getValue(); - if (value == null) { - } - else if (value.getClass().equals(String.class)) { - intent.putExtra(key, (String)value); - } - else if (value.getClass().equals(Boolean.class)) { - intent.putExtra(key, (Boolean)value); - } - else if (value.getClass().equals(Integer.class)) { - intent.putExtra(key, (Integer)value); - } - } + // Add parameters + if (params != null) { + java.util.Set> s = params.entrySet(); + java.util.Iterator> it = s.iterator(); + while(it.hasNext()) { + Entry entry = it.next(); + String key = entry.getKey(); + Object value = entry.getValue(); + if (value == null) { + } + else if (value.getClass().equals(String.class)) { + intent.putExtra(key, (String)value); + } + else if (value.getClass().equals(Boolean.class)) { + intent.putExtra(key, (Boolean)value); + } + else if (value.getClass().equals(Integer.class)) { + intent.putExtra(key, (Integer)value); + } + } - } - } - else { - intent = new Intent(Intent.ACTION_VIEW); - intent.setData(Uri.parse(url)); - } - this.startActivity(intent); - } catch (android.content.ActivityNotFoundException e) { - System.out.println("DroidGap.showWebPage: Error loading url "+url+":"+ e.toString()); - } + } + } + else { + intent = new Intent(Intent.ACTION_VIEW); + intent.setData(Uri.parse(url)); + } + this.startActivity(intent); + + // Finish current activity + if (clearPrev) { + this.finish(); + } } /** @@ -1127,10 +1130,14 @@ public class DroidGap extends PhonegapActivity { // NOTE: This replaces our app with new URL. When BACK is pressed, // our app is reloaded and restarted. All state is lost. if (this.ctx.loadInWebView || url.startsWith("file://") || url.indexOf(this.ctx.baseUrl) == 0) { - HashMap params = new HashMap(); - params.put("loadingDialog", ""); - params.put("hideLoadingDialogOnPageLoad", true); - this.ctx.showWebPage(url, true, params); + try { + HashMap params = new HashMap(); + params.put("loadingDialog", ""); + params.put("hideLoadingDialogOnPageLoad", true); + this.ctx.showWebPage(url, true, true, params); + } catch (android.content.ActivityNotFoundException e) { + System.out.println("Error loading url into DroidGap - "+url+":"+ e.toString()); + } } // If not our application, let default viewer handle From 76b2df208e2755b7336e15026435f4a1739577c5 Mon Sep 17 00:00:00 2001 From: Bryce Curtis Date: Wed, 29 Jun 2011 18:28:29 -0500 Subject: [PATCH 56/59] Add comment --- framework/src/com/phonegap/DroidGap.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/framework/src/com/phonegap/DroidGap.java b/framework/src/com/phonegap/DroidGap.java index 57421649..ec839159 100755 --- a/framework/src/com/phonegap/DroidGap.java +++ b/framework/src/com/phonegap/DroidGap.java @@ -882,6 +882,9 @@ public class DroidGap extends PhonegapActivity { */ @Override public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) { + + // Security check to make sure any requests are coming from the page initially + // loaded in webview and not another loaded in an iframe. boolean reqOk = false; if (url.indexOf(this.ctx.baseUrl) == 0) { reqOk = true; From b8cc36e8059fa8e7d141f5b56176d6b052abcaa0 Mon Sep 17 00:00:00 2001 From: Bryce Curtis Date: Thu, 30 Jun 2011 10:42:27 -0500 Subject: [PATCH 57/59] Don't clear activity stack by default. --- framework/src/com/phonegap/DroidGap.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/src/com/phonegap/DroidGap.java b/framework/src/com/phonegap/DroidGap.java index ec839159..3830722e 100755 --- a/framework/src/com/phonegap/DroidGap.java +++ b/framework/src/com/phonegap/DroidGap.java @@ -1137,7 +1137,7 @@ public class DroidGap extends PhonegapActivity { HashMap params = new HashMap(); params.put("loadingDialog", ""); params.put("hideLoadingDialogOnPageLoad", true); - this.ctx.showWebPage(url, true, true, params); + this.ctx.showWebPage(url, true, false, params); } catch (android.content.ActivityNotFoundException e) { System.out.println("Error loading url into DroidGap - "+url+":"+ e.toString()); } From b7156c6803550140657bfd3f1d2383724eaab4a8 Mon Sep 17 00:00:00 2001 From: Bryce Curtis Date: Thu, 30 Jun 2011 10:53:23 -0500 Subject: [PATCH 58/59] Bump up version to 0.9.6 for example. --- example/index.html | 2 +- framework/assets/www/index.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/example/index.html b/example/index.html index a32ae9d5..59f0da52 100644 --- a/example/index.html +++ b/example/index.html @@ -5,7 +5,7 @@ PhoneGap - + diff --git a/framework/assets/www/index.html b/framework/assets/www/index.html index ff45efc3..69cd9963 100644 --- a/framework/assets/www/index.html +++ b/framework/assets/www/index.html @@ -1,7 +1,7 @@ - + From c98b758e94338e17e9a96022a8e9be0be845a281 Mon Sep 17 00:00:00 2001 From: Bryce Curtis Date: Thu, 30 Jun 2011 13:15:30 -0500 Subject: [PATCH 59/59] Update version to 0.9.6.1 --- VERSION | 2 +- example/index.html | 8 ++++---- framework/assets/www/index.html | 2 +- framework/src/com/phonegap/Device.java | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/VERSION b/VERSION index 85b7c695..c6989407 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.9.6 +0.9.6.1 diff --git a/example/index.html b/example/index.html index 59f0da52..5981abb4 100644 --- a/example/index.html +++ b/example/index.html @@ -5,8 +5,8 @@ PhoneGap - - + + @@ -31,8 +31,8 @@ Get a Picture Get Phone's Contacts Check Network - diff --git a/framework/assets/www/index.html b/framework/assets/www/index.html index 69cd9963..fd6e0de2 100644 --- a/framework/assets/www/index.html +++ b/framework/assets/www/index.html @@ -1,7 +1,7 @@ - + diff --git a/framework/src/com/phonegap/Device.java b/framework/src/com/phonegap/Device.java index ce69dff1..d86b2d06 100755 --- a/framework/src/com/phonegap/Device.java +++ b/framework/src/com/phonegap/Device.java @@ -18,7 +18,7 @@ import android.provider.Settings; public class Device extends Plugin { - public static String phonegapVersion = "0.9.6"; // PhoneGap version + public static String phonegapVersion = "0.9.6.1"; // PhoneGap version public static String platform = "Android"; // Device OS public static String uuid; // Device UUID