Merge branch 'master' into 4.0.x (bindButton changes)

Conflicts:
	framework/src/org/apache/cordova/CordovaWebView.java
	package.json
This commit is contained in:
Andrew Grieve 2014-06-24 15:22:27 -04:00
commit 4ce5123a12
7 changed files with 178 additions and 87 deletions

View File

@ -116,6 +116,50 @@ function copyGradleWrapper(sdkPath, projectPath) {
shell.cp('-r', path.join(wrapperDir, 'gradle'), projectPath);
}
/**
* Test whether a package name is acceptable for use as an android project.
* Returns a promise, fulfilled if the package name is acceptable; rejected
* otherwise.
*/
function validatePackageName(package_name) {
//Make the package conform to Java package types
//Enforce underscore limitation
if (!/^[a-zA-Z]+(\.[a-zA-Z0-9][a-zA-Z0-9_]*)+$/.test(package_name)) {
return Q.reject('Package name must look like: com.company.Name');
}
//Class is a reserved word
if(/\b[Cc]lass\b/.test(package_name)) {
return Q.reject('class is a reserved word');
}
return Q.resolve();
}
/**
* Test whether a project name is acceptable for use as an android class.
* Returns a promise, fulfilled if the project name is acceptable; rejected
* otherwise.
*/
function validateProjectName(project_name) {
//Make sure there's something there
if (project_name === '') {
return Q.reject('Project name cannot be empty');
}
//Enforce stupid name error
if (project_name === 'CordovaActivity') {
return Q.reject('Project name cannot be CordovaActivity');
}
//Classes in Java don't begin with numbers
if (/^[0-9]/.test(project_name)) {
return Q.reject('Project name must not begin with a number');
}
return Q.resolve();
}
/**
* $ create [options]
*
@ -156,34 +200,14 @@ exports.createProject = function(project_path, package_name, project_name, proje
}
//Make the package conform to Java package types
if (!/[a-zA-Z0-9_]+\.[a-zA-Z0-9_](.[a-zA-Z0-9_])*/.test(package_name)) {
return Q.reject('Package name must look like: com.company.Name');
}
//Enforce underscore limitation
if (/[_]+[a-zA-Z0-9_]*/.test(package_name)) {
return Q.reject("Package name can't begin with an underscore");
}
//Enforce stupid name error
if (project_name === 'CordovaActivity') {
return Q.reject('Project name cannot be CordovaActivity');
}
//Classes in Java don't begin with numbers
if (/[0-9]+[a-zA-Z0-9]/.test(project_name)) {
return Q.reject('Project name must not begin with a number');
}
//Class is a reserved word
if(/[C|c]+lass+[\s|\.]/.test(package_name) && !/[a-zA-Z0-9_]+[C|c]+lass/.test(package_name))
{
return Q.reject('class is a reserved word');
}
// Check that requirements are met and proper targets are installed
return check_reqs.run()
return validatePackageName(package_name)
.then(function() {
validateProjectName(project_name);
})
// Check that requirements are met and proper targets are installed
.then(function() {
check_reqs.run();
}).then(function() {
// Log the given values for the project
console.log('Creating Cordova project for the Android platform:');
console.log('\tPath: ' + project_path);
@ -284,3 +308,7 @@ exports.updateProject = function(projectPath) {
});
};
// For testing
exports.validatePackageName = validatePackageName;
exports.validateProjectName = validateProjectName;

View File

