diff --git a/.gitignore b/.gitignore index 9f90a6b1..e73b34b3 100644 --- a/.gitignore +++ b/.gitignore @@ -4,28 +4,29 @@ gen assets/www/cordova.js framework/assets/www/.tmp* local.properties -framework/proguard.cfg -framework/cordova.jar -framework/cordova-*.jar -framework/phonegap-*.jar +framework/lib +proguard.cfg +proguard.cfg +proguard-project.txt framework/bin framework/test/org/apache/cordova/*.class framework/assets/www/.DS_Store framework/assets/www/cordova-*.js framework/assets/www/phonegap-*.js framework/libs +test/libs example ./test test/bin test/assets/www/.tmp* tmp/** -*.tmp -test/libs/*.jar bin/node_modules .metadata -*.bak tmp/**/* -*.swp Thumbs.db Desktop.ini - +*.tmp +*.bak +*.swp +*.class +*.jar diff --git a/VERSION b/VERSION index ccbccc3d..d2a7ed1f 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.2.0 +2.3.0rc1 diff --git a/bin/create b/bin/create index 1bce739e..e01cef98 100755 --- a/bin/create +++ b/bin/create @@ -147,13 +147,13 @@ replace "s/__ACTIVITY__/${ACTIVITY}/g" "$MANIFEST_PATH" replace "s/__PACKAGE__/${PACKAGE}/g" "$MANIFEST_PATH" replace "s/__APILEVEL__/${API_LEVEL}/g" "$MANIFEST_PATH" -# creating cordova folder and copying emulate/debug/log/launch scripts +# creating cordova folder and copying run/build/log/launch scripts mkdir "$PROJECT_PATH"/cordova createAppInfoJar cp "$BUILD_PATH"/bin/templates/cordova/appinfo.jar "$PROJECT_PATH"/cordova/appinfo.jar cp "$BUILD_PATH"/bin/templates/cordova/cordova "$PROJECT_PATH"/cordova/cordova -cp "$BUILD_PATH"/bin/templates/cordova/debug "$PROJECT_PATH"/cordova/debug +cp "$BUILD_PATH"/bin/templates/cordova/build "$PROJECT_PATH"/cordova/build +cp "$BUILD_PATH"/bin/templates/cordova/release "$PROJECT_PATH"/cordova/release cp "$BUILD_PATH"/bin/templates/cordova/clean "$PROJECT_PATH"/cordova/clean cp "$BUILD_PATH"/bin/templates/cordova/log "$PROJECT_PATH"/cordova/log -cp "$BUILD_PATH"/bin/templates/cordova/emulate "$PROJECT_PATH"/cordova/emulate -cp "$BUILD_PATH"/bin/templates/cordova/BOOM "$PROJECT_PATH"/cordova/BOOM +cp "$BUILD_PATH"/bin/templates/cordova/run "$PROJECT_PATH"/cordova/run diff --git a/bin/create.js b/bin/create.js index 2d6b8d61..258c32cd 100644 --- a/bin/create.js +++ b/bin/create.js @@ -198,10 +198,9 @@ exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\cordova\\appinfo.jar ' + PRO exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\cordova\\cordova.js ' + PROJECT_PATH + '\\cordova\\cordova.js /Y'); exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\cordova\\cordova.bat ' + PROJECT_PATH + '\\cordova\\cordova.bat /Y'); exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\cordova\\clean.bat ' + PROJECT_PATH + '\\cordova\\clean.bat /Y'); -exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\cordova\\debug.bat ' + PROJECT_PATH + '\\cordova\\debug.bat /Y'); +exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\cordova\\build.bat ' + PROJECT_PATH + '\\cordova\\build.bat /Y'); exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\cordova\\log.bat ' + PROJECT_PATH + '\\cordova\\log.bat /Y'); -exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\cordova\\emulate.bat ' + PROJECT_PATH + '\\cordova\\emulate.bat /Y'); -exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\cordova\\BOOM.bat ' + PROJECT_PATH + '\\cordova\\BOOM.bat /Y'); +exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\cordova\\run.bat ' + PROJECT_PATH + '\\cordova\\run.bat /Y'); // interpolate the activity name and package WScript.Echo("Updating AndroidManifest.xml and Main Activity..."); diff --git a/bin/templates/cordova/debug b/bin/templates/cordova/build similarity index 96% rename from bin/templates/cordova/debug rename to bin/templates/cordova/build index aaa59780..e586e4d6 100755 --- a/bin/templates/cordova/debug +++ b/bin/templates/cordova/build @@ -21,4 +21,4 @@ set -e CORDOVA_PATH=$( cd "$( dirname "$0" )" && pwd ) -bash "$CORDOVA_PATH"/cordova debug +bash "$CORDOVA_PATH"/cordova build diff --git a/bin/templates/cordova/BOOM.bat b/bin/templates/cordova/build.bat similarity index 97% rename from bin/templates/cordova/BOOM.bat rename to bin/templates/cordova/build.bat index 7b83f2ac..8e6ca9ad 100644 --- a/bin/templates/cordova/BOOM.bat +++ b/bin/templates/cordova/build.bat @@ -15,4 +15,4 @@ :: specific language governing permissions and limitations :: under the License. -%~dp0\cordova.bat BOOM +%~dp0\cordova.bat build diff --git a/bin/templates/cordova/cordova b/bin/templates/cordova/cordova index 1a1a4870..1945a4c4 100755 --- a/bin/templates/cordova/cordova +++ b/bin/templates/cordova/cordova @@ -17,12 +17,12 @@ #!/bin/bash -set -e PROJECT_PATH=$( cd "$( dirname "$0" )/.." && pwd ) function check_devices { - local devices=`adb devices | awk '/List of devices attached/ { while(getline > 0) { print }}'` +# FIXME + local devices=`adb devices | awk '/List of devices attached/ { while(getline > 0) { print }}' | grep device` if [ -z "$devices" ] ; then echo "1" else @@ -37,7 +37,6 @@ function emulate { # Do not launch an emulator if there is already one running or if a device is attached if [ $(check_devices) == 0 ] ; then - echo "Device attached or emulator already running" return fi @@ -62,8 +61,9 @@ function emulate { do echo "$i) ${avd_list[$i]}" done - echo -n "> " - read avd_id + read -t 5 -p "> " avd_id + # default value if input timeout + if [ $avd_id -eq 1000 ] ; then avd_id=0 ; fi done emulator -cpu-delay 0 -no-boot-anim -cache /tmp/cache -avd ${avd_list[$avd_id]} 1> /dev/null 2>&1 & fi @@ -78,14 +78,75 @@ function log { adb logcat } -function debug { - if [ $(check_devices) == 0 ] ; then - ant debug install +function run { + clean && emulate && wait_for_device && install && launch +} + +function install { + + declare -a devices=($(adb devices | awk '/List of devices attached/ { while(getline > 0) { print }}' | grep device | cut -f 1)) + local device_id="1000" #FIXME: hopefully user does not have 1000 AVDs + + if [ ${#devices[@]} == 0 ] + then + # should not reach here. Emulator should launch or device should be attached + echo "Emulator not running or device not attached. Could not install debug package" + exit 70 + fi + + if [ ${#devices[@]} == 1 ] + then + export ANDROID_SERIAL=${devices[0]} + # User has more than 1 AVD + elif [ ${#devices[@]} -gt 1 ] + then + while [ -z ${devices[$device_id]} ] + do + echo "Choose from one of the following devices/emulators [0 to $((${#devices[@]}-1))]:" + for(( i = 0 ; i < ${#devices[@]} ; i++ )) + do + echo "$i) ${devices[$i]}" + done + read -t 5 -p "> " device_id + # default value if input timeout + if [ $device_id -eq 1000 ] ; then device_id=0 ; fi + done + export ANDROID_SERIAL=${devices[$device_id]} + fi + + ant debug install +} + +function build { + ant debug +} + +function release { + ant release +} + +function wait_for_device { + local i="0" + echo -n "Waiting for device..." + + while [ $i -lt 300 ] + do + if [ $(check_devices) -eq 0 ] + then + break + else + sleep 1 + i=$[i+1] + echo -n "." + fi + done + # Device timeout: emulator has not started in time or device not attached + if [ $i -eq 300 ] + then + echo "device timeout!" + exit 69 else - ant debug - echo "##################################################################" - echo "# Plug in your device or launch an emulator with cordova/emulate #" - echo "##################################################################" + echo "connected!" fi } @@ -94,9 +155,5 @@ function launch { adb shell am start -n $launch_str } -function BOOM { - clean && debug && launch -} - # TODO parse arguments (cd "$PROJECT_PATH" && $1) diff --git a/bin/templates/cordova/cordova.js b/bin/templates/cordova/cordova.js index ff91430b..51533cb9 100644 --- a/bin/templates/cordova/cordova.js +++ b/bin/templates/cordova/cordova.js @@ -34,17 +34,19 @@ function exec(command) { return output; } -function emulator_running() { +function device_running() { var local_devices = shell.Exec("%comspec% /c adb devices").StdOut.ReadAll(); - if(local_devices.match(/emulator/)) { + if(local_devices.match(/\w+\tdevice/)) { + WScript.Echo("Yes"); return true; } + WScript.Echo("No"); return false; } function emulate() { // don't run emulator if a device is plugged in or if emulator is already running - if(emulator_running()) { - WScript.Echo("Device or Emulator already running!"); + if(device_running()) { + //WScript.Echo("Device or Emulator already running!"); return; } var oExec = shell.Exec("%comspec% /c android.bat list avd"); @@ -84,18 +86,18 @@ function emulate() { } function clean() { + WScript.Echo("Cleaning project..."); exec("%comspec% /c ant.bat clean -f "+ROOT+"\\build.xml 2>&1"); } -function debug() { - if(emulator_running()) { - exec("%comspec% /c ant.bat debug install -f "+ROOT+"\\build.xml 2>&1"); - } else { - exec("%comspec% /c ant.bat debug -f "+ROOT+"\\build.xml 2>&1"); - WScript.Echo("##################################################################"); - WScript.Echo("# Plug in your device or launch an emulator with cordova/emulate #"); - WScript.Echo("##################################################################"); - } +function build() { + WScript.Echo("Building project..."); + exec("%comspec% /c ant.bat debug -f "+ROOT+"\\build.xml 2>&1"); +} + +function install() { + WScript.Echo("Building/Installing project..."); + exec("%comspec% /c ant.bat debug install -f "+ROOT+"\\build.xml 2>&1"); } function log() { @@ -103,14 +105,28 @@ function log() { } function launch() { + WScript.Echo("Launching app..."); var launch_str=exec("%comspec% /c java -jar "+ROOT+"\\cordova\\appinfo.jar "+ROOT+"\\AndroidManifest.xml"); //WScript.Echo(launch_str); exec("%comspec% /c adb shell am start -n "+launch_str+" 2>&1"); } -function BOOM() { +function run() { + var i=0; clean(); - debug(); + emulate(); + WScript.Stdout.Write('Waiting for device...'); + while(!device_running() && i < 300) { + WScript.Stdout.Write('.'); + WScript.sleep(1000); + i += 1; + } + if(i == 300) { + WScript.Stderr.WriteLine("device/emulator timeout!"); + } else { + WScript.Stdout.WriteLine("connected!"); + } + install(); launch(); } var args = WScript.Arguments; diff --git a/bin/templates/cordova/debug.bat b/bin/templates/cordova/debug.bat deleted file mode 100644 index f980eb72..00000000 --- a/bin/templates/cordova/debug.bat +++ /dev/null @@ -1,18 +0,0 @@ -:: 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. - -%~dp0\cordova.bat debug diff --git a/bin/templates/cordova/emulate.bat b/bin/templates/cordova/emulate.bat deleted file mode 100644 index 87ef969f..00000000 --- a/bin/templates/cordova/emulate.bat +++ /dev/null @@ -1 +0,0 @@ -%~dp0\cordova.bat emulate diff --git a/bin/templates/cordova/emulate b/bin/templates/cordova/release similarity index 95% rename from bin/templates/cordova/emulate rename to bin/templates/cordova/release index fe27b2f3..73d873e3 100755 --- a/bin/templates/cordova/emulate +++ b/bin/templates/cordova/release @@ -21,4 +21,4 @@ set -e CORDOVA_PATH=$( cd "$( dirname "$0" )" && pwd ) -bash "$CORDOVA_PATH"/cordova emulate +bash "$CORDOVA_PATH"/cordova release diff --git a/bin/templates/cordova/BOOM b/bin/templates/cordova/run similarity index 96% rename from bin/templates/cordova/BOOM rename to bin/templates/cordova/run index 443502d8..840a8d5a 100755 --- a/bin/templates/cordova/BOOM +++ b/bin/templates/cordova/run @@ -21,4 +21,4 @@ set -e CORDOVA_PATH=$( cd "$( dirname "$0" )" && pwd ) -bash $CORDOVA_PATH/cordova BOOM +bash "$CORDOVA_PATH"/cordova run diff --git a/bin/templates/cordova/run.bat b/bin/templates/cordova/run.bat new file mode 100644 index 00000000..7c470ed8 --- /dev/null +++ b/bin/templates/cordova/run.bat @@ -0,0 +1 @@ +%~dp0\cordova.bat run diff --git a/bin/templates/project/assets/www/index.html b/bin/templates/project/assets/www/index.html index 4f8dc5f2..0f8a50bc 100644 --- a/bin/templates/project/assets/www/index.html +++ b/bin/templates/project/assets/www/index.html @@ -33,7 +33,7 @@

