http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/da952e07/cordova-lib/spec-cordova/fixtures/platforms/atari/framework/src/org/apache/cordova/engine/SystemWebViewEngine.java ---------------------------------------------------------------------- diff --git a/cordova-lib/spec-cordova/fixtures/platforms/atari/framework/src/org/apache/cordova/engine/SystemWebViewEngine.java b/cordova-lib/spec-cordova/fixtures/platforms/atari/framework/src/org/apache/cordova/engine/SystemWebViewEngine.java new file mode 100755 index 0000000..0fa0276 --- /dev/null +++ b/cordova-lib/spec-cordova/fixtures/platforms/atari/framework/src/org/apache/cordova/engine/SystemWebViewEngine.java @@ -0,0 +1,350 @@ +/* + 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.engine; + +import android.annotation.SuppressLint; +import android.annotation.TargetApi; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.ApplicationInfo; +import android.os.Build; +import android.view.View; +import android.webkit.ValueCallback; +import android.webkit.WebSettings; +import android.webkit.WebSettings.LayoutAlgorithm; +import android.webkit.WebView; + +import org.apache.cordova.CordovaBridge; +import org.apache.cordova.CordovaInterface; +import org.apache.cordova.CordovaPreferences; +import org.apache.cordova.CordovaResourceApi; +import org.apache.cordova.CordovaWebView; +import org.apache.cordova.CordovaWebViewEngine; +import org.apache.cordova.ICordovaCookieManager; +import org.apache.cordova.LOG; +import org.apache.cordova.NativeToJsMessageQueue; +import org.apache.cordova.PluginManager; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + + +/** + * Glue class between CordovaWebView (main Cordova logic) and SystemWebView (the actual View). + * We make the Engine separate from the actual View so that: + * A) We don't need to worry about WebView methods clashing with CordovaWebViewEngine methods + * (e.g.: goBack() is void for WebView, and boolean for CordovaWebViewEngine) + * B) Separating the actual View from the Engine makes API surfaces smaller. + * Class uses two-phase initialization. However, CordovaWebView is responsible for calling .init(). + */ +public class SystemWebViewEngine implements CordovaWebViewEngine { + public static final String TAG = "SystemWebViewEngine"; + + protected final SystemWebView webView; + protected final SystemCookieManager cookieManager; + protected CordovaPreferences preferences; + protected CordovaBridge bridge; + protected CordovaWebViewEngine.Client client; + protected CordovaWebView parentWebView; + protected CordovaInterface cordova; + protected PluginManager pluginManager; + protected CordovaResourceApi resourceApi; + protected NativeToJsMessageQueue nativeToJsMessageQueue; + private BroadcastReceiver receiver; + + /** Used when created via reflection. */ + public SystemWebViewEngine(Context context, CordovaPreferences preferences) { + this(new SystemWebView(context), preferences); + } + + public SystemWebViewEngine(SystemWebView webView) { + this(webView, null); + } + + public SystemWebViewEngine(SystemWebView webView, CordovaPreferences preferences) { + this.preferences = preferences; + this.webView = webView; + cookieManager = new SystemCookieManager(webView); + } + + @Override + public void init(CordovaWebView parentWebView, CordovaInterface cordova, CordovaWebViewEngine.Client client, + CordovaResourceApi resourceApi, PluginManager pluginManager, + NativeToJsMessageQueue nativeToJsMessageQueue) { + if (this.cordova != null) { + throw new IllegalStateException(); + } + // Needed when prefs are not passed by the constructor + if (preferences == null) { + preferences = parentWebView.getPreferences(); + } + this.parentWebView = parentWebView; + this.cordova = cordova; + this.client = client; + this.resourceApi = resourceApi; + this.pluginManager = pluginManager; + this.nativeToJsMessageQueue = nativeToJsMessageQueue; + webView.init(this, cordova); + + initWebViewSettings(); + + nativeToJsMessageQueue.addBridgeMode(new NativeToJsMessageQueue.OnlineEventsBridgeMode(new NativeToJsMessageQueue.OnlineEventsBridgeMode.OnlineEventsBridgeModeDelegate() { + @Override + public void setNetworkAvailable(boolean value) { + webView.setNetworkAvailable(value); + } + @Override + public void runOnUiThread(Runnable r) { + SystemWebViewEngine.this.cordova.getActivity().runOnUiThread(r); + } + })); + if(Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN_MR2) + nativeToJsMessageQueue.addBridgeMode(new NativeToJsMessageQueue.EvalBridgeMode(this, cordova)); + bridge = new CordovaBridge(pluginManager, nativeToJsMessageQueue); + exposeJsInterface(webView, bridge); + } + + @Override + public CordovaWebView getCordovaWebView() { + return parentWebView; + } + + @Override + public ICordovaCookieManager getCookieManager() { + return cookieManager; + } + + @Override + public View getView() { + return webView; + } + + @SuppressLint({"NewApi", "SetJavaScriptEnabled"}) + @SuppressWarnings("deprecation") + private void initWebViewSettings() { + webView.setInitialScale(0); + webView.setVerticalScrollBarEnabled(false); + // Enable JavaScript + final WebSettings settings = webView.getSettings(); + settings.setJavaScriptEnabled(true); + settings.setJavaScriptCanOpenWindowsAutomatically(true); + settings.setLayoutAlgorithm(LayoutAlgorithm.NORMAL); + + // 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 }); + + String manufacturer = android.os.Build.MANUFACTURER; + LOG.d(TAG, "CordovaWebView is running on device made by: " + manufacturer); + if(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.HONEYCOMB && + android.os.Build.MANUFACTURER.contains("HTC")) + { + 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."); + } + + //We don't save any form data in the application + settings.setSaveFormData(false); + settings.setSavePassword(false); + + // 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.JELLY_BEAN) { + settings.setAllowUniversalAccessFromFileURLs(true); + } + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) { + settings.setMediaPlaybackRequiresUserGesture(false); + } + // Enable database + // We keep this disabled because we use or shim to get around DOM_EXCEPTION_ERROR_16 + String databasePath = webView.getContext().getApplicationContext().getDir("database", Context.MODE_PRIVATE).getPath(); + settings.setDatabaseEnabled(true); + settings.setDatabasePath(databasePath); + + + //Determine whether we're in debug or release mode, and turn on Debugging! + ApplicationInfo appInfo = webView.getContext().getApplicationContext().getApplicationInfo(); + if ((appInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0 && + android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) { + enableRemoteDebugging(); + } + + settings.setGeolocationDatabasePath(databasePath); + + // Enable DOM storage + settings.setDomStorageEnabled(true); + + // Enable built-in geolocation + settings.setGeolocationEnabled(true); + + // Enable AppCache + // Fix for CB-2282 + settings.setAppCacheMaxSize(5 * 1048576); + settings.setAppCachePath(databasePath); + settings.setAppCacheEnabled(true); + + // Fix for CB-1405 + // Google issue 4641 + String defaultUserAgent = settings.getUserAgentString(); + + // Fix for CB-3360 + String overrideUserAgent = preferences.getString("OverrideUserAgent", null); + if (overrideUserAgent != null) { + settings.setUserAgentString(overrideUserAgent); + } else { + String appendUserAgent = preferences.getString("AppendUserAgent", null); + if (appendUserAgent != null) { + settings.setUserAgentString(defaultUserAgent + " " + appendUserAgent); + } + } + // End CB-3360 + + IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(Intent.ACTION_CONFIGURATION_CHANGED); + if (this.receiver == null) { + this.receiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + settings.getUserAgentString(); + } + }; + webView.getContext().registerReceiver(this.receiver, intentFilter); + } + // end CB-1405 + } + + @TargetApi(Build.VERSION_CODES.KITKAT) + private void enableRemoteDebugging() { + try { + WebView.setWebContentsDebuggingEnabled(true); + } catch (IllegalArgumentException e) { + LOG.d(TAG, "You have one job! To turn on Remote Web Debugging! YOU HAVE FAILED! "); + e.printStackTrace(); + } + } + + private static void exposeJsInterface(WebView webView, CordovaBridge bridge) { + if ((Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1)) { + 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; + } + SystemExposedJsApi exposedJsApi = new SystemExposedJsApi(bridge); + webView.addJavascriptInterface(exposedJsApi, "_cordovaNative"); + } + + + /** + * Load the url into the webview. + */ + @Override + public void loadUrl(final String url, boolean clearNavigationStack) { + webView.loadUrl(url); + } + + @Override + public String getUrl() { + return webView.getUrl(); + } + + @Override + public void stopLoading() { + webView.stopLoading(); + } + + @Override + public void clearCache() { + webView.clearCache(true); + } + + @Override + public void clearHistory() { + webView.clearHistory(); + } + + @Override + public boolean canGoBack() { + return webView.canGoBack(); + } + + /** + * Go to previous page in history. (We manage our own history) + * + * @return true if we went back, false if we are already at top + */ + @Override + public boolean goBack() { + // Check webview first to see if there is a history + // This is needed to support curPage#diffLink, since they are added to parentEngine's history, but not our history url array (JQMobile behavior) + if (webView.canGoBack()) { + webView.goBack(); + return true; + } + return false; + } + + @Override + public void setPaused(boolean value) { + if (value) { + webView.onPause(); + webView.pauseTimers(); + } else { + webView.onResume(); + webView.resumeTimers(); + } + } + + @Override + public void destroy() { + webView.chromeClient.destroyLastDialog(); + webView.destroy(); + // unregister the receiver + if (receiver != null) { + try { + webView.getContext().unregisterReceiver(receiver); + } catch (Exception e) { + LOG.e(TAG, "Error unregistering configuration receiver: " + e.getMessage(), e); + } + } + } + + @Override + public void evaluateJavascript(String js, ValueCallback<String> callback) { + if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + webView.evaluateJavascript(js, callback); + } + else + { + LOG.d(TAG, "This webview is using the old bridge"); + } + } +}
http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/da952e07/cordova-lib/spec-cordova/fixtures/platforms/atari/package.json ---------------------------------------------------------------------- diff --git a/cordova-lib/spec-cordova/fixtures/platforms/atari/package.json b/cordova-lib/spec-cordova/fixtures/platforms/atari/package.json new file mode 100644 index 0000000..00abaa0 --- /dev/null +++ b/cordova-lib/spec-cordova/fixtures/platforms/atari/package.json @@ -0,0 +1,49 @@ +{ + "name": "atari", + "version": "1.0.0", + "description": "platform test repo. Copy of cordova-android", + "bin": { + "create": "bin/create" + }, + "main": "bin/templates/cordova/Api.js", + "repository": { + "type": "git", + "url": "https://git-wip-us.apache.org/repos/asf/cordova-android.git" + }, + "keywords": [ + "android", + "cordova", + "apache" + ], + "scripts": { + "test": "npm run jshint && jasmine", + "cover": "istanbul cover --root bin/templates/cordova --print detail jasmine", + "test-build": "jasmine --captureExceptions --color spec/e2e/*.spec.js", + "jshint": "jshint bin && jshint spec" + }, + "author": "Apache Software Foundation", + "license": "Apache-2.0", + "dependencies": { + "cordova-common": "^1.5.0", + "elementtree": "^0.1.6", + "nopt": "^3.0.1", + "properties-parser": "^0.2.3", + "q": "^1.4.1", + "shelljs": "^0.5.3" + }, + "bundledDependencies": [ + "cordova-common", + "elementtree", + "nopt", + "properties-parser", + "q", + "shelljs" + ], + "devDependencies": { + "istanbul": "^0.4.2", + "jasmine": "^2.5.2", + "jshint": "^2.6.0", + "promise-matchers": "~0", + "rewire": "^2.1.3" + } +} http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/da952e07/cordova-lib/spec-cordova/platform.spec.js ---------------------------------------------------------------------- diff --git a/cordova-lib/spec-cordova/platform.spec.js b/cordova-lib/spec-cordova/platform.spec.js index 763ba9c..604d974 100644 --- a/cordova-lib/spec-cordova/platform.spec.js +++ b/cordova-lib/spec-cordova/platform.spec.js @@ -30,6 +30,7 @@ var helpers = require('./helpers'), var projectRoot = 'C:\\Projects\\cordova-projects\\move-tracker'; var pluginsDir = path.join(__dirname, 'fixtures', 'plugins'); +var platDir = path.join(__dirname, 'fixtures', 'platforms'); describe('platform end-to-end', function () { @@ -353,4 +354,65 @@ describe('plugin add and rm end-to-end --fetch', function () { }) .fin(done); }, 60000); -}); \ No newline at end of file +}); + + +describe('non-core platform add and rm end-to-end --fetch', function () { + + var tmpDir = helpers.tmpDir('non-core-platform-test'); + var project = path.join(tmpDir, 'hello'); + var results; + + beforeEach(function() { + process.chdir(tmpDir); + events.on('results', function(res) { results = res; }); + }); + + afterEach(function() { + process.chdir(path.join(__dirname, '..')); // Needed to rm the dir on Windows. + shell.rm('-rf', tmpDir); + }); + + it('Test 009 : should add and remove 3rd party platforms', function(done) { + + var installed; + cordova.raw.create('hello') + .then(function() { + process.chdir(project); + //add cordova-android instead of android + return cordova.raw.platform('add', 'cordova-android', {'fetch': true}); + }) + .then(function() { + //local 3rd party platform + return cordova.raw.platform('add', path.join(platDir, 'atari'), {'fetch': true}); + }) + .then(function() { + //3rd party platform from npm + return cordova.raw.platform('add', 'cordova-platform-test', {'fetch': true}); + }) + + .then(function() { + expect(path.join(project, 'platforms', 'android')).toExist(); + expect(path.join(project, 'platforms', 'cordova-platform-test')).toExist(); + expect(path.join(project, 'platforms', 'atari')).toExist(); + return cordova.raw.platform('ls'); + }) + .then(function() { + //use regex to grab installed platforms + installed = results.match(/Installed platforms:\n (.*)\n (.*)\n (.*)/); + expect(installed).toBeDefined(); + expect(installed[1].indexOf('android')).toBeGreaterThan(-1); + expect(installed[3].indexOf('cordova-platform-test')).toBeGreaterThan(-1); + expect(installed[2].indexOf('atari')).toBeGreaterThan(-1); + return cordova.raw.platform('rm', 'atari', {'fetch':true}); + }) + .then(function() { + expect(path.join(project, 'platforms', 'atari')).not.toExist(); + }) + .fail(function(err) { + console.error(err); + expect(err).toBeUndefined(); + }) + .fin(done); + }, 90000); +}); http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/da952e07/cordova-lib/spec-plugman/install.spec.js ---------------------------------------------------------------------- diff --git a/cordova-lib/spec-plugman/install.spec.js b/cordova-lib/spec-plugman/install.spec.js index 4e486f0..081cc3c 100644 --- a/cordova-lib/spec-plugman/install.spec.js +++ b/cordova-lib/spec-plugman/install.spec.js @@ -437,13 +437,13 @@ describe('install', function() { }); describe('failure', function() { - it('Test 021 : should throw if platform is unrecognized', function(done) { + it('Test 021 : should throw if platform is unrecognized & is missing api.js', function(done) { install('atari', project, 'SomePlugin') .then(function() { expect(false).toBe(true); done(); }).fail(function err (errMsg) { - expect(errMsg.toString()).toContain('atari not supported.'); + expect(errMsg.toString()).toContain('It is missing API.js'); done(); }, 6000); }); --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