@ -21,8 +21,8 @@ package org.apache.cordova;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Locale;
import org.apache.cordova.Config;
@ -72,8 +72,7 @@ public class AndroidWebView extends WebView implements CordovaWebView {
public static final String TAG = "CordovaWebView";
public static final String CORDOVA_VERSION = "4.0.0-dev";
private ArrayList<Integer> keyDownCodes = new ArrayList<Integer>();
private ArrayList<Integer> keyUpCodes = new ArrayList<Integer>();
private HashSet<Integer> boundKeyCodes = new HashSet<Integer>();
PluginManager pluginManager;
private boolean paused;
@ -92,10 +91,6 @@ public class AndroidWebView extends WebView implements CordovaWebView {
// Flag to track that a loadUrl timeout occurred
int loadUrlTimeout = 0;
private boolean bound;
private boolean handleButton = false;
private long lastMenuEventTime = 0;
NativeToJsMessageQueue jsMessageQueue;
@ -708,17 +703,13 @@ public class AndroidWebView extends WebView implements CordovaWebView {
@Override
public boolean onKeyDown(int keyCode, KeyEvent event)
{
if(keyDownCodes.contains(keyCode))
if(boundKeyCodes.contains(keyCode))
{
if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
// only override default behavior is event bound
LOG.d(TAG, "Down Key Hit");
this.loadUrl("javascript:cordova.fireDocumentEvent('volumedownbutton');");
return true;
}
// If volumeup key
else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
LOG.d(TAG, "Up Key Hit");
this.loadUrl("javascript:cordova.fireDocumentEvent('volumeupbutton');");
return true;
}
@ -729,7 +720,8 @@ public class AndroidWebView extends WebView implements CordovaWebView {
}
else if(keyCode == KeyEvent.KEYCODE_BACK)
{
return !(this.startOfHistory()) || this.bound;
return !(this.startOfHistory()) || isButtonPlumbedToJs(KeyEvent.KEYCODE_BACK);
}
else if(keyCode == KeyEvent.KEYCODE_MENU)
{
@ -746,10 +738,8 @@ public class AndroidWebView extends WebView implements CordovaWebView {
return super.onKeyDown(keyCode, event);
}
}
return super.onKeyDown(keyCode, event);
}
@Override
public boolean onKeyUp(int keyCode, KeyEvent event)
@ -759,10 +749,11 @@ public class AndroidWebView extends WebView implements CordovaWebView {
// A custom view is currently displayed (e.g. playing a video)
if(mCustomView != null) {
this.hideCustomView();
return true;
} else {
// The webview is currently displayed
// If back key is bound, then send event to JavaScript
if (this.bound) {
if (isButtonPlumbedToJs(KeyEvent.KEYCODE_BACK)) {
this.loadUrl("javascript:cordova.fireDocumentEvent('backbutton');");
return true;
} else {
@ -788,48 +779,31 @@ public class AndroidWebView extends WebView implements CordovaWebView {
this.loadUrl("javascript:cordova.fireDocumentEvent('searchbutton');");
return true;
}
else if(keyUpCodes.contains(keyCode))
{
//What the hell should this do?
return super.onKeyUp(keyCode, event);
}
//Does webkit change this behavior?
return super.onKeyUp(keyCode, event);
}
public void bindButton(boolean override)
{
this.bound = override;
}
public void bindButton(String button, boolean override) {
// TODO Auto-generated method stub
if (button.compareTo("volumeup")==0) {
keyDownCodes.add(KeyEvent.KEYCODE_VOLUME_UP);
@Override
public void setButtonPlumbedToJs(int keyCode, boolean value) {
switch (keyCode) {
case KeyEvent.KEYCODE_VOLUME_DOWN:
case KeyEvent.KEYCODE_VOLUME_UP:
case KeyEvent.KEYCODE_BACK:
// TODO: Why are search and menu buttons handled separately?
boundKeyCodes.add(keyCode);
return;
default:
throw new IllegalArgumentException("Unsupported keycode: " + keyCode);
}
else if (button.compareTo("volumedown")==0) {
keyDownCodes.add(KeyEvent.KEYCODE_VOLUME_DOWN);
}
}
public void bindButton(int keyCode, boolean keyDown, boolean override) {
if(keyDown)
{
keyDownCodes.add(keyCode);
}
else
{
keyUpCodes.add(keyCode);
}
}
public boolean isBackButtonBound()
@Override
public boolean isButtonPlumbedToJs(int keyCode)
{
return this.bound;
return boundKeyCodes.contains(keyCode);
}
public void handlePause(boolean keepRunning)
{
LOG.d(TAG, "Handle the pause");
@ -901,10 +875,6 @@ public class AndroidWebView extends WebView implements CordovaWebView {
return paused;
}
public boolean hadKeyEvent() {
return handleButton;
}
// Wrapping these functions in their own class prevents warnings in adb like:
// VFY: unable to resolve virtual method 285: Landroid/webkit/WebSettings;.setAllowUniversalAccessFromFileURLs
@TargetApi(16)

View File

@ -136,10 +136,8 @@ public interface CordovaWebView {
CordovaResourceApi getResourceApi();
void bindButton(boolean override);
void bindButton(String button, boolean override);
boolean isBackButtonBound();
void setButtonPlumbedToJs(int keyCode, boolean override);
boolean isButtonPlumbedToJs(int keyCode);
void sendPluginResult(PluginResult cr, String callbackId);
@ -148,7 +146,6 @@ public interface CordovaWebView {
void setLayoutParams(android.widget.FrameLayout.LayoutParams layoutParams);
// Required for test
String getUrl();
boolean isPaused();
}

View File

@ -32,6 +32,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.telephony.TelephonyManager;
import android.view.KeyEvent;
import java.util.HashMap;
@ -217,7 +218,7 @@ public class CoreAndroid extends CordovaPlugin {
*/
public void overrideBackbutton(boolean override) {
LOG.i("App", "WARNING: Back Button Default Behavior will be overridden. The backbutton event will be fired!");
webView.bindButton(override);
webView.setButtonPlumbedToJs(KeyEvent.KEYCODE_BACK, override);
}
/**
@ -229,7 +230,12 @@ public class CoreAndroid extends CordovaPlugin {
*/
public void overrideButton(String button, boolean override) {
LOG.i("App", "WARNING: Volume Button Default Behavior will be overridden. The volume event will be fired!");
webView.bindButton(button, override);
if (button.equals("volumeup")) {
webView.setButtonPlumbedToJs(KeyEvent.KEYCODE_VOLUME_UP, override);
}
else if (button.equals("volumedown")) {
webView.setButtonPlumbedToJs(KeyEvent.KEYCODE_VOLUME_DOWN, override);
}
}
/**
@ -238,7 +244,7 @@ public class CoreAndroid extends CordovaPlugin {
* @return boolean
*/
public boolean isBackbuttonOverridden() {
return webView.isBackButtonBound();
return webView.isButtonPlumbedToJs(KeyEvent.KEYCODE_BACK);
}
/**

View File

@ -171,6 +171,7 @@ public class PluginManager {
service = "";
pluginClass = "";
insideFeature = false;
onload = false;
}
}
try {

View File

@ -12,11 +12,18 @@
"cordova",
"apache"
],
"scripts": {
"test": "jasmine-node --color spec"
},
"author": "Apache Software Foundation",
"license": "Apache version 2.0",
"dependencies": {
"q": "^0.9.0",
"shelljs": "^0.2.6",
"which": "^1.0.5"
},
"devDependencies": {
"jasmine-node": "~1",
"promise-matchers": "~0"
}
}

82
spec/create.spec.js Normal file
View File

@ -0,0 +1,82 @@
/**
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.
*/
/* jshint laxcomma:true */
require("promise-matchers");
var create = require("../bin/lib/create");
describe("create", function () {
describe("validatePackageName", function() {
var valid = [
"org.apache.mobilespec"
, "com.example"
, "com.42floors.package"
];
var invalid = [
""
, "com.class.is.bad"
, "0com.example.mobilespec"
, "c-m.e@a!p%e.mobilespec"
, "notenoughdots"
, ".starts.with.a.dot"
, "ends.with.a.dot."
, "_underscore.anything"
, "underscore._something"
, "_underscore._all._the._things"
];
valid.forEach(function(package_name) {
it("should accept " + package_name, function(done) {
expect(create.validatePackageName(package_name)).toHaveBeenResolved(done);
});
});
invalid.forEach(function(package_name) {
it("should reject " + package_name, function(done) {
expect(create.validatePackageName(package_name)).toHaveBeenRejected(done);
});
});
});
describe("validateProjectName", function() {
var valid = [
"mobilespec"
, "package_name"
, "PackageName"
, "CordovaLib"
];
var invalid = [
""
, "0startswithdigit"
, "CordovaActivity"
];
valid.forEach(function(project_name) {
it("should accept " + project_name, function(done) {
expect(create.validateProjectName(project_name)).toHaveBeenResolved(done);
});
});
invalid.forEach(function(project_name) {
it("should reject " + project_name, function(done) {
expect(create.validateProjectName(project_name)).toHaveBeenRejected(done);
});
});
});
});