Device is Ready

- + + diff --git a/framework/project.properties b/framework/project.properties index 4383e57e..d556741d 100644 --- a/framework/project.properties +++ b/framework/project.properties @@ -10,7 +10,7 @@ # Indicates whether an apk should be generated for each density. split.density=false # Project target. -target=Google Inc.:Google APIs:16 +target=Google Inc.:Google APIs:17 apk-configurations= renderscript.opt.level=O0 android.library=true diff --git a/framework/res/xml/config.xml b/framework/res/xml/config.xml index da9c1f46..b6900078 100644 --- a/framework/res/xml/config.xml +++ b/framework/res/xml/config.xml @@ -51,6 +51,7 @@ + diff --git a/framework/src/org/apache/cordova/App.java b/framework/src/org/apache/cordova/App.java index 7bdf1166..dad34a07 100755 --- a/framework/src/org/apache/cordova/App.java +++ b/framework/src/org/apache/cordova/App.java @@ -19,28 +19,30 @@ package org.apache.cordova; +import org.apache.cordova.api.CallbackContext; +import org.apache.cordova.api.CordovaPlugin; import org.apache.cordova.api.LOG; -import org.apache.cordova.api.Plugin; import org.apache.cordova.api.PluginResult; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; + import java.util.HashMap; /** * This class exposes methods in DroidGap that can be called from JavaScript. */ -public class App extends Plugin { +public class App extends CordovaPlugin { /** * Executes the request and returns PluginResult. * - * @param action The action to execute. - * @param args JSONArry of arguments for the plugin. - * @param callbackId The callback id used when calling back into JavaScript. - * @return A PluginResult object with a status and message. + * @param action The action to execute. + * @param args JSONArry of arguments for the plugin. + * @param callbackContext The callback context from which we were invoked. + * @return A PluginResult object with a status and message. */ - public PluginResult execute(String action, JSONArray args, String callbackId) { + public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException { PluginResult.Status status = PluginResult.Status.OK; String result = ""; @@ -79,9 +81,11 @@ public class App extends Plugin { else if (action.equals("exitApp")) { this.exitApp(); } - return new PluginResult(status, result); + callbackContext.sendPluginResult(new PluginResult(status, result)); + return true; } catch (JSONException e) { - return new PluginResult(PluginResult.Status.JSON_EXCEPTION); + callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION)); + return false; } } diff --git a/framework/src/org/apache/cordova/CameraLauncher.java b/framework/src/org/apache/cordova/CameraLauncher.java index f9cfb94a..49a68c03 100755 --- a/framework/src/org/apache/cordova/CameraLauncher.java +++ b/framework/src/org/apache/cordova/CameraLauncher.java @@ -289,6 +289,17 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect // If sending base64 image back if (destType == DATA_URL) { bitmap = getScaledBitmap(FileUtils.stripFileProtocol(imageUri.toString())); + if (bitmap == null) { + // Try to get the bitmap from intent. + bitmap = (Bitmap)intent.getExtras().get("data"); + } + + // Double-check the bitmap. + if (bitmap == null) { + Log.d(LOG_TAG, "I either have a null image path or bitmap"); + this.failPicture("Unable to create bitmap!"); + return; + } if (rotate != 0 && this.correctOrientation) { bitmap = getRotatedBitmap(rotate, bitmap, exif); @@ -567,6 +578,9 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect options.inJustDecodeBounds = false; options.inSampleSize = calculateSampleSize(options.outWidth, options.outHeight, this.targetWidth, this.targetHeight); Bitmap unscaledBitmap = BitmapFactory.decodeFile(imagePath, options); + if (unscaledBitmap == null) { + return null; + } return Bitmap.createScaledBitmap(unscaledBitmap, widthHeight[0], widthHeight[1], true); } diff --git a/framework/src/org/apache/cordova/Capture.java b/framework/src/org/apache/cordova/Capture.java index 4cf46dd5..3eecf379 100644 --- a/framework/src/org/apache/cordova/Capture.java +++ b/framework/src/org/apache/cordova/Capture.java @@ -127,7 +127,7 @@ public class Capture extends CordovaPlugin { // If the mimeType isn't set the rest will fail // so let's see if we can determine it. - if (mimeType == null || mimeType.equals("")) { + if (mimeType == null || mimeType.equals("") || "null".equals(mimeType)) { mimeType = FileUtils.getMimeType(filePath); } Log.d(LOG_TAG, "Mime type = " + mimeType); diff --git a/framework/src/org/apache/cordova/ContactAccessorSdk5.java b/framework/src/org/apache/cordova/ContactAccessorSdk5.java index 8497135e..73d05bc0 100644 --- a/framework/src/org/apache/cordova/ContactAccessorSdk5.java +++ b/framework/src/org/apache/cordova/ContactAccessorSdk5.java @@ -1579,7 +1579,7 @@ public class ContactAccessorSdk5 extends ContactAccessor { Uri uri = Uri.parse(path); return mApp.getActivity().getContentResolver().openInputStream(uri); } - if (path.startsWith("http:") || path.startsWith("file:")) { + if (path.startsWith("http:") || path.startsWith("https:") || path.startsWith("file:")) { URL url = new URL(path); return url.openStream(); } diff --git a/framework/src/org/apache/cordova/CordovaWebView.java b/framework/src/org/apache/cordova/CordovaWebView.java index a2b65663..336cf521 100755 --- a/framework/src/org/apache/cordova/CordovaWebView.java +++ b/framework/src/org/apache/cordova/CordovaWebView.java @@ -20,6 +20,8 @@ package org.apache.cordova; import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; @@ -98,8 +100,8 @@ public class CordovaWebView extends WebView { private long lastMenuEventTime = 0; - NativeToJsMessageQueue jsMessageQueue; - ExposedJsApi exposedJsApi; + NativeToJsMessageQueue jsMessageQueue; + ExposedJsApi exposedJsApi; /** custom view created by the browser (a video player for example) */ private View mCustomView; @@ -200,8 +202,8 @@ public class CordovaWebView extends WebView { * @param defStyle * @param privateBrowsing */ - @TargetApi(11) - public CordovaWebView(Context context, AttributeSet attrs, int defStyle, boolean privateBrowsing) { + @TargetApi(11) + public CordovaWebView(Context context, AttributeSet attrs, int defStyle, boolean privateBrowsing) { super(context, attrs, defStyle, privateBrowsing); if (CordovaInterface.class.isInstance(context)) { @@ -244,15 +246,28 @@ public class CordovaWebView extends WebView { settings.setJavaScriptEnabled(true); settings.setJavaScriptCanOpenWindowsAutomatically(true); settings.setLayoutAlgorithm(LayoutAlgorithm.NORMAL); - - //Set the nav dump for HTC 2.x devices (disabling for ICS/Jellybean) - if(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.HONEYCOMB) - settings.setNavDump(true); + // Set the nav dump for HTC 2.x devices (disabling for ICS, deprecated entirely for Jellybean 4.2) + try { + Method gingerbread_getMethod = WebSettings.class.getMethod("setNavDump", new Class[] { boolean.class }); + if(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.HONEYCOMB) + { + gingerbread_getMethod.invoke(settings, true); + } + } catch (NoSuchMethodException e) { + Log.d(TAG, "We are on a modern version of Android, we will deprecate HTC 2.3 devices in 2.8"); + } catch (IllegalArgumentException e) { + Log.d(TAG, "Doing the NavDump failed with bad arguments"); + } catch (IllegalAccessException e) { + Log.d(TAG, "This should never happen: IllegalAccessException means this isn't Android anymore"); + } catch (InvocationTargetException e) { + Log.d(TAG, "This should never happen: InvocationTargetException means this isn't Android anymore."); + } + // Jellybean rightfully tried to lock this down. Too bad they didn't give us a whitelist // while we do this if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) - Level16Apis.enableUniversalAccess(settings); + Level16Apis.enableUniversalAccess(settings); // Enable database settings.setDatabaseEnabled(true); String databasePath = this.cordova.getActivity().getApplicationContext().getDir("database", Context.MODE_PRIVATE).getPath(); @@ -288,17 +303,19 @@ public class CordovaWebView extends WebView { } private void updateUserAgentString() { - this.getSettings().getUserAgentString(); + this.getSettings().getUserAgentString(); } private void exposeJsInterface() { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.GINGERBREAD) { + int SDK_INT = Build.VERSION.SDK_INT; + boolean isHoneycomb = (SDK_INT >= Build.VERSION_CODES.HONEYCOMB && SDK_INT <= Build.VERSION_CODES.HONEYCOMB_MR2); + if (isHoneycomb || (SDK_INT < Build.VERSION_CODES.GINGERBREAD)) { Log.i(TAG, "Disabled addJavascriptInterface() bridge since Android version is old."); // Bug being that Java Strings do not get converted to JS strings automatically. // This isn't hard to work-around on the JS side, but it's easier to just // use the prompt bridge instead. return; - } else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB && Build.MANUFACTURER.equals("unknown")) { + } else if (SDK_INT < Build.VERSION_CODES.HONEYCOMB && Build.MANUFACTURER.equals("unknown")) { // addJavascriptInterface crashes on the 2.3 emulator. Log.i(TAG, "Disabled addJavascriptInterface() bridge callback due to a bug on the 2.3 emulator"); return; @@ -610,7 +627,7 @@ public class CordovaWebView extends WebView { // Check webview first to see if there is a history // This is needed to support curPage#diffLink, since they are added to appView's history, but not our history url array (JQMobile behavior) if (super.canGoBack()) { - printBackForwardList(); + printBackForwardList(); super.goBack(); return true; @@ -841,27 +858,27 @@ public class CordovaWebView extends WebView { // If back key if (keyCode == KeyEvent.KEYCODE_BACK) { // A custom view is currently displayed (e.g. playing a video) - if(mCustomView != null) { - this.hideCustomView(); - } else { - // The webview is currently displayed - // If back key is bound, then send event to JavaScript - if (this.bound) { - this.loadUrl("javascript:cordova.fireDocumentEvent('backbutton');"); - return true; - } else { - // If not bound - // Go to previous page in webview if it is possible to go back - if (this.backHistory()) { - return true; - } - // If not, then invoke default behaviour - else { - //this.activityState = ACTIVITY_EXITING; - return false; - } - } - } + if(mCustomView != null) { + this.hideCustomView(); + } else { + // The webview is currently displayed + // If back key is bound, then send event to JavaScript + if (this.bound) { + this.loadUrl("javascript:cordova.fireDocumentEvent('backbutton');"); + return true; + } else { + // If not bound + // Go to previous page in webview if it is possible to go back + if (this.backHistory()) { + return true; + } + // If not, then invoke default behaviour + else { + //this.activityState = ACTIVITY_EXITING; + return false; + } + } + } } // Legacy else if (keyCode == KeyEvent.KEYCODE_MENU) { @@ -1003,14 +1020,14 @@ public class CordovaWebView extends WebView { } public void printBackForwardList() { - WebBackForwardList currentList = this.copyBackForwardList(); - int currentSize = currentList.getSize(); - for(int i = 0; i < currentSize; ++i) - { - WebHistoryItem item = currentList.getItemAtIndex(i); - String url = item.getUrl(); - LOG.d(TAG, "The URL at index: " + Integer.toString(i) + "is " + url ); - } + WebBackForwardList currentList = this.copyBackForwardList(); + int currentSize = currentList.getSize(); + for(int i = 0; i < currentSize; ++i) + { + WebHistoryItem item = currentList.getItemAtIndex(i); + String url = item.getUrl(); + LOG.d(TAG, "The URL at index: " + Integer.toString(i) + "is " + url ); + } } @@ -1027,8 +1044,8 @@ public class CordovaWebView extends WebView { } public void showCustomView(View view, WebChromeClient.CustomViewCallback callback) { - // This code is adapted from the original Android Browser code, licensed under the Apache License, Version 2.0 - Log.d(TAG, "showing Custom View"); + // This code is adapted from the original Android Browser code, licensed under the Apache License, Version 2.0 + Log.d(TAG, "showing Custom View"); // if a view already exists then immediately terminate the new one if (mCustomView != null) { callback.onCustomViewHidden(); @@ -1036,57 +1053,57 @@ public class CordovaWebView extends WebView { } // Store the view and its callback for later (to kill it properly) - mCustomView = view; - mCustomViewCallback = callback; - + mCustomView = view; + mCustomViewCallback = callback; + // Add the custom view to its container. - ViewGroup parent = (ViewGroup) this.getParent(); - parent.addView(view, COVER_SCREEN_GRAVITY_CENTER); - - // Hide the content view. - this.setVisibility(View.GONE); - - // Finally show the custom view container. - parent.setVisibility(View.VISIBLE); - parent.bringToFront(); + ViewGroup parent = (ViewGroup) this.getParent(); + parent.addView(view, COVER_SCREEN_GRAVITY_CENTER); + + // Hide the content view. + this.setVisibility(View.GONE); + + // Finally show the custom view container. + parent.setVisibility(View.VISIBLE); + parent.bringToFront(); } - public void hideCustomView() { - // This code is adapted from the original Android Browser code, licensed under the Apache License, Version 2.0 - Log.d(TAG, "Hidding Custom View"); - if (mCustomView == null) return; + public void hideCustomView() { + // This code is adapted from the original Android Browser code, licensed under the Apache License, Version 2.0 + Log.d(TAG, "Hidding Custom View"); + if (mCustomView == null) return; - // Hide the custom view. - mCustomView.setVisibility(View.GONE); - - // Remove the custom view from its container. - ViewGroup parent = (ViewGroup) this.getParent(); - parent.removeView(mCustomView); - mCustomView = null; - mCustomViewCallback.onCustomViewHidden(); - + // Hide the custom view. + mCustomView.setVisibility(View.GONE); + + // Remove the custom view from its container. + ViewGroup parent = (ViewGroup) this.getParent(); + parent.removeView(mCustomView); + mCustomView = null; + mCustomViewCallback.onCustomViewHidden(); + // Show the content view. this.setVisibility(View.VISIBLE); - } - - /** - * if the video overlay is showing then we need to know - * as it effects back button handling - * - * @return - */ - public boolean isCustomViewShowing() { - return mCustomView != null; - } - - public WebBackForwardList restoreState(Bundle savedInstanceState) - { - WebBackForwardList myList = super.restoreState(savedInstanceState); - Log.d(TAG, "WebView restoration crew now restoring!"); - //Initialize the plugin manager once more - this.pluginManager.init(); - return myList; - } + } + + /** + * if the video overlay is showing then we need to know + * as it effects back button handling + * + * @return + */ + public boolean isCustomViewShowing() { + return mCustomView != null; + } + + public WebBackForwardList restoreState(Bundle savedInstanceState) + { + WebBackForwardList myList = super.restoreState(savedInstanceState); + Log.d(TAG, "WebView restoration crew now restoring!"); + //Initialize the plugin manager once more + this.pluginManager.init(); + return myList; + } public void storeResult(int requestCode, int resultCode, Intent intent) { mResult = new ActivityResult(requestCode, resultCode, intent); diff --git a/framework/src/org/apache/cordova/Device.java b/framework/src/org/apache/cordova/Device.java index 6a164740..8967bf47 100644 --- a/framework/src/org/apache/cordova/Device.java +++ b/framework/src/org/apache/cordova/Device.java @@ -39,7 +39,7 @@ import android.telephony.TelephonyManager; public class Device extends CordovaPlugin { public static final String TAG = "Device"; - public static String cordovaVersion = "2.2.0"; // Cordova version + public static String cordovaVersion = "2.3.0rc1"; // Cordova version public static String platform = "Android"; // Device OS public static String uuid; // Device UUID @@ -80,9 +80,7 @@ public class Device extends CordovaPlugin { r.put("platform", Device.platform); r.put("name", this.getProductName()); r.put("cordova", Device.cordovaVersion); - //JSONObject pg = new JSONObject(); - //pg.put("version", Device.CordovaVersion); - //r.put("cordova", pg); + r.put("model", this.getProductName()); callbackContext.success(r); } else { diff --git a/framework/src/org/apache/cordova/DroidGap.java b/framework/src/org/apache/cordova/DroidGap.java index 51c9c595..3288e726 100755 --- a/framework/src/org/apache/cordova/DroidGap.java +++ b/framework/src/org/apache/cordova/DroidGap.java @@ -40,7 +40,6 @@ import android.graphics.Color; import android.media.AudioManager; import android.os.Bundle; import android.os.Handler; -import android.util.Log; import android.view.Display; import android.view.KeyEvent; import android.view.Menu; @@ -156,11 +155,6 @@ public class DroidGap extends Activity implements CordovaInterface { private static int ACTIVITY_EXITING = 2; private int activityState = 0; // 0=starting, 1=running (after 1st resume), 2=shutting down - // The base of the initial URL for our app. - // Does not include file name. Ends with / - // ie http://server/path/ - String baseUrl = null; - // Plugin to call when activity result is received protected CordovaPlugin activityResultCallback = null; protected boolean activityResultKeepRunning; @@ -859,7 +853,7 @@ public class DroidGap extends Activity implements CordovaInterface { // If errorUrl specified, then load it final String errorUrl = me.getStringProperty("errorUrl", null); - if ((errorUrl != null) && (errorUrl.startsWith("file://") || errorUrl.indexOf(me.baseUrl) == 0 || this.appView.isUrlWhiteListed(errorUrl)) && (!failingUrl.equals(errorUrl))) { + if ((errorUrl != null) && (errorUrl.startsWith("file://") || this.appView.isUrlWhiteListed(errorUrl)) && (!failingUrl.equals(errorUrl))) { // Load URL on UI thread me.runOnUiThread(new Runnable() { diff --git a/framework/src/org/apache/cordova/ExposedJsApi.java b/framework/src/org/apache/cordova/ExposedJsApi.java index b386a402..a36bb62b 100755 --- a/framework/src/org/apache/cordova/ExposedJsApi.java +++ b/framework/src/org/apache/cordova/ExposedJsApi.java @@ -18,6 +18,7 @@ */ package org.apache.cordova; +import android.webkit.JavascriptInterface; import org.apache.cordova.api.PluginManager; import org.apache.cordova.api.PluginResult; import org.json.JSONException; @@ -37,6 +38,7 @@ import org.json.JSONException; this.jsMessageQueue = jsMessageQueue; } + @JavascriptInterface public String exec(String service, String action, String callbackId, String arguments) throws JSONException { jsMessageQueue.setPaused(true); try { @@ -51,10 +53,12 @@ import org.json.JSONException; } } + @JavascriptInterface public void setNativeToJsBridgeMode(int value) { jsMessageQueue.setBridgeMode(value); } + @JavascriptInterface public String retrieveJsMessages() { return jsMessageQueue.popAndEncode(); } diff --git a/framework/src/org/apache/cordova/FileUtils.java b/framework/src/org/apache/cordova/FileUtils.java index 973b820d..8aff2629 100755 --- a/framework/src/org/apache/cordova/FileUtils.java +++ b/framework/src/org/apache/cordova/FileUtils.java @@ -984,8 +984,16 @@ public class FileUtils extends CordovaPlugin { * @return a mime type */ public static String getMimeType(String filename) { + // Stupid bug in getFileExtensionFromUrl when the file name has a space + // So we need to replace the space with a url encoded %20 + String url = filename.replace(" ", "%20"); MimeTypeMap map = MimeTypeMap.getSingleton(); - return map.getMimeTypeFromExtension(MimeTypeMap.getFileExtensionFromUrl(filename)); + String extension = MimeTypeMap.getFileExtensionFromUrl(url); + if (extension.toLowerCase().equals("3ga")) { + return "audio/3gpp"; + } else { + return map.getMimeTypeFromExtension(extension); + } } /** @@ -1069,16 +1077,18 @@ public class FileUtils extends CordovaPlugin { */ @SuppressWarnings("deprecation") protected static String getRealPathFromURI(Uri contentUri, CordovaInterface cordova) { - String uri = contentUri.toString(); - if (uri.startsWith("content:")) { + final String scheme = contentUri.getScheme(); + + if (scheme.compareTo("content") == 0) { String[] proj = { _DATA }; Cursor cursor = cordova.getActivity().managedQuery(contentUri, proj, null, null, null); int column_index = cursor.getColumnIndexOrThrow(_DATA); cursor.moveToFirst(); return cursor.getString(column_index); + } else if (scheme.compareTo("file") == 0) { + return contentUri.getPath(); } else { - return uri; + return contentUri.toString(); } - } } diff --git a/framework/src/org/apache/cordova/InAppBrowser.java b/framework/src/org/apache/cordova/InAppBrowser.java new file mode 100644 index 00000000..338d3440 --- /dev/null +++ b/framework/src/org/apache/cordova/InAppBrowser.java @@ -0,0 +1,513 @@ +/* + 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. +*/ +package org.apache.cordova; + +import java.io.InputStream; +import java.util.HashMap; +import java.util.StringTokenizer; + +import org.apache.cordova.api.CallbackContext; +import org.apache.cordova.api.CordovaPlugin; +import org.apache.cordova.api.PluginResult; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import android.app.Dialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.net.Uri; +import android.text.InputType; +import android.util.Log; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.KeyEvent; +import android.view.View; +import android.view.Window; +import android.view.WindowManager; +import android.view.WindowManager.LayoutParams; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputMethodManager; +import android.webkit.WebChromeClient; +import android.webkit.WebSettings; +import android.webkit.WebView; +import android.webkit.WebViewClient; +import android.widget.Button; +import android.widget.EditText; +import android.widget.LinearLayout; +import android.widget.RelativeLayout; + +public class InAppBrowser extends CordovaPlugin { + + private static final String NULL = "null"; + protected static final String LOG_TAG = "InAppBrowser"; + private static final String SELF = "_self"; + private static final String SYSTEM = "_system"; + private static final String BLANK = "_blank"; + private static final String LOCATION = "location"; + private static int CLOSE_EVENT = 0; + private static int LOCATION_CHANGED_EVENT = 1; + + private String browserCallbackId = null; + + private Dialog dialog; + private WebView inAppWebView; + private EditText edittext; + private boolean showLocationBar = true; + private CallbackContext callbackContext; + + /** + * Executes the request and returns PluginResult. + * + * @param action The action to execute. + * @param args JSONArry of arguments for the plugin. + * @param callbackId The callback id used when calling back into JavaScript. + * @return A PluginResult object with a status and message. + */ + public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException { + PluginResult.Status status = PluginResult.Status.OK; + String result = ""; + this.callbackContext = callbackContext; + + try { + if (action.equals("open")) { + String url = args.getString(0); + String target = args.optString(1); + if (target == null || target.equals("") || target.equals(NULL)) { + target = SELF; + } + HashMap features = parseFeature(args.optString(2)); + + Log.d(LOG_TAG, "target = " + target); + + url = updateUrl(url); + + // SELF + if (SELF.equals(target)) { + Log.d(LOG_TAG, "in self"); + // load in webview + if (url.startsWith("file://") || url.startsWith("javascript:") + || this.webView.isUrlWhiteListed(url)) { + this.webView.loadUrl(url); + } + // load in InAppBrowser + else { + result = this.showWebPage(url, features); + } + } + // SYSTEM + else if (SYSTEM.equals(target)) { + Log.d(LOG_TAG, "in system"); + result = this.openExternal(url); + } + // BLANK - or anything else + else { + Log.d(LOG_TAG, "in blank"); + result = this.showWebPage(url, features); + } + } + else if (action.equals("close")) { + closeDialog(); + + JSONObject obj = new JSONObject(); + obj.put("type", CLOSE_EVENT); + + PluginResult pluginResult = new PluginResult(status, obj); + pluginResult.setKeepCallback(false); + this.callbackContext.sendPluginResult(pluginResult); + } + else { + status = PluginResult.Status.INVALID_ACTION; + } + this.callbackContext.sendPluginResult(new PluginResult(status, result)); + } catch (JSONException e) { + this.callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION)); + } + return true; + } + + /** + * Put the list of features into a hash map + * + * @param optString + * @return + */ + private HashMap parseFeature(String optString) { + if (optString.equals(NULL)) { + return null; + } else { + HashMap map = new HashMap(); + StringTokenizer features = new StringTokenizer(optString, ","); + StringTokenizer option; + while(features.hasMoreElements()) { + option = new StringTokenizer(features.nextToken(), "="); + if (option.hasMoreElements()) { + String key = option.nextToken(); + Boolean value = option.nextToken().equals("no") ? Boolean.FALSE : Boolean.TRUE; + map.put(key, value); + } + } + return map; + } + } + + /** + * Convert relative URL to full path + * + * @param url + * @return + */ + private String updateUrl(String url) { + Uri newUrl = Uri.parse(url); + if (newUrl.isRelative()) { + url = this.webView.getUrl().substring(0, this.webView.getUrl().lastIndexOf("/")+1) + url; + } + return url; + } + + /** + * Display a new browser with the specified URL. + * + * @param url The url to load. + * @param usePhoneGap Load url in PhoneGap webview + * @return "" if ok, or error message. + */ + public String openExternal(String url) { + try { + Intent intent = null; + intent = new Intent(Intent.ACTION_VIEW); + intent.setData(Uri.parse(url)); + this.cordova.getActivity().startActivity(intent); + return ""; + } catch (android.content.ActivityNotFoundException e) { + Log.d(LOG_TAG, "InAppBrowser: Error loading url "+url+":"+ e.toString()); + return e.toString(); + } + } + + /** + * Closes the dialog + */ + private void closeDialog() { + // TODO: fire 'exit' event + this.webView.sendJavascript("cordova.fireWindowEvent('exit');"); + if (dialog != null) { + dialog.dismiss(); + } + } + + /** + * Checks to see if it is possible to go back one page in history, then does so. + */ + private void goBack() { + if (this.inAppWebView.canGoBack()) { + this.inAppWebView.goBack(); + } + } + + /** + * Checks to see if it is possible to go forward one page in history, then does so. + */ + private void goForward() { + if (this.inAppWebView.canGoForward()) { + this.inAppWebView.goForward(); + } + } + + /** + * Navigate to the new page + * + * @param url to load + */ + private void navigate(String url) { + InputMethodManager imm = (InputMethodManager)this.cordova.getActivity().getSystemService(Context.INPUT_METHOD_SERVICE); + imm.hideSoftInputFromWindow(edittext.getWindowToken(), 0); + + if (!url.startsWith("http") && !url.startsWith("file:")) { + this.inAppWebView.loadUrl("http://" + url); + } else { + this.inAppWebView.loadUrl(url); + } + this.inAppWebView.requestFocus(); + } + + + /** + * Should we show the location bar? + * + * @return boolean + */ + private boolean getShowLocationBar() { + return this.showLocationBar; + } + + /** + * Display a new browser with the specified URL. + * + * @param url The url to load. + * @param jsonObject + */ + public String showWebPage(final String url, HashMap features) { + // Determine if we should hide the location bar. + showLocationBar = true; + if (features != null) { + showLocationBar = features.get(LOCATION).booleanValue(); + } + + final CordovaWebView thatWebView = this.webView; + + // Create dialog in new thread + Runnable runnable = new Runnable() { + /** + * Convert our DIP units to Pixels + * + * @return int + */ + private int dpToPixels(int dipValue) { + int value = (int) TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, + (float) dipValue, + cordova.getActivity().getResources().getDisplayMetrics() + ); + + return value; + } + + public void run() { + // Let's create the main dialog + dialog = new Dialog(cordova.getActivity(), android.R.style.Theme_NoTitleBar); + dialog.getWindow().getAttributes().windowAnimations = android.R.style.Animation_Dialog; + dialog.requestWindowFeature(Window.FEATURE_NO_TITLE); + dialog.setCancelable(true); + dialog.setOnDismissListener(new DialogInterface.OnDismissListener() { + public void onDismiss(DialogInterface dialog) { + try { + JSONObject obj = new JSONObject(); + obj.put("type", CLOSE_EVENT); + + sendUpdate(obj, false); + } catch (JSONException e) { + Log.d(LOG_TAG, "Should never happen"); + } + } + }); + + // Main container layout + LinearLayout main = new LinearLayout(cordova.getActivity()); + main.setOrientation(LinearLayout.VERTICAL); + + // Toolbar layout + RelativeLayout toolbar = new RelativeLayout(cordova.getActivity()); + toolbar.setLayoutParams(new RelativeLayout.LayoutParams(LayoutParams.FILL_PARENT, this.dpToPixels(44))); + toolbar.setPadding(this.dpToPixels(2), this.dpToPixels(2), this.dpToPixels(2), this.dpToPixels(2)); + toolbar.setHorizontalGravity(Gravity.LEFT); + toolbar.setVerticalGravity(Gravity.TOP); + + // Action Button Container layout + RelativeLayout actionButtonContainer = new RelativeLayout(cordova.getActivity()); + actionButtonContainer.setLayoutParams(new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); + actionButtonContainer.setHorizontalGravity(Gravity.LEFT); + actionButtonContainer.setVerticalGravity(Gravity.CENTER_VERTICAL); + actionButtonContainer.setId(1); + + // Back button + Button back = new Button(cordova.getActivity()); + RelativeLayout.LayoutParams backLayoutParams = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.FILL_PARENT); + backLayoutParams.addRule(RelativeLayout.ALIGN_LEFT); + back.setLayoutParams(backLayoutParams); + back.setContentDescription("Back Button"); + back.setId(2); + back.setText("<"); + back.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + goBack(); + } + }); + + // Forward button + Button forward = new Button(cordova.getActivity()); + RelativeLayout.LayoutParams forwardLayoutParams = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.FILL_PARENT); + forwardLayoutParams.addRule(RelativeLayout.RIGHT_OF, 2); + forward.setLayoutParams(forwardLayoutParams); + forward.setContentDescription("Forward Button"); + forward.setId(3); + forward.setText(">"); + forward.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + goForward(); + } + }); + + // Edit Text Box + edittext = new EditText(cordova.getActivity()); + RelativeLayout.LayoutParams textLayoutParams = new RelativeLayout.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT); + textLayoutParams.addRule(RelativeLayout.RIGHT_OF, 1); + textLayoutParams.addRule(RelativeLayout.LEFT_OF, 5); + edittext.setLayoutParams(textLayoutParams); + edittext.setId(4); + edittext.setSingleLine(true); + edittext.setText(url); + edittext.setInputType(InputType.TYPE_TEXT_VARIATION_URI); + edittext.setImeOptions(EditorInfo.IME_ACTION_GO); + edittext.setInputType(InputType.TYPE_NULL); // Will not except input... Makes the text NON-EDITABLE + edittext.setOnKeyListener(new View.OnKeyListener() { + public boolean onKey(View v, int keyCode, KeyEvent event) { + // If the event is a key-down event on the "enter" button + if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) { + navigate(edittext.getText().toString()); + return true; + } + return false; + } + }); + + // Close button + Button close = new Button(cordova.getActivity()); + RelativeLayout.LayoutParams closeLayoutParams = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.FILL_PARENT); + closeLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT); + close.setLayoutParams(closeLayoutParams); + forward.setContentDescription("Close Button"); + close.setId(5); + close.setText("Done"); + close.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + closeDialog(); + } + }); + + // WebView + inAppWebView = new WebView(cordova.getActivity()); + inAppWebView.setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT)); + inAppWebView.setWebChromeClient(new WebChromeClient()); + WebViewClient client = new InAppBrowserClient(thatWebView, edittext); + inAppWebView.setWebViewClient(client); + WebSettings settings = inAppWebView.getSettings(); + settings.setJavaScriptEnabled(true); + settings.setJavaScriptCanOpenWindowsAutomatically(true); + settings.setBuiltInZoomControls(true); + settings.setPluginsEnabled(true); + settings.setDomStorageEnabled(true); + inAppWebView.loadUrl(url); + inAppWebView.setId(6); + inAppWebView.getSettings().setLoadWithOverviewMode(true); + inAppWebView.getSettings().setUseWideViewPort(true); + inAppWebView.requestFocus(); + inAppWebView.requestFocusFromTouch(); + + // Add the back and forward buttons to our action button container layout + actionButtonContainer.addView(back); + actionButtonContainer.addView(forward); + + // Add the views to our toolbar + toolbar.addView(actionButtonContainer); + toolbar.addView(edittext); + toolbar.addView(close); + + // Don't add the toolbar if its been disabled + if (getShowLocationBar()) { + // Add our toolbar to our main view/layout + main.addView(toolbar); + } + + // Add our webview to our main view/layout + main.addView(inAppWebView); + + WindowManager.LayoutParams lp = new WindowManager.LayoutParams(); + lp.copyFrom(dialog.getWindow().getAttributes()); + lp.width = WindowManager.LayoutParams.FILL_PARENT; + lp.height = WindowManager.LayoutParams.FILL_PARENT; + + dialog.setContentView(main); + dialog.show(); + dialog.getWindow().setAttributes(lp); + } + + private Bitmap loadDrawable(String filename) throws java.io.IOException { + InputStream input = cordova.getActivity().getAssets().open(filename); + return BitmapFactory.decodeStream(input); + } + }; + this.cordova.getActivity().runOnUiThread(runnable); + return ""; + } + + /** + * Create a new plugin result and send it back to JavaScript + * + * @param obj a JSONObject contain event payload information + */ + private void sendUpdate(JSONObject obj, boolean keepCallback) { + if (this.browserCallbackId != null) { + PluginResult result = new PluginResult(PluginResult.Status.OK, obj); + result.setKeepCallback(keepCallback); + this.callbackContext.sendPluginResult(result); + } + } + + /** + * The webview client receives notifications about appView + */ + public class InAppBrowserClient extends WebViewClient { + EditText edittext; + CordovaWebView webView; + + /** + * Constructor. + * + * @param mContext + * @param edittext + */ + public InAppBrowserClient(CordovaWebView webView, EditText mEditText) { + this.webView = webView; + this.edittext = mEditText; + } + + /** + * Notify the host application that a page has started loading. + * + * @param view The webview initiating the callback. + * @param url The url of the page. + */ + @Override + public void onPageStarted(WebView view, String url, Bitmap favicon) { + super.onPageStarted(view, url, favicon); + String newloc; + if (url.startsWith("http:") || url.startsWith("https:") || url.startsWith("file:")) { + newloc = url; + } else { + newloc = "http://" + url; + } + + if (!newloc.equals(edittext.getText().toString())) { + edittext.setText(newloc); + } + + // TODO: Fire 'loadstart' event only on the InAppBrowser object + this.webView.sendJavascript("cordova.fireWindowEvent('loadstart', '" + url + "');"); + } + + public void onPageFinished(WebView view, String url) { + super.onPageFinished(view, url); + // TODO: Fire 'loadstop' event only on the InAppBrowser object + this.webView.sendJavascript("cordova.fireWindowEvent('loadstop', '" + url + "');"); + } + } +} \ No newline at end of file diff --git a/framework/src/org/apache/cordova/NetworkManager.java b/framework/src/org/apache/cordova/NetworkManager.java index 5d879180..bb4743f2 100755 --- a/framework/src/org/apache/cordova/NetworkManager.java +++ b/framework/src/org/apache/cordova/NetworkManager.java @@ -74,6 +74,7 @@ public class NetworkManager extends CordovaPlugin { ConnectivityManager sockMan; BroadcastReceiver receiver; + private String lastStatus = ""; /** * Constructor. @@ -99,12 +100,11 @@ public class NetworkManager extends CordovaPlugin { intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); if (this.receiver == null) { this.receiver = new BroadcastReceiver() { - @SuppressWarnings("deprecation") @Override public void onReceive(Context context, Intent intent) { // (The null check is for the ARM Emulator, please use Intel Emulator for better results) - if(NetworkManager.this.webView != null) - updateConnectionInfo((NetworkInfo) intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO)); + if(NetworkManager.this.webView != null) + updateConnectionInfo(sockMan.getActiveNetworkInfo()); } }; cordova.getActivity().registerReceiver(this.receiver, intentFilter); @@ -147,13 +147,6 @@ public class NetworkManager extends CordovaPlugin { } } - /** - * Stop the network receiver on navigation. - */ - public void onReset() { - this.onDestroy(); - } - //-------------------------------------------------------------------------- // LOCAL METHODS //-------------------------------------------------------------------------- @@ -166,7 +159,14 @@ public class NetworkManager extends CordovaPlugin { */ private void updateConnectionInfo(NetworkInfo info) { // send update to javascript "navigator.network.connection" - sendUpdate(this.getConnectionInfo(info)); + // Jellybean sends its own info + String thisStatus = this.getConnectionInfo(info); + if(!thisStatus.equals(lastStatus)) + { + sendUpdate(thisStatus); + lastStatus = thisStatus; + } + } /** @@ -186,6 +186,7 @@ public class NetworkManager extends CordovaPlugin { type = getType(info); } } + Log.d("CordovaNetworkManager", "Connection Type: " + type); return type; } @@ -200,7 +201,6 @@ public class NetworkManager extends CordovaPlugin { result.setKeepCallback(true); connectionCallbackContext.sendPluginResult(result); } - webView.postMessage("networkconnection", type); } diff --git a/test/AndroidManifest.xml b/test/AndroidManifest.xml index e35f6c67..aee2e04f 100755 --- a/test/AndroidManifest.xml +++ b/test/AndroidManifest.xml @@ -255,5 +255,15 @@ + + + + + + diff --git a/test/src/org/apache/cordova/test/BackButtonMultiPageTest.java b/test/src/org/apache/cordova/test/BackButtonMultiPageTest.java index fa3e0b04..6d4a7ac8 100644 --- a/test/src/org/apache/cordova/test/BackButtonMultiPageTest.java +++ b/test/src/org/apache/cordova/test/BackButtonMultiPageTest.java @@ -26,6 +26,7 @@ import org.apache.cordova.test.actions.backbuttonmultipage; import android.test.ActivityInstrumentationTestCase2; import android.view.KeyEvent; +import android.view.inputmethod.BaseInputConnection; import android.widget.FrameLayout; import android.widget.LinearLayout; @@ -97,6 +98,54 @@ public class BackButtonMultiPageTest extends ActivityInstrumentationTestCase2