Compare commits

..

1 Commits

Author SHA1 Message Date
Ian Clelland
6201a6f6ec Add guard lines to gradle build file for extensions 2014-11-06 14:39:58 -05:00
35 changed files with 606 additions and 1433 deletions

View File

@@ -25,8 +25,8 @@ Anyone can contribute to Cordova. And we need your contributions.
There are multiple ways to contribute: report bugs, improve the docs, and
contribute code.
For instructions on this, start with the
For instructions on this, start with the
[contribution overview](http://cordova.apache.org/#contribute).
The details are explained there, but the important items are:
@@ -35,4 +35,3 @@ The details are explained there, but the important items are:
- Run the tests so your patch doesn't break existing functionality.
We look forward to your contributions!

View File

@@ -20,40 +20,6 @@
-->
## Release Notes for Cordova (Android) ##
### Release 3.7.2 (May 2015) ###
* Removed Intent Functionality from Preferences - Preferences can no longer be set by intents
### Release 3.7.1 (January 2015) ###
* CB-8411 Initialize plugins only after `createViews()` is called (regression in 3.7.0)
### Release 3.7.0 (January 2015) ###
* CB-8328 Allow plugins to handle certificate challenges (close #150)
* CB-8201 Add support for auth dialogs into Cordova Android
* CB-8017 Add support for `<input type=file>` for Lollipop
* CB-8143 Loads of gradle improvements (try it with cordova/build --gradle)
* CB-8329 Cancel outstanding ActivityResult requests when a new startActivityForResult occurs
* CB-8026 Bumping up Android Version and setting it up to allow third-party cookies. This might change later.
* CB-8210 Use PluginResult for various events from native so that content-security-policy <meta> can be used
* CB-8168 Add support for `cordova/run --list` (closes #139)
* CB-8176 Vastly better auto-detection of SDK & JDK locations
* CB-8079 Use activity class package name, but fallback to application package name when looking for splash screen drawable
* CB-8147 Have corodva/build warn about unrecognized flags rather than fail
* CB-7881 Android tooling shouldn't lock application directory
* CB-8112 Turn off mediaPlaybackRequiresUserGesture
* CB-6153 Add a preference for controlling hardware button audio stream (DefaultVolumeStream)
* CB-8031 Fix race condition that shows as ConcurrentModificationException
* CB-7974 Cancel timeout timer if view is destroyed
* CB-7940 Disable exec bridge if bridgeSecret is wrong
* CB-7758 Allow content-url-hosted pages to access the bridge
* CB-6511 Fixes build for android when app name contains unicode characters.
* CB-7707 Added multipart PluginResult
* CB-6837 Fix leaked window when hitting back button while alert being rendered
* CB-7674 Move preference activation back into onCreate()
* CB-7499 Support RTL text direction
* CB-7330 Don't run check_reqs for bin/create.
### 3.6.4 (Sept 30, 2014) ###
* Set VERSION to 3.6.4 (via coho)

View File

@@ -1 +1 @@
3.7.2
3.7.0-dev

View File

@@ -156,26 +156,20 @@ module.exports.check_android = function() {
}
if (!hasAndroidHome && !androidCmdPath) {
if (isWindows) {
// Android Studio 1.0 installer
maybeSetAndroidHome(path.join(process.env['LOCALAPPDATA'], 'Android', 'sdk'));
maybeSetAndroidHome(path.join(process.env['ProgramFiles'], 'Android', 'sdk'));
// Android Studio pre-1.0 installer
// Android Studio installer.
maybeSetAndroidHome(path.join(process.env['LOCALAPPDATA'], 'Android', 'android-studio', 'sdk'));
maybeSetAndroidHome(path.join(process.env['ProgramFiles'], 'Android', 'android-studio', 'sdk'));
// Stand-alone installer
// Stand-alone installer.
maybeSetAndroidHome(path.join(process.env['LOCALAPPDATA'], 'Android', 'android-sdk'));
maybeSetAndroidHome(path.join(process.env['ProgramFiles'], 'Android', 'android-sdk'));
} else if (process.platform == 'darwin') {
// Android Studio 1.0 installer
maybeSetAndroidHome(path.join(process.env['HOME'], 'Library', 'Android', 'sdk'));
// Android Studio pre-1.0 installer
maybeSetAndroidHome('/Applications/Android Studio.app/sdk');
// Stand-alone zip file that user might think to put under /Applications
maybeSetAndroidHome('/Applications/android-sdk-macosx');
maybeSetAndroidHome('/Applications/android-sdk');
}
if (process.env['HOME']) {
// Stand-alone zip file that user might think to put under their home directory
// or their HOME directory.
maybeSetAndroidHome(path.join(process.env['HOME'], 'android-sdk-macosx'));
maybeSetAndroidHome(path.join(process.env['HOME'], 'android-sdk'));
}

View File

@@ -72,7 +72,6 @@ function copyJsAndLibrary(projectPath, shared, projectName) {
shell.cp('-f', path.join(ROOT, 'framework', 'AndroidManifest.xml'), nestedCordovaLibPath);
shell.cp('-f', path.join(ROOT, 'framework', 'project.properties'), nestedCordovaLibPath);
shell.cp('-f', path.join(ROOT, 'framework', 'build.gradle'), nestedCordovaLibPath);
shell.cp('-f', path.join(ROOT, 'framework', 'cordova.gradle'), nestedCordovaLibPath);
shell.cp('-r', path.join(ROOT, 'framework', 'src'), nestedCordovaLibPath);
// Create an eclipse project file and set the name of it to something unique.
// Without this, you can't import multiple CordovaLib projects into the same workspace.
@@ -123,6 +122,7 @@ function copyBuildRules(projectPath) {
shell.cp('-f', path.join(srcDir, 'custom_rules.xml'), projectPath);
shell.cp('-f', path.join(srcDir, 'build.gradle'), projectPath);
shell.cp('-f', path.join(srcDir, 'cordova.gradle'), projectPath);
}
function copyScripts(projectPath) {

View File

@@ -24,7 +24,6 @@ var shell = require('shelljs'),
Q = require('q'),
path = require('path'),
fs = require('fs'),
os = require('os'),
ROOT = path.join(__dirname, '..', '..');
var check_reqs = require('./check_reqs');
var exec = require('./exec');
@@ -69,13 +68,11 @@ function findOutputApksHelper(dir, build_type, arch) {
if (ret.length === 0) {
return ret;
}
// Assume arch-specific build if newest api has -x86 or -arm.
var archSpecific = !!/-x86|-arm/.exec(ret[0]);
// And show only arch-specific ones (or non-arch-specific)
ret = ret.filter(function(p) {
return !!/-x86|-arm/.exec(p) == archSpecific;
});
if (arch && ret.length > 1) {
if (arch) {
ret = ret.filter(function(p) {
return p.indexOf('-' + arch) != -1;
});
@@ -176,20 +173,46 @@ var builders = {
}
},
gradle: {
getArgs: function(cmd, arch, extraArgs) {
if (cmd == 'release') {
cmd = 'cdvBuildRelease';
getArgs: function(cmd, arch) {
var lintSteps;
if (process.env['BUILD_MULTIPLE_APKS']) {
lintSteps = [
'lint',
'lintVitalX86Release',
'lintVitalArmv7Release',
'compileLint',
'copyReleaseLint',
'copyDebugLint'
];
} else {
lintSteps = [
'lint',
'lintVitalRelease',
'compileLint',
'copyReleaseLint',
'copyDebugLint'
];
}
if (arch == 'arm' && cmd == 'debug') {
cmd = 'assembleArmv7Debug';
} else if (arch == 'arm' && cmd == 'release') {
cmd = 'assembleArmv7Release';
} else if (arch == 'x86' && cmd == 'debug') {
cmd = 'assembleX86Debug';
} else if (arch == 'x86' && cmd == 'release') {
cmd = 'assembleX86Release';
} else if (cmd == 'debug') {
cmd = 'cdvBuildDebug';
cmd = 'assembleDebug';
} else if (cmd == 'release') {
cmd = 'assembleRelease';
}
var args = [cmd, '-b', path.join(ROOT, 'build.gradle')];
if (arch) {
args.push('-PcdvBuildArch=' + arch);
}
// 10 seconds -> 6 seconds
args.push('-Dorg.gradle.daemon=true');
args.push.apply(args, extraArgs);
// Excluding lint: 6s-> 1.6s
for (var i = 0; i < lintSteps.length; ++i) {
args.push('-x', lintSteps[i]);
}
// Shaves another 100ms, but produces a "try at own risk" warning. Not worth it (yet):
// args.push('-Dorg.gradle.parallel=true');
return args;
@@ -214,11 +237,10 @@ var builders = {
shell.mkdir('-p', path.join(projectPath, 'gradle'));
shell.cp('-r', path.join(wrapperDir, 'gradle', 'wrapper'), path.join(projectPath, 'gradle'));
// If the gradle distribution URL is set, make sure it points to version we want.
// If the gradle distribution URL is set, make sure it points to version 1.12.
// If it's not set, do nothing, assuming that we're using a future version of gradle that we don't want to mess with.
// For some reason, using ^ and $ don't work. This does the job, though.
var distributionUrlRegex = /distributionUrl.*zip/;
var distributionUrl = 'distributionUrl=http\\://services.gradle.org/distributions/gradle-2.2.1-all.zip';
var distributionUrlRegex = '/^distributionUrl=.*$/';
var distributionUrl = 'distributionUrl=http\\://services.gradle.org/distributions/gradle-1.12-all.zip';
var gradleWrapperPropertiesPath = path.join(projectPath, 'gradle', 'wrapper', 'gradle-wrapper.properties');
shell.sed('-i', distributionUrlRegex, distributionUrl, gradleWrapperPropertiesPath);
@@ -226,9 +248,7 @@ var builders = {
var pluginBuildGradle = path.join(projectPath, 'cordova', 'lib', 'plugin-build.gradle');
var subProjects = extractSubProjectPaths();
for (var i = 0; i < subProjects.length; ++i) {
if (subProjects[i] !== 'CordovaLib') {
shell.cp('-f', pluginBuildGradle, path.join(ROOT, subProjects[i], 'build.gradle'));
}
shell.cp('-f', pluginBuildGradle, path.join(ROOT, subProjects[i], 'build.gradle'));
}
var subProjectsAsGradlePaths = subProjects.map(function(p) { return ':' + p.replace(/[/\\]/g, ':') });
@@ -253,22 +273,20 @@ var builders = {
* Builds the project with gradle.
* Returns a promise.
*/
build: function(build_type, arch, extraArgs) {
build: function(build_type, arch) {
var builder = this;
var wrapper = path.join(ROOT, 'gradlew');
var args = this.getArgs(build_type == 'debug' ? 'debug' : 'release', arch, extraArgs);
var args = this.getArgs(build_type == 'debug' ? 'debug' : 'release', arch);
return Q().then(function() {
console.log('Running: ' + wrapper + ' ' + args.concat(extraArgs).join(' '));
return spawn(wrapper, args);
});
},
clean: function(extraArgs) {
clean: function() {
var builder = this;
var wrapper = path.join(ROOT, 'gradlew');
var args = builder.getArgs('clean', null, extraArgs);
var args = builder.getArgs('clean');
return Q().then(function() {
console.log('Running: ' + wrapper + ' ' + args.concat(extraArgs).join(' '));
return spawn(wrapper, args);
});
},
@@ -303,61 +321,37 @@ function parseOpts(options, resolvedTarget) {
var ret = {
buildType: 'debug',
buildMethod: process.env['ANDROID_BUILD'] || 'ant',
arch: null,
extraArgs: []
};
var multiValueArgs = {
'versionCode': true,
'minSdkVersion': true,
'gradleArg': true
arch: null
};
// Iterate through command line options
for (var i=0; options && (i < options.length); ++i) {
if (/^--/.exec(options[i])) {
var keyValue = options[i].substring(2).split('=');
var flagName = keyValue.shift();
var flagValue = keyValue.join('=');
if (multiValueArgs[flagName] && !flagValue) {
flagValue = options[i + 1];
++i;
}
switch(flagName) {
var option = options[i].substring(2);
switch(option) {
case 'debug':
case 'release':
ret.buildType = flagName;
ret.buildType = option;
break;
case 'ant':
case 'gradle':
ret.buildMethod = flagName;
break;
case 'device':
case 'emulator':
// Don't need to do anything special to when building for device vs emulator.
// iOS uses this flag to switch on architecture.
ret.buildMethod = option;
break;
case 'nobuild' :
ret.buildMethod = 'none';
break;
case 'versionCode':
ret.extraArgs.push('-PcdvVersionCode=' + flagValue);
break;
case 'minSdkVersion':
ret.extraArgs.push('-PcdvMinSdkVersion=' + flagValue);
break;
case 'gradleArg':
ret.extraArgs.push(flagValue);
break;
default :
console.warn('Build option --\'' + flagName + '\' not recognized (ignoring).');
return Q.reject('Build option \'' + options[i] + '\' not recognized.');
}
} else {
console.warn('Build option \'' + options[i] + '\' not recognized (ignoring).');
return Q.reject('Build option \'' + options[i] + '\' not recognized.');
}
}
ret.arch = resolvedTarget && resolvedTarget.arch;
var multiApk = ret.buildMethod == 'gradle' && process.env['BUILD_MULTIPLE_APKS'];
if (multiApk && !/0|false|no/i.exec(multiApk)) {
ret.arch = resolvedTarget && resolvedTarget.arch;
}
return ret;
}
@@ -371,7 +365,7 @@ module.exports.runClean = function(options) {
var builder = builders[opts.buildMethod];
return builder.prepEnv()
.then(function() {
return builder.clean(opts.extraArgs);
return builder.clean();
}).then(function() {
shell.rm('-rf', path.join(ROOT, 'out'));
});
@@ -386,7 +380,7 @@ module.exports.run = function(options, optResolvedTarget) {
var builder = builders[opts.buildMethod];
return builder.prepEnv()
.then(function() {
return builder.build(opts.buildType, opts.arch, opts.extraArgs);
return builder.build(opts.buildType, opts.arch);
}).then(function() {
var apkPaths = builder.findOutputApks(opts.buildType, opts.arch);
console.log('Built the following apk(s):');
@@ -404,44 +398,12 @@ module.exports.run = function(options, optResolvedTarget) {
* Returns "arm" or "x86".
*/
module.exports.detectArchitecture = function(target) {
function helper() {
return exec('adb -s ' + target + ' shell cat /proc/cpuinfo', os.tmpdir())
.then(function(output) {
if (/intel/i.exec(output)) {
return 'x86';
}
return 'arm';
});
}
// It sometimes happens (at least on OS X), that this command will hang forever.
// To fix it, either unplug & replug device, or restart adb server.
return helper().timeout(1000, 'Device communication timed out. Try unplugging & replugging the device.')
.then(null, function(err) {
if (/timed out/.exec('' + err)) {
// adb kill-server doesn't seem to do the trick.
// Could probably find a x-platform version of killall, but I'm not actually
// sure that this scenario even happens on non-OSX machines.
return exec('killall adb')
.then(function() {
console.log('adb seems hung. retrying.');
return helper()
.then(null, function() {
// The double kill is sadly often necessary, at least on mac.
console.log('Now device not found... restarting adb again.');
return exec('killall adb')
.then(function() {
return helper()
.then(null, function() {
return Q.reject('USB is flakey. Try unplugging & replugging the device.');
});
});
});
}, function() {
// For non-killall OS's.
return Q.reject(err);
})
return exec('adb -s ' + target + ' shell cat /proc/cpuinfo')
.then(function(output) {
if (/intel/i.exec(output)) {
return 'x86';
}
throw err;
return 'arm';
});
};
@@ -467,15 +429,12 @@ module.exports.findBestApkForArchitecture = function(buildResults, arch) {
};
module.exports.help = function() {
console.log('Usage: ' + path.relative(process.cwd(), path.join(ROOT, 'cordova', 'build')) + ' [flags]');
console.log('Flags:');
console.log(' \'--debug\': will build project in debug mode (default)');
console.log('Usage: ' + path.relative(process.cwd(), path.join(ROOT, 'cordova', 'build')) + ' [build_type]');
console.log('Build Types : ');
console.log(' \'--debug\': Default build, will build project in debug mode');
console.log(' \'--release\': will build project for release');
console.log(' \'--ant\': will build project with ant (default)');
console.log(' \'--ant\': Default build, will build project with ant');
console.log(' \'--gradle\': will build project with gradle');
console.log(' \'--nobuild\': will skip build process (useful when using run command)');
console.log(' \'--versionCode=#\': Override versionCode for this build. Useful for uploading multiple APKs. Requires --gradle.');
console.log(' \'--minSdkVersion=#\': Override minSdkVersion for this build. Useful for uploading multiple APKs. Requires --gradle.');
console.log(' \'--gradleArg=<gradle command line arg>\': Extra args to pass to the gradle command. Use one flag per arg. Ex. --gradleArg=-PcdvBuildMultipleApks=true');
console.log(' \'--nobuild\': will skip build process (can be used with run command)');
process.exit(0);
};

View File

@@ -22,50 +22,29 @@
var exec = require('./exec'),
Q = require('q'),
path = require('path'),
os = require('os'),
build = require('./build'),
appinfo = require('./appinfo'),
ROOT = path.join(__dirname, '..', '..');
/**
* Returns a promise for the list of the device ID's found
* @param lookHarder When true, try restarting adb if no devices are found.
*/
module.exports.list = function(lookHarder) {
function helper() {
return exec('adb devices', os.tmpdir())
.then(function(output) {
var response = output.split('\n');
var device_list = [];
for (var i = 1; i < response.length; i++) {
if (response[i].match(/\w+\tdevice/) && !response[i].match(/emulator/)) {
device_list.push(response[i].replace(/\tdevice/, '').replace('\r', ''));
}
module.exports.list = function() {
return exec('adb devices')
.then(function(output) {
var response = output.split('\n');
var device_list = [];
for (var i = 1; i < response.length; i++) {
if (response[i].match(/\w+\tdevice/) && !response[i].match(/emulator/)) {
device_list.push(response[i].replace(/\tdevice/, '').replace('\r', ''));
}
return device_list;
});
}
return helper()
.then(function(list) {
if (list.length === 0 && lookHarder) {
// adb kill-server doesn't seem to do the trick.
// Could probably find a x-platform version of killall, but I'm not actually
// sure that this scenario even happens on non-OSX machines.
return exec('killall adb')
.then(function() {
console.log('Restarting adb to see if more devices are detected.');
return helper();
}, function() {
// For non-killall OS's.
return list;
});
}
return list;
return device_list;
});
}
module.exports.resolveTarget = function(target) {
return this.list(true)
return this.list()
.then(function(device_list) {
if (!device_list || !device_list.length) {
return Q.reject('ERROR: Failed to deploy to device, no devices found.');
@@ -101,19 +80,19 @@ module.exports.install = function(target, buildResults) {
console.log('Using apk: ' + apk_path);
console.log('Installing app on device...');
var cmd = 'adb -s ' + resolvedTarget.target + ' install -r "' + apk_path + '"';
return exec(cmd, os.tmpdir())
return exec(cmd)
.then(function(output) {
if (output.match(/Failure/)) return Q.reject('ERROR: Failed to install apk to device: ' + output);
//unlock screen
var cmd = 'adb -s ' + resolvedTarget.target + ' shell input keyevent 82';
return exec(cmd, os.tmpdir());
return exec(cmd);
}, function(err) { return Q.reject('ERROR: Failed to install apk to device: ' + err); })
.then(function() {
// launch the application
console.log('Launching application...');
var cmd = 'adb -s ' + resolvedTarget.target + ' shell am start -W -a android.intent.action.MAIN -n ' + launchName;
return exec(cmd, os.tmpdir());
return exec(cmd);
}).then(function() {
console.log('LAUNCH SUCCESS');
}, function(err) {

View File

@@ -23,7 +23,6 @@ var shell = require('shelljs'),
exec = require('./exec'),
Q = require('q'),
path = require('path'),
os = require('os'),
appinfo = require('./appinfo'),
build = require('./build'),
ROOT = path.join(__dirname, '..', '..'),
@@ -109,7 +108,7 @@ module.exports.best_image = function() {
// Returns a promise.
module.exports.list_started = function() {
return exec('adb devices', os.tmpdir())
return exec('adb devices')
.then(function(output) {
var response = output.split('\n');
var started_emulator_list = [];
@@ -124,7 +123,7 @@ module.exports.list_started = function() {
// Returns a promise.
module.exports.list_targets = function() {
return exec('android list targets', os.tmpdir())
return exec('android list targets')
.then(function(output) {
var target_out = output.split('\n');
var targets = [];
@@ -202,7 +201,7 @@ module.exports.start = function(emulator_ID) {
console.log('BOOT COMPLETE');
//unlock screen
return exec('adb -s ' + emulator_id + ' shell input keyevent 82', os.tmpdir());
return exec('adb -s ' + emulator_id + ' shell input keyevent 82');
}).then(function() {
//return the new emulator id for the started emulators
return emulator_id;
@@ -232,7 +231,7 @@ module.exports.wait_for_emulator = function(num_running) {
*/
module.exports.wait_for_boot = function(emulator_id) {
var self = this;
return exec('adb -s ' + emulator_id + ' shell getprop init.svc.bootanim', os.tmpdir())
return exec('adb -s ' + emulator_id + ' shell getprop init.svc.bootanim')
.then(function(output) {
if (output.match(/stopped/)) {
return;
@@ -310,7 +309,7 @@ module.exports.install = function(target, buildResults) {
var apk_path = build.findBestApkForArchitecture(buildResults, resolvedTarget.arch);
console.log('Installing app on emulator...');
console.log('Using apk: ' + apk_path);
return exec('adb -s ' + resolvedTarget.target + ' install -r "' + apk_path + '"', os.tmpdir())
return exec('adb -s ' + resolvedTarget.target + ' install -r "' + apk_path + '"')
.then(function(output) {
if (output.match(/Failure/)) {
return Q.reject('Failed to install apk to emulator: ' + output);
@@ -320,13 +319,13 @@ module.exports.install = function(target, buildResults) {
return Q.reject('Failed to install apk to emulator: ' + err);
}).then(function() {
//unlock screen
return exec('adb -s ' + resolvedTarget.target + ' shell input keyevent 82', os.tmpdir());
return exec('adb -s ' + resolvedTarget.target + ' shell input keyevent 82');
}).then(function() {
// launch the application
console.log('Launching application...');
var launchName = appinfo.getActivityName();
cmd = 'adb -s ' + resolvedTarget.target + ' shell am start -W -a android.intent.action.MAIN -n ' + launchName;
return exec(cmd, os.tmpdir());
return exec(cmd);
}).then(function(output) {
console.log('LAUNCH SUCCESS');
}, function(err) {

View File

@@ -21,7 +21,6 @@
var shell = require('shelljs'),
path = require('path'),
os = require('os'),
Q = require('q'),
child_process = require('child_process'),
ROOT = path.join(__dirname, '..', '..');
@@ -33,7 +32,7 @@ var shell = require('shelljs'),
module.exports.run = function() {
var cmd = 'adb logcat | grep -v nativeGetEnabledTags';
var d = Q.defer();
var adb = child_process.spawn('adb', ['logcat'], {cwd: os.tmpdir()});
var adb = child_process.spawn('adb', ['logcat']);
adb.stdout.on('data', function(data) {
var lines = data ? data.toString().split('\n') : [];

View File

@@ -23,22 +23,8 @@ buildscript {
mavenCentral()
}
// Switch the Android Gradle plugin version requirement depending on the
// installed version of Gradle. This dependency is documented at
// http://tools.android.com/tech-docs/new-build-system/version-compatibility
// and https://issues.apache.org/jira/browse/CB-8143
if (gradle.gradleVersion >= "2.2") {
dependencies {
classpath 'com.android.tools.build:gradle:1.0.0+'
}
} else if (gradle.gradleVersion >= "2.1") {
dependencies {
classpath 'com.android.tools.build:gradle:0.14.0+'
}
} else {
dependencies {
classpath 'com.android.tools.build:gradle:0.12.0+'
}
dependencies {
classpath 'com.android.tools.build:gradle:0.12.+'
}
}
@@ -46,18 +32,16 @@ apply plugin: 'android-library'
dependencies {
compile fileTree(dir: 'libs', include: '*.jar')
debugCompile project(path: ":CordovaLib", configuration: "debug")
releaseCompile project(path: ":CordovaLib", configuration: "release")
}
android {
compileSdkVersion cdvCompileSdkVersion
buildToolsVersion cdvBuildToolsVersion
compileSdkVersion cordova.cordovaSdkVersion
buildToolsVersion cordova.cordovaBuildToolsVersion
publishNonDefault true
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_6
targetCompatibility JavaVersion.VERSION_1_6
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}
sourceSets {

View File

@@ -23,12 +23,11 @@ var path = require('path'),
build = require('./build'),
emulator = require('./emulator'),
device = require('./device'),
shell = require('shelljs'),
Q = require('q');
/*
* Runs the application on a device if available.
* If no device is found, it will use a started emulator.
* If not device is found, it will use a started emulator.
* If no started emulators are found it will attempt to start an avd.
* If no avds are found it will error out.
* Returns a promise.
@@ -36,51 +35,26 @@ var path = require('path'),
module.exports.run = function(args) {
var buildFlags = [];
var install_target;
var list = false;
for (var i=2; i<args.length; i++) {
if (/^--(debug|release|ant|gradle|nobuild|versionCode=|minSdkVersion=|gradleArg=)/.exec(args[i])) {
buildFlags.push(args[i]);
if (args[i] == '--debug') {
buildFlags.push('--debug');
} else if (args[i] == '--release') {
buildFlags.push('--release');
} else if (args[i] == '--nobuild') {
buildFlags.push('--nobuild');
} else if (args[i] == '--device') {
install_target = '--device';
} else if (args[i] == '--emulator') {
install_target = '--emulator';
} else if (/^--target=/.exec(args[i])) {
} else if (args[i].substring(0, 9) == '--target=') {
install_target = args[i].substring(9, args[i].length);
} else if (args[i] == '--list') {
list = true;
} else {
console.warn('Option \'' + args[i] + '\' not recognized (ignoring).');
console.error('ERROR : Run option \'' + args[i] + '\' not recognized.');
process.exit(2);
}
}
if (list) {
var output = '';
var temp = '';
if (!install_target) {
output += 'Available Android Devices:\n';
temp = shell.exec(path.join(__dirname, 'list-devices'), {silent:true}).output;
temp = temp.replace(/^(?=[^\s])/gm, '\t');
output += temp;
output += 'Available Android Virtual Devices:\n';
temp = shell.exec(path.join(__dirname, 'list-emulator-images'), {silent:true}).output;
temp = temp.replace(/^(?=[^\s])/gm, '\t');
output += temp;
} else if (install_target == '--emulator') {
output += 'Available Android Virtual Devices:\n';
temp = shell.exec(path.join(__dirname, 'list-emulator-images'), {silent:true}).output;
temp = temp.replace(/^(?=[^\s])/gm, '\t');
output += temp;
} else if (install_target == '--device') {
output += 'Available Android Devices:\n';
temp = shell.exec(path.join(__dirname, 'list-devices'), {silent:true}).output;
temp = temp.replace(/^(?=[^\s])/gm, '\t');
output += temp;
}
console.log(output);
return;
}
return Q()
.then(function() {
if (!install_target) {

View File

@@ -26,15 +26,13 @@ var isWindows = process.platform.slice(0, 3) == 'win';
// Takes a command and optional current working directory.
module.exports = function(cmd, args, opt_cwd) {
var d = Q.defer();
var opts = { cwd: opt_cwd, stdio: 'inherit' };
try {
// Work around spawn not being able to find .bat files.
if (isWindows) {
args = [['/s', '/c', '"' + [cmd].concat(args).map(function(a){if (/^[^"].* .*[^"]/.test(a)) return '"' + a + '"'; return a;}).join(' ')+'"'].join(' ')];
cmd = 'cmd';
opts.windowsVerbatimArguments = true;
args.unshift('/s', '/c', cmd);
cmd = 'cmd';
}
var child = child_process.spawn(cmd, args, opts);
var child = child_process.spawn(cmd, args, {cwd: opt_cwd, stdio: 'inherit'});
child.on('exit', function(code) {
if (code) {
d.reject('Error code ' + code + ' for command: ' + cmd + ' with args: ' + args);
@@ -47,4 +45,5 @@ module.exports = function(cmd, args, opt_cwd) {
d.reject(e);
}
return d.promise;
};
}

View File

@@ -20,6 +20,6 @@
*/
// Coho updates this line:
var VERSION = "3.7.2";
var VERSION = "3.7.0-dev";
console.log(VERSION);

View File

@@ -19,6 +19,11 @@
// GENERATED FILE! DO NOT EDIT!
import java.util.regex.Pattern
import groovy.swing.SwingBuilder
ext.cordova = {}
apply from: 'cordova.gradle', to: ext.cordova
apply plugin: 'android'
buildscript {
@@ -26,117 +31,16 @@ buildscript {
mavenCentral()
}
// Switch the Android Gradle plugin version requirement depending on the
// installed version of Gradle. This dependency is documented at
// http://tools.android.com/tech-docs/new-build-system/version-compatibility
// and https://issues.apache.org/jira/browse/CB-8143
if (gradle.gradleVersion >= "2.2") {
dependencies {
classpath 'com.android.tools.build:gradle:1.0.0+'
}
} else if (gradle.gradleVersion >= "2.1") {
dependencies {
classpath 'com.android.tools.build:gradle:0.14.0+'
}
} else {
dependencies {
classpath 'com.android.tools.build:gradle:0.12.0+'
}
dependencies {
classpath 'com.android.tools.build:gradle:0.12.0+'
}
}
// Allow plugins to declare Maven dependencies via build-extras.gradle.
repositories {
mavenCentral()
}
task wrapper(type: Wrapper) {
gradleVersion = '2.2.1'
gradleVersion = '1.12'
}
// Configuration properties. Set these via environment variables, build-extras.gradle, or gradle.properties.
// Refer to: http://www.gradle.org/docs/current/userguide/tutorial_this_and_that.html
ext {
apply from: 'CordovaLib/cordova.gradle'
// The value for android.compileSdkVersion.
if (!project.hasProperty('cdvCompileSdkVersion')) {
cdvCompileSdkVersion = privateHelpers.getProjectTarget()
}
// The value for android.buildToolsVersion.
if (!project.hasProperty('cdvBuildToolsVersion')) {
cdvBuildToolsVersion = privateHelpers.findLatestInstalledBuildTools()
}
// Sets the versionCode to the given value.
if (!project.hasProperty('cdvVersionCode')) {
cdvVersionCode = null
}
// Sets the minSdkVersion to the given value.
if (!project.hasProperty('cdvMinSdkVersion')) {
cdvMinSdkVersion = null
}
// Whether to build architecture-specific APKs.
if (!project.hasProperty('cdvBuildMultipleApks')) {
cdvBuildMultipleApks = false
}
// .properties files to use for release signing.
if (!project.hasProperty('cdvReleaseSigningPropertiesFile')) {
cdvReleaseSigningPropertiesFile = null
}
// .properties files to use for debug signing.
if (!project.hasProperty('cdvDebugSigningPropertiesFile')) {
cdvDebugSigningPropertiesFile = null
}
// Set by build.js script.
if (!project.hasProperty('cdvBuildArch')) {
cdvBuildArch = null
}
}
def hasBuildExtras = file('build-extras.gradle').exists()
if (hasBuildExtras) {
apply from: 'build-extras.gradle'
}
def computeBuildTargetName(debugBuild) {
def ret = 'assemble'
if (cdvBuildMultipleApks && cdvBuildArch) {
def arch = cdvBuildArch == 'arm' ? 'armv7' : cdvBuildArch
ret += '' + arch.toUpperCase().charAt(0) + arch.substring(1);
}
return ret + (debugBuild ? 'Debug' : 'Release')
}
// Make cdvBuild a task that depends on the debug/arch-sepecific task.
task cdvBuildDebug
cdvBuildDebug.dependsOn {
return computeBuildTargetName(true)
}
task cdvBuildRelease
cdvBuildRelease.dependsOn {
return computeBuildTargetName(false)
}
task cdvPrintProps << {
println('cdvCompileSdkVersion=' + cdvCompileSdkVersion)
println('cdvBuildToolsVersion=' + cdvBuildToolsVersion)
println('cdvVersionCode=' + cdvVersionCode)
println('cdvMinSdkVersion=' + cdvMinSdkVersion)
println('cdvBuildMultipleApks=' + cdvBuildMultipleApks)
println('cdvReleaseSigningPropertiesFile=' + cdvReleaseSigningPropertiesFile)
println('cdvDebugSigningPropertiesFile=' + cdvDebugSigningPropertiesFile)
println('cdvBuildArch=' + cdvBuildArch)
println('computedVersionCode=' + android.defaultConfig.versionCode)
if (android.productFlavors.has('armv7')) {
println('computedArmv7VersionCode=' + android.productFlavors.armv7.versionCode)
}
if (android.productFlavors.has('x86')) {
println('computedx86VersionCode=' + android.productFlavors.x86.versionCode)
}
}
// PLUGIN GRADLE EXTENSIONS START
// PLUGIN GRADLE EXTENSIONS END
ext.multiarch=false
android {
sourceSets {
@@ -151,29 +55,23 @@ android {
}
}
def versionCodeOverride = cdvVersionCode ? Integer.parseInt(cdvVersionCode) : null
def minSdkVersionOverride = cdvMinSdkVersion ? Integer.parseInt(cdvMinSdkVersion) : null
defaultConfig {
versionCode versionCodeOverride ?: Integer.parseInt("" + privateHelpers.extractIntFromManifest("versionCode") + "0")
if (minSdkVersionOverride != null) {
minSdkVersion minSdkVersionOverride
}
versionCode Integer.parseInt(System.env.ANDROID_VERSION_CODE ?: ("" + getVersionCodeFromManifest() + "0"))
}
compileSdkVersion cdvCompileSdkVersion
buildToolsVersion cdvBuildToolsVersion
compileSdkVersion cordova.cordovaSdkVersion
buildToolsVersion cordova.cordovaBuildToolsVersion
if (Boolean.valueOf(cdvBuildMultipleApks)) {
if (multiarch || System.env.BUILD_MULTIPLE_APKS) {
productFlavors {
armv7 {
versionCode versionCodeOverride ?: defaultConfig.versionCode + 2
versionCode defaultConfig.versionCode + 2
ndk {
abiFilters "armeabi-v7a", ""
}
}
x86 {
versionCode versionCodeOverride ?: defaultConfig.versionCode + 4
versionCode defaultConfig.versionCode + 4
ndk {
abiFilters "x86", ""
}
@@ -184,31 +82,21 @@ android {
}
}
}
} else if (!versionCodeOverride) {
def minSdkVersion = minSdkVersionOverride ?: privateHelpers.extractIntFromManifest("minSdkVersion")
// Vary versionCode by the two most common API levels:
// 14 is ICS, which is the lowest API level for many apps.
// 20 is Lollipop, which is the lowest API level for the updatable system webview.
if (minSdkVersion >= 20) {
defaultConfig.versionCode += 9
} else if (minSdkVersion >= 14) {
defaultConfig.versionCode += 8
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_6
targetCompatibility JavaVersion.VERSION_1_6
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}
if (cdvReleaseSigningPropertiesFile) {
if (System.env.RELEASE_SIGNING_PROPERTIES_FILE) {
signingConfigs {
release {
// These must be set or Gradle will complain (even if they are overridden).
keyAlias = ""
keyPassword = "__unset" // And these must be set to non-empty in order to have the signing step added to the task graph.
keyPassword = ""
storeFile = null
storePassword = "__unset"
storePassword = ""
}
}
buildTypes {
@@ -216,10 +104,10 @@ android {
signingConfig signingConfigs.release
}
}
addSigningProps(cdvReleaseSigningPropertiesFile, signingConfigs.release)
addSigningProps(System.env.RELEASE_SIGNING_PROPERTIES_FILE, signingConfigs.release)
}
if (cdvDebugSigningPropertiesFile) {
addSigningProps(cdvDebugSigningPropertiesFile, signingConfigs.debug)
if (System.env.DEBUG_SIGNING_PROPERTIES_FILE) {
addSigningProps(System.env.DEBUG_SIGNING_PROPERTIES_FILE, signingConfigs.debug)
}
}
@@ -229,15 +117,41 @@ dependencies {
// SUB-PROJECT DEPENDENCIES END
}
def promptForPassword(msg) {
if (System.console() == null) {
def ret = null
new SwingBuilder().edt {
dialog(modal: true, title: 'Enter password', alwaysOnTop: true, resizable: false, locationRelativeTo: null, pack: true, show: true) {
vbox {
label(text: msg)
def input = passwordField()
button(defaultButton: true, text: 'OK', actionPerformed: {
ret = input.password;
dispose();
})
}
}
}
if (!ret) {
throw new GradleException('User canceled build')
}
return new String(ret)
} else {
return System.console().readPassword('\n' + msg);
}
}
def promptForReleaseKeyPassword() {
if (!cdvReleaseSigningPropertiesFile) {
if (!System.env.RELEASE_SIGNING_PROPERTIES_FILE) {
return;
}
if ('__unset'.equals(android.signingConfigs.release.storePassword)) {
android.signingConfigs.release.storePassword = privateHelpers.promptForPassword('Enter key store password: ')
if (!android.signingConfigs.release.storePassword) {
android.signingConfigs.release.storePassword = promptForPassword('Enter key store password: ')
println('set to:' + android.signingConfigs.release.storePassword)
}
if ('__unset'.equals(android.signingConfigs.release.keyPassword)) {
android.signingConfigs.release.keyPassword = privateHelpers.promptForPassword('Enter key password: ');
if (!android.signingConfigs.release.keyPassword) {
android.signingConfigs.release.keyPassword = promptForPassword('Enter key password: ');
}
}
@@ -249,23 +163,38 @@ gradle.taskGraph.whenReady { taskGraph ->
}
}
def getVersionCodeFromManifest() {
def manifestFile = file(android.sourceSets.main.manifest.srcFile)
def pattern = Pattern.compile("versionCode=\"(\\d+)\"")
def matcher = pattern.matcher(manifestFile.getText())
matcher.find()
return Integer.parseInt(matcher.group(1))
}
def ensureValueExists(filePath, props, key) {
if (props.get(key) == null) {
throw new GradleException(filePath + ': Missing key required "' + key + '"')
}
return props.get(key)
}
def addSigningProps(propsFilePath, signingConfig) {
def propsFile = file(propsFilePath)
def props = new Properties()
propsFile.withReader { reader ->
props.load(reader)
}
def storeFile = new File(privateHelpers.ensureValueExists(propsFilePath, props, 'storeFile'))
def storeFile = new File(ensureValueExists(propsFilePath, props, 'storeFile'))
if (!storeFile.isAbsolute()) {
storeFile = RelativePath.parse(true, storeFile.toString()).getFile(propsFile.getParentFile())
}
if (!storeFile.exists()) {
throw new FileNotFoundException('Keystore file does not exist: ' + storeFile.getAbsolutePath())
}
signingConfig.keyAlias = privateHelpers.ensureValueExists(propsFilePath, props, 'keyAlias')
signingConfig.keyPassword = props.get('keyPassword', signingConfig.keyPassword)
signingConfig.keyAlias = ensureValueExists(propsFilePath, props, 'keyAlias')
signingConfig.keyPassword = props.get('keyPassword')
signingConfig.storeFile = storeFile
signingConfig.storePassword = props.get('storePassword', signingConfig.storePassword)
signingConfig.storePassword = props.get('storePassword')
def storeType = props.get('storeType')
if (!storeType) {
def filename = storeFile.getName().toLowerCase();
@@ -278,8 +207,9 @@ def addSigningProps(propsFilePath, signingConfig) {
}
}
// This can be defined within build-extras.gradle as:
// ext.postBuildExtras = { ... code here ... }
if (hasProperty('postBuildExtras')) {
postBuildExtras()
// PLUGIN GRADLE EXTENSIONS START
// PLUGIN GRADLE EXTENSIONS END
if (file('build-extras.gradle').exists()) {
apply from: 'build-extras.gradle'
}

View File

@@ -18,21 +18,16 @@
*/
import java.util.regex.Pattern
import groovy.swing.SwingBuilder
String doEnsureValueExists(filePath, props, key) {
if (props.get(key) == null) {
throw new GradleException(filePath + ': Missing key required "' + key + '"')
String getProjectTarget(String defaultTarget) {
def manifestFile = file("project.properties")
def pattern = Pattern.compile("target\\s*=\\s*(.*)")
def matcher = pattern.matcher(manifestFile.getText())
if (matcher.find()) {
matcher.group(1)
} else {
defaultTarget
}
return props.get(key)
}
String doGetProjectTarget() {
def props = new Properties()
file('project.properties').withReader { reader ->
props.load(reader)
}
return doEnsureValueExists('project.properties', props, 'target')
}
String[] getAvailableBuildTools() {
@@ -42,7 +37,7 @@ String[] getAvailableBuildTools() {
.sort { a, b -> compareVersions(b, a) }
}
String doFindLatestInstalledBuildTools(String minBuildToolsVersion) {
String latestBuildToolsAvailable(String minBuildToolsVersion) {
def availableBuildToolsVersions
try {
availableBuildToolsVersions = getAvailableBuildTools()
@@ -120,46 +115,6 @@ String getAndroidSdkDir() {
androidSdkDir
}
def doExtractIntFromManifest(name) {
def manifestFile = file(android.sourceSets.main.manifest.srcFile)
def pattern = Pattern.compile(name + "=\"(\\d+)\"")
def matcher = pattern.matcher(manifestFile.getText())
matcher.find()
return Integer.parseInt(matcher.group(1))
}
def doPromptForPassword(msg) {
if (System.console() == null) {
def ret = null
new SwingBuilder().edt {
dialog(modal: true, title: 'Enter password', alwaysOnTop: true, resizable: false, locationRelativeTo: null, pack: true, show: true) {
vbox {
label(text: msg)
def input = passwordField()
button(defaultButton: true, text: 'OK', actionPerformed: {
ret = input.password;
dispose();
})
}
}
}
if (!ret) {
throw new GradleException('User canceled build')
}
return new String(ret)
} else {
return System.console().readPassword('\n' + msg);
}
}
// Properties exported here are visible to all plugins.
ext {
// These helpers are shared, but are not guaranteed to be stable / unchanged.
privateHelpers = {}
privateHelpers.getProjectTarget = { doGetProjectTarget() }
privateHelpers.findLatestInstalledBuildTools = { doFindLatestInstalledBuildTools('19.1.0') }
privateHelpers.extractIntFromManifest = { name -> doExtractIntFromManifest(name) }
privateHelpers.promptForPassword = { msg -> doPromptForPassword(msg) }
privateHelpers.ensureValueExists = { filePath, props, key -> doEnsureValueExists(filePath, props, key) }
}
ext.cordovaSdkVersion = System.env.MIN_SDK_VERSION ?: getProjectTarget("android-19")
ext.cordovaBuildToolsVersion = latestBuildToolsAvailable("19.1.0")

View File

@@ -1,5 +1,5 @@
// Platform: android
// 24ab6855470f2dc0662624b597c98585e56a1666
// 1fc2526faa6197e1637ecb48ebe0f876f008ba0f
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
@@ -19,7 +19,7 @@
under the License.
*/
;(function() {
var PLATFORM_VERSION_BUILD_LABEL = '3.7.2';
var PLATFORM_VERSION_BUILD_LABEL = '3.7.0-dev';
// file: src/scripts/require.js
/*jshint -W079 */
@@ -284,16 +284,10 @@ var cordova = {
if (callback) {
if (isSuccess && status == cordova.callbackStatus.OK) {
callback.success && callback.success.apply(null, args);
} else if (!isSuccess) {
} else {
callback.fail && callback.fail.apply(null, args);
}
/*
else
Note, this case is intentionally not caught.
this can happen if isSuccess is true, but callbackStatus is NO_RESULT
which is used to remove a callback from the list without calling the callbacks
typically keepCallback is false in this case
*/
// Clear callback if not expecting any more results
if (!keepCallback) {
delete cordova.callbacks[callbackId];
@@ -515,14 +509,9 @@ function each(objects, func, context) {
function clobber(obj, key, value) {
exports.replaceHookForTesting(obj, key);
var needsProperty = false;
try {
obj[key] = value;
} catch (e) {
needsProperty = true;
}
obj[key] = value;
// Getters can only be overridden by getters.
if (needsProperty || obj[key] !== value) {
if (obj[key] !== value) {
utils.defineGetter(obj, key, function() {
return value;
});
@@ -637,6 +626,7 @@ var utils = require('cordova/utils'),
* onDeviceReady* User event fired to indicate that Cordova is ready
* onResume User event fired to indicate a start/resume lifecycle event
* onPause User event fired to indicate a pause lifecycle event
* onDestroy* Internal event fired when app is being destroyed (User should use window.onunload event, not this one).
*
* The events marked with an * are sticky. Once they have fired, they will stay in the fired state.
* All listeners that subscribe after the event is fired will be executed right away.
@@ -848,6 +838,9 @@ channel.create('onResume');
// Event to indicate a pause lifecycle event
channel.create('onPause');
// Event to indicate a destroy lifecycle event
channel.createSticky('onDestroy');
// Channels that must fire before "deviceready" is fired.
channel.waitForInitialization('onCordovaReady');
channel.waitForInitialization('onDOMContentLoaded');
@@ -1034,7 +1027,12 @@ function buildPayload(payload, message) {
payload.push(+message.slice(1));
} else if (payloadKind == 'A') {
var data = message.slice(1);
payload.push(base64.toArrayBuffer(data));
var bytes = window.atob(data);
var arraybuffer = new Uint8Array(bytes.length);
for (var i = 0; i < bytes.length; i++) {
arraybuffer[i] = bytes.charCodeAt(i);
}
payload.push(arraybuffer.buffer);
} else if (payloadKind == 'S') {
payload.push(window.atob(message.slice(1)));
} else if (payloadKind == 'M') {
@@ -1169,7 +1167,6 @@ var cordova = require('cordova');
var modulemapper = require('cordova/modulemapper');
var platform = require('cordova/platform');
var pluginloader = require('cordova/pluginloader');
var utils = require('cordova/utils');
var platformInitChannelsArray = [channel.onNativeReady, channel.onPluginsReady];
@@ -1201,19 +1198,21 @@ function replaceNavigator(origNavigator) {
for (var key in origNavigator) {
if (typeof origNavigator[key] == 'function') {
newNavigator[key] = origNavigator[key].bind(origNavigator);
}
else {
} else {
(function(k) {
utils.defineGetterSetter(newNavigator,key,function() {
return origNavigator[k];
});
})(key);
Object.defineProperty(newNavigator, k, {
get: function() {
return origNavigator[k];
},
configurable: true,
enumerable: true
});
})(key);
}
}
}
return newNavigator;
}
if (window.navigator) {
window.navigator = replaceNavigator(window.navigator);
}
@@ -1294,7 +1293,6 @@ define("cordova/init_b", function(require, exports, module) {
var channel = require('cordova/channel');
var cordova = require('cordova');
var platform = require('cordova/platform');
var utils = require('cordova/utils');
var platformInitChannelsArray = [channel.onDOMContentLoaded, channel.onNativeReady];
@@ -1329,13 +1327,16 @@ function replaceNavigator(origNavigator) {
for (var key in origNavigator) {
if (typeof origNavigator[key] == 'function') {
newNavigator[key] = origNavigator[key].bind(origNavigator);
}
else {
} else {
(function(k) {
utils.defineGetterSetter(newNavigator,key,function() {
return origNavigator[k];
});
})(key);
Object.defineProperty(newNavigator, k, {
get: function() {
return origNavigator[k];
},
configurable: true,
enumerable: true
});
})(key);
}
}
}
@@ -1384,7 +1385,7 @@ platform.bootstrap && platform.bootstrap();
* Create all cordova objects once native side is ready.
*/
channel.join(function() {
platform.initialize && platform.initialize();
// Fire event to notify that all objects are created
@@ -1519,14 +1520,12 @@ module.exports = {
// TODO: Extract this as a proper plugin.
modulemapper.clobbers('cordova/plugin/android/app', 'navigator.app');
var APP_PLUGIN_NAME = Number(cordova.platformVersion.split('.')[0]) >= 4 ? 'CoreAndroid' : 'App';
// Inject a listener for the backbutton on the document.
var backButtonChannel = cordova.addDocumentEventHandler('backbutton');
backButtonChannel.onHasSubscribersChange = function() {
// If we just attached the first handler or detached the last handler,
// let native know we need to override the back button.
exec(null, null, APP_PLUGIN_NAME, "overrideBackbutton", [this.numHandlers == 1]);
exec(null, null, "App", "overrideBackbutton", [this.numHandlers == 1]);
};
// Add hardware MENU and SEARCH button handlers
@@ -1537,7 +1536,7 @@ module.exports = {
// generic button bind used for volumeup/volumedown buttons
var volumeButtonChannel = cordova.addDocumentEventHandler(buttonName + 'button');
volumeButtonChannel.onHasSubscribersChange = function() {
exec(null, null, APP_PLUGIN_NAME, "overrideButton", [buttonName, this.numHandlers == 1]);
exec(null, null, "App", "overrideButton", [buttonName, this.numHandlers == 1]);
};
}
// Inject a listener for the volume buttons on the document.
@@ -1547,38 +1546,11 @@ module.exports = {
// Let native code know we are all done on the JS side.
// Native code will then un-hide the WebView.
channel.onCordovaReady.subscribe(function() {
exec(onMessageFromNative, null, APP_PLUGIN_NAME, 'messageChannel', []);
exec(null, null, APP_PLUGIN_NAME, "show", []);
exec(null, null, "App", "show", []);
});
}
};
function onMessageFromNative(msg) {
var cordova = require('cordova');
var action = msg.action;
switch (action)
{
// Button events
case 'backbutton':
case 'menubutton':
case 'searchbutton':
// App life cycle events
case 'pause':
case 'resume':
// Keyboard events
case 'hidekeyboard':
case 'showkeyboard':
// Volume events
case 'volumedownbutton':
case 'volumeupbutton':
cordova.fireDocumentEvent(action);
break;
default:
throw new Error('Unknown event action ' + action);
}
}
});
// file: src/android/plugin/android/app.js
@@ -1977,4 +1949,4 @@ window.cordova = require('cordova');
require('cordova/init');
})();
})();

View File

@@ -23,35 +23,30 @@ buildscript {
mavenCentral()
}
// Switch the Android Gradle plugin version requirement depending on the
// installed version of Gradle. This dependency is documented at
// http://tools.android.com/tech-docs/new-build-system/version-compatibility
// and https://issues.apache.org/jira/browse/CB-8143
if (gradle.gradleVersion >= "2.2") {
dependencies {
classpath 'com.android.tools.build:gradle:1.0.0+'
}
} else if (gradle.gradleVersion >= "2.1") {
dependencies {
classpath 'com.android.tools.build:gradle:0.14.0+'
}
} else {
dependencies {
classpath 'com.android.tools.build:gradle:0.12.0+'
}
dependencies {
// This should be updated with each cordova-android release.
// It can affect things like where the .apk is generated.
// It also dictates what the minimum android build-tools version
// that you need (Set in bin/templates/project/cordova.gradle).
// Make sure the value is the same in all locations:
// * framework/build.gradle
// * bin/templates/project/cordova.gradle
// * bin/templates/cordova/lib/plugin-build.gradle
// * distributionUrl within bin/templates/cordova/lib/build.js.
classpath 'com.android.tools.build:gradle:0.12.+'
}
}
apply plugin: 'android-library'
android {
compileSdkVersion cdvCompileSdkVersion
buildToolsVersion cdvBuildToolsVersion
compileSdkVersion cordova.cordovaSdkVersion
buildToolsVersion cordova.cordovaBuildToolsVersion
publishNonDefault true
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_6
targetCompatibility JavaVersion.VERSION_1_6
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}
sourceSets {

View File

@@ -10,7 +10,7 @@
# Indicates whether an apk should be generated for each density.
split.density=false
# Project target.
target=android-21
target=android-19
apk-configurations=
renderscript.opt.level=O0
android.library=true

View File

@@ -41,19 +41,8 @@ import java.util.HashMap;
*/
public class App extends CordovaPlugin {
public static final String PLUGIN_NAME = "App";
protected static final String TAG = "CordovaApp";
private BroadcastReceiver telephonyReceiver;
private CallbackContext messageChannel;
/**
* Send an event to be fired on the Javascript side.
*
* @param action The name of the event to be fired
*/
public void fireJavascriptEvent(String action) {
sendEventMessage(action);
}
/**
* Sets the context of the Command. This can then be used to do things like
@@ -111,11 +100,6 @@ public class App extends CordovaPlugin {
else if (action.equals("exitApp")) {
this.exitApp();
}
else if (action.equals("messageChannel")) {
messageChannel = callbackContext;
return true;
}
callbackContext.sendPluginResult(new PluginResult(status, result));
return true;
} catch (JSONException e) {
@@ -265,7 +249,7 @@ public class App extends CordovaPlugin {
public void exitApp() {
this.webView.postMessage("exit", null);
}
/**
* Listen for telephony events: RINGING, OFFHOOK and IDLE
@@ -303,21 +287,7 @@ public class App extends CordovaPlugin {
};
// Register the receiver
webView.getContext().registerReceiver(this.telephonyReceiver, intentFilter);
}
private void sendEventMessage(String action) {
JSONObject obj = new JSONObject();
try {
obj.put("action", action);
} catch (JSONException e) {
LOG.e(TAG, "Failed to create event message", e);
}
PluginResult pluginResult = new PluginResult(PluginResult.Status.OK, obj);
pluginResult.setKeepCallback(true);
if (messageChannel != null) {
messageChannel.sendPluginResult(pluginResult);
}
this.cordova.getActivity().registerReceiver(this.telephonyReceiver, intentFilter);
}
/*
@@ -326,6 +296,6 @@ public class App extends CordovaPlugin {
*/
public void onDestroy()
{
webView.getContext().unregisterReceiver(this.telephonyReceiver);
this.cordova.getActivity().unregisterReceiver(this.telephonyReceiver);
}
}

View File

@@ -37,6 +37,7 @@ public class Config {
parser = new ConfigXmlParser();
parser.parse(action);
parser.getPreferences().setPreferencesBundle(action.getIntent().getExtras());
parser.getPreferences().copyIntoIntentExtras(action);
}
// Intended to be used for testing only; creates an empty configuration.

View File

@@ -20,23 +20,28 @@ package org.apache.cordova;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Locale;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.json.JSONArray;
import org.apache.cordova.CordovaInterface;
import org.apache.cordova.CordovaPlugin;
import org.apache.cordova.LOG;
import org.json.JSONException;
import org.json.JSONObject;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Color;
import android.media.AudioManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.Display;
import android.view.KeyEvent;
@@ -47,6 +52,7 @@ import android.view.ViewGroup;
import android.view.ViewParent;
import android.view.Window;
import android.view.WindowManager;
import android.webkit.ValueCallback;
import android.webkit.WebViewClient;
import android.widget.LinearLayout;
@@ -93,6 +99,7 @@ public class CordovaActivity extends Activity implements CordovaInterface {
@Deprecated // Will be removed. Use findViewById() to retrieve views.
protected LinearLayout root;
protected ProgressDialog spinnerDialog = null;
private final ExecutorService threadPool = Executors.newCachedThreadPool();
private static int ACTIVITY_STARTING = 0;
@@ -101,8 +108,7 @@ public class CordovaActivity extends Activity implements CordovaInterface {
private int activityState = 0; // 0=starting, 1=running (after 1st resume), 2=shutting down
// Plugin to call when activity result is received
protected int activityResultRequestCode;
protected CordovaPlugin activityResultCallback;
protected CordovaPlugin activityResultCallback = null;
protected boolean activityResultKeepRunning;
/*
@@ -110,10 +116,9 @@ public class CordovaActivity extends Activity implements CordovaInterface {
*/
// Draw a splash screen using an image located in the drawable resource directory.
@Deprecated // Use "SplashScreen" preference instead.
// This is not the same as calling super.loadSplashscreen(url)
protected int splashscreen = 0;
@Deprecated // Use "SplashScreenDelay" preference instead.
protected int splashscreenTime = -1;
protected int splashscreenTime = 3000;
// LoadUrl timeout value in msec (default of 20 sec)
protected int loadUrlTimeoutValue = 20000;
@@ -232,6 +237,7 @@ public class CordovaActivity extends Activity implements CordovaInterface {
parser.parse(this);
preferences = parser.getPreferences();
preferences.setPreferencesBundle(getIntent().getExtras());
preferences.copyIntoIntentExtras(this);
internalWhitelist = parser.getInternalWhitelist();
externalWhitelist = parser.getExternalWhitelist();
launchUrl = parser.getLaunchUrl();
@@ -260,6 +266,9 @@ public class CordovaActivity extends Activity implements CordovaInterface {
ViewGroup.LayoutParams.MATCH_PARENT,
1.0F));
// Add web view but make it invisible while loading URL
appView.setVisibility(View.INVISIBLE);
// need to remove appView from any existing parent before invoking root.addView(appView)
ViewParent parent = appView.getParent();
if ((parent != null) && (parent != root)) {
@@ -324,14 +333,12 @@ public class CordovaActivity extends Activity implements CordovaInterface {
public void init(CordovaWebView webView, CordovaWebViewClient webViewClient, CordovaChromeClient webChromeClient) {
LOG.d(TAG, "CordovaActivity.init()");
if (splashscreenTime >= 0) {
preferences.set("SplashScreenDelay", splashscreenTime);
}
if (splashscreen != 0) {
preferences.set("SplashDrawableId", splashscreen);
}
appView = webView != null ? webView : makeWebView();
if (appView.pluginManager == null) {
appView.init(this, webViewClient != null ? webViewClient : makeWebViewClient(appView),
webChromeClient != null ? webChromeClient : makeChromeClient(appView),
pluginEntries, internalWhitelist, externalWhitelist, preferences);
}
// TODO: Have the views set this themselves.
if (preferences.getBoolean("DisallowOverscroll", false)) {
@@ -339,18 +346,9 @@ public class CordovaActivity extends Activity implements CordovaInterface {
}
createViews();
// Init plugins only after creating views
if (appView.pluginManager == null) {
appView.init(this, webViewClient != null ? webViewClient : makeWebViewClient(appView),
webChromeClient != null ? webChromeClient : makeChromeClient(appView),
pluginEntries, internalWhitelist, externalWhitelist, preferences);
}
// Wire the hardware volume controls to control media if desired.
String volumePref = preferences.getString("DefaultVolumeStream", "");
if ("media".equals(volumePref.toLowerCase(Locale.ENGLISH))) {
setVolumeControlStream(AudioManager.STREAM_MUSIC);
}
// TODO: Make this a preference (CB-6153)
// Setup the hardware volume controls to handle volume control
setVolumeControlStream(AudioManager.STREAM_MUSIC);
}
/**
@@ -360,10 +358,36 @@ public class CordovaActivity extends Activity implements CordovaInterface {
if (appView == null) {
init();
}
this.splashscreenTime = preferences.getInteger("SplashScreenDelay", this.splashscreenTime);
String splash = preferences.getString("SplashScreen", null);
if(this.splashscreenTime > 0 && splash != null)
{
this.splashscreen = getResources().getIdentifier(splash, "drawable", getClass().getPackage().getName());;
if(this.splashscreen != 0)
{
this.showSplashScreen(this.splashscreenTime);
}
}
// If keepRunning
this.keepRunning = preferences.getBoolean("KeepRunning", true);
appView.loadUrlIntoView(url, true);
//Check if the view is attached to anything
if(appView.getParent() != null)
{
// Then load the spinner
this.loadSpinner();
}
//Load the correct splashscreen
if(this.splashscreen != 0)
{
this.appView.loadUrl(url, this.splashscreenTime);
}
else
{
this.appView.loadUrl(url);
}
}
/**
@@ -373,12 +397,44 @@ public class CordovaActivity extends Activity implements CordovaInterface {
* @param url
* @param time The number of ms to wait before loading webview
*/
@Deprecated // Use loadUrl(String url) instead.
public void loadUrl(final String url, int time) {
this.splashscreenTime = time;
this.loadUrl(url);
}
/*
* Load the spinner
*/
void loadSpinner() {
// If loadingDialog property, then show the App loading dialog for first page of app
String loading = null;
if ((this.appView == null) || !this.appView.canGoBack()) {
loading = preferences.getString("LoadingDialog", null);
}
else {
loading = preferences.getString("LoadingPageDialog", null);
}
if (loading != null) {
String title = "";
String message = "Loading Application...";
if (loading.length() > 0) {
int comma = loading.indexOf(',');
if (comma > 0) {
title = loading.substring(0, comma);
message = loading.substring(comma + 1);
}
else {
title = "";
message = loading;
}
}
this.spinnerStart(title, message);
}
}
@Deprecated
public void cancelLoadUrl() {
@@ -529,6 +585,9 @@ public class CordovaActivity extends Activity implements CordovaInterface {
{
this.appView.handlePause(this.keepRunning);
}
// hide the splash screen to avoid leaking a window
this.removeSplashScreen();
}
/**
@@ -583,6 +642,9 @@ public class CordovaActivity extends Activity implements CordovaInterface {
LOG.d(TAG, "CordovaActivity.onDestroy()");
super.onDestroy();
// hide the splash screen to avoid leaking a window
this.removeSplashScreen();
if (this.appView != null) {
appView.handleDestroy();
}
@@ -632,20 +694,28 @@ public class CordovaActivity extends Activity implements CordovaInterface {
* @param title Title of the dialog
* @param message The message of the dialog
*/
@Deprecated // Call this directly on SplashScreen plugin instead.
public void spinnerStart(final String title, final String message) {
JSONArray args = new JSONArray();
args.put(title);
args.put(message);
doSplashScreenAction("spinnerStart", args);
if (this.spinnerDialog != null) {
this.spinnerDialog.dismiss();
this.spinnerDialog = null;
}
final CordovaActivity me = this;
this.spinnerDialog = ProgressDialog.show(CordovaActivity.this, title, message, true, true,
new DialogInterface.OnCancelListener() {
public void onCancel(DialogInterface dialog) {
me.spinnerDialog = null;
}
});
}
/**
* Stop spinner - Must be called from UI thread
*/
@Deprecated // Call this directly on SplashScreen plugin instead.
public void spinnerStop() {
doSplashScreenAction("spinnerStop", null);
if (this.spinnerDialog != null && this.spinnerDialog.isShowing()) {
this.spinnerDialog.dismiss();
this.spinnerDialog = null;
}
}
/**
@@ -666,7 +736,7 @@ public class CordovaActivity extends Activity implements CordovaInterface {
* @param requestCode The request code that is passed to callback to identify the activity
*/
public void startActivityForResult(CordovaPlugin command, Intent intent, int requestCode) {
setActivityResultCallback(command);
this.activityResultCallback = command;
this.activityResultKeepRunning = this.keepRunning;
// If multitasking turned on, then disable it for activities that return results
@@ -674,19 +744,8 @@ public class CordovaActivity extends Activity implements CordovaInterface {
this.keepRunning = false;
}
try {
startActivityForResult(intent, requestCode);
} catch (RuntimeException e) { // E.g.: ActivityNotFoundException
activityResultCallback = null;
throw e;
}
}
@Override
public void startActivityForResult(Intent intent, int requestCode, Bundle options) {
// Capture requestCode here so that it is captured in the setActivityResultCallback() case.
activityResultRequestCode = requestCode;
super.startActivityForResult(intent, requestCode, options);
// Start activity
super.startActivityForResult(intent, requestCode);
}
/**
@@ -696,34 +755,37 @@ public class CordovaActivity extends Activity implements CordovaInterface {
* @param requestCode The request code originally supplied to startActivityForResult(),
* allowing you to identify who this result came from.
* @param resultCode The integer result code returned by the child activity through its setResult().
* @param intent An Intent, which can return result data to the caller (various data can be attached to Intent "extras").
* @param data An Intent, which can return result data to the caller (various data can be attached to Intent "extras").
*/
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
LOG.d(TAG, "Incoming Result. Request code = " + requestCode);
LOG.d(TAG, "Incoming Result");
super.onActivityResult(requestCode, resultCode, intent);
Log.d(TAG, "Request code = " + requestCode);
if (appView != null && requestCode == CordovaChromeClient.FILECHOOSER_RESULTCODE) {
ValueCallback<Uri> mUploadMessage = this.appView.getWebChromeClient().getValueCallback();
Log.d(TAG, "did we get here?");
if (null == mUploadMessage)
return;
Uri result = intent == null || resultCode != Activity.RESULT_OK ? null : intent.getData();
Log.d(TAG, "result = " + result);
mUploadMessage.onReceiveValue(result);
mUploadMessage = null;
}
CordovaPlugin callback = this.activityResultCallback;
if(callback == null && initCallbackClass != null) {
// The application was restarted, but had defined an initial callback
// before being shut down.
callback = appView.pluginManager.getPlugin(initCallbackClass);
this.activityResultCallback = appView.pluginManager.getPlugin(initCallbackClass);
callback = this.activityResultCallback;
}
initCallbackClass = null;
activityResultCallback = null;
if (callback != null) {
if(callback != null) {
LOG.d(TAG, "We have a callback to send this result to");
callback.onActivityResult(requestCode, resultCode, intent);
} else {
LOG.w(TAG, "Got an activity result, but no plugin was registered to receive it.");
}
}
public void setActivityResultCallback(CordovaPlugin plugin) {
// Cancel any previously pending activity.
if (activityResultCallback != null) {
activityResultCallback.onActivityResult(activityResultRequestCode, Activity.RESULT_CANCELED, null);
}
this.activityResultCallback = plugin;
}
@@ -745,6 +807,8 @@ public class CordovaActivity extends Activity implements CordovaInterface {
// Load URL on UI thread
me.runOnUiThread(new Runnable() {
public void run() {
// Stop "app loading" spinner if showing
me.spinnerStop();
me.appView.showWebPage(errorUrl, false, true, null);
}
});
@@ -848,34 +912,62 @@ public class CordovaActivity extends Activity implements CordovaInterface {
}
}
private void doSplashScreenAction(String action, JSONArray args) {
CordovaPlugin p = appView.pluginManager.getPlugin("org.apache.cordova.splashscreeninternal");
if (p != null) {
args = args == null ? new JSONArray() : args;
try {
p.execute(action, args, null);
} catch (JSONException e) {
e.printStackTrace();
}
}
}
protected Dialog splashDialog;
/**
* Removes the Dialog that displays the splash screen
*/
@Deprecated
public void removeSplashScreen() {
doSplashScreenAction("hide", null);
if (splashDialog != null && splashDialog.isShowing()) {
splashDialog.dismiss();
splashDialog = null;
}
}
/**
* Shows the splash screen over the full Activity
*/
@SuppressWarnings("deprecation")
@Deprecated
protected void showSplashScreen(final int time) {
preferences.set("SplashScreenDelay", time);
doSplashScreenAction("show", null);
final CordovaActivity that = this;
Runnable runnable = new Runnable() {
public void run() {
// Get reference to display
Display display = getWindowManager().getDefaultDisplay();
// Create the layout for the dialog
LinearLayout root = new LinearLayout(that.getActivity());
root.setMinimumHeight(display.getHeight());
root.setMinimumWidth(display.getWidth());
root.setOrientation(LinearLayout.VERTICAL);
root.setBackgroundColor(preferences.getInteger("backgroundColor", Color.BLACK));
root.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT, 0.0F));
root.setBackgroundResource(that.splashscreen);
// Create and show the dialog
splashDialog = new Dialog(that, android.R.style.Theme_Translucent_NoTitleBar);
// check to see if the splash screen should be full screen
if ((getWindow().getAttributes().flags & WindowManager.LayoutParams.FLAG_FULLSCREEN)
== WindowManager.LayoutParams.FLAG_FULLSCREEN) {
splashDialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
}
splashDialog.setContentView(root);
splashDialog.setCancelable(false);
splashDialog.show();
// Set Runnable to remove splash screen just in case
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
public void run() {
removeSplashScreen();
}
}, time);
}
};
this.runOnUiThread(runnable);
}
@Override
@@ -920,7 +1012,28 @@ public class CordovaActivity extends Activity implements CordovaInterface {
LOG.d(TAG, "onMessage(" + id + "," + data + ")");
}
if ("onReceivedError".equals(id)) {
if ("splashscreen".equals(id)) {
if ("hide".equals(data.toString())) {
this.removeSplashScreen();
}
else {
// If the splash dialog is showing don't try to show it again
if (this.splashDialog == null || !this.splashDialog.isShowing()) {
String splashResource = preferences.getString("SplashScreen", null);
if (splashResource != null) {
splashscreen = getResources().getIdentifier(splashResource, "drawable", getClass().getPackage().getName());
}
this.showSplashScreen(this.splashscreenTime);
}
}
}
else if ("spinner".equals(id)) {
if ("stop".equals(data.toString())) {
this.spinnerStop();
this.appView.setVisibility(View.VISIBLE);
}
}
else if ("onReceivedError".equals(id)) {
JSONObject d = (JSONObject) data;
try {
this.onReceivedError(d.getInt("errorCode"), d.getString("description"), d.getString("url"));

View File

@@ -99,8 +99,6 @@ public class CordovaBridge {
}
// Bridge secret wrong and bridge not due to it being from the previous page.
if (expectedBridgeSecret < 0 || bridgeSecret != expectedBridgeSecret) {
Log.e(LOG_TAG, "Bridge access attempt with wrong secret token, possibly from malicious code. Disabling exec() bridge!");
clearBridgeSecret();
throw new IllegalAccessException();
}
return true;

View File

@@ -22,14 +22,10 @@ import org.apache.cordova.CordovaInterface;
import org.apache.cordova.LOG;
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.ActivityNotFoundException;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.util.Log;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.View;
@@ -71,7 +67,10 @@ public class CordovaChromeClient extends WebChromeClient {
//Keep track of last AlertDialog showed
private AlertDialog lastHandledDialog;
// File Chooser
public ValueCallback<Uri> mUploadMessage;
@Deprecated
public CordovaChromeClient(CordovaInterface cordova) {
this.cordova = cordova;
@@ -310,10 +309,7 @@ public class CordovaChromeClient extends WebChromeClient {
}
return mVideoProgressView;
}
// <input type=file> support:
// openFileChooser() is for pre KitKat and in KitKat mr1 (it's known broken in KitKat).
// For Lollipop, we use onShowFileChooser().
public void openFileChooser(ValueCallback<Uri> uploadMsg) {
this.openFileChooser(uploadMsg, "*/*");
}
@@ -322,41 +318,20 @@ public class CordovaChromeClient extends WebChromeClient {
this.openFileChooser(uploadMsg, acceptType, null);
}
public void openFileChooser(final ValueCallback<Uri> uploadMsg, String acceptType, String capture)
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture)
{
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("*/*");
cordova.startActivityForResult(new CordovaPlugin() {
@Override
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
Uri result = intent == null || resultCode != Activity.RESULT_OK ? null : intent.getData();
Log.d(TAG, "Receive file chooser URL: " + result);
uploadMsg.onReceiveValue(result);
}
}, intent, FILECHOOSER_RESULTCODE);
mUploadMessage = uploadMsg;
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("*/*");
this.cordova.getActivity().startActivityForResult(Intent.createChooser(i, "File Browser"),
FILECHOOSER_RESULTCODE);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
public boolean onShowFileChooser(WebView webView, final ValueCallback<Uri[]> filePathsCallback, final WebChromeClient.FileChooserParams fileChooserParams) {
Intent intent = fileChooserParams.createIntent();
try {
cordova.startActivityForResult(new CordovaPlugin() {
@Override
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
Uri[] result = WebChromeClient.FileChooserParams.parseResult(resultCode, intent);
Log.d(TAG, "Receive file chooser URL: " + result);
filePathsCallback.onReceiveValue(result);
}
}, intent, FILECHOOSER_RESULTCODE);
} catch (ActivityNotFoundException e) {
Log.w("No activity found to handle file chooser intent.", e);
filePathsCallback.onReceiveValue(null);
}
return true;
public ValueCallback<Uri> getValueCallback() {
return this.mUploadMessage;
}
public void destroyLastDialog(){
if(lastHandledDialog != null){
lastHandledDialog.cancel();

View File

@@ -1,96 +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.
*/
package org.apache.cordova;
import java.security.Principal;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import android.webkit.ClientCertRequest;
/**
* Implementation of the ICordovaClientCertRequest for Android WebView.
*/
public class CordovaClientCertRequest implements ICordovaClientCertRequest {
private final ClientCertRequest request;
public CordovaClientCertRequest(ClientCertRequest request) {
this.request = request;
}
/**
* Cancel this request
*/
public void cancel()
{
request.cancel();
}
/*
* Returns the host name of the server requesting the certificate.
*/
public String getHost()
{
return request.getHost();
}
/*
* Returns the acceptable types of asymmetric keys (can be null).
*/
public String[] getKeyTypes()
{
return request.getKeyTypes();
}
/*
* Returns the port number of the server requesting the certificate.
*/
public int getPort()
{
return request.getPort();
}
/*
* Returns the acceptable certificate issuers for the certificate matching the private key (can be null).
*/
public Principal[] getPrincipals()
{
return request.getPrincipals();
}
/*
* Ignore the request for now. Do not remember user's choice.
*/
public void ignore()
{
request.ignore();
}
/*
* Proceed with the specified private key and client certificate chain. Remember the user's positive choice and use it for future requests.
*
* @param privateKey The privateKey
* @param chain The certificate chain
*/
public void proceed(PrivateKey privateKey, X509Certificate[] chain)
{
request.proceed(privateKey, chain);
}
}

View File

@@ -1,51 +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.
*/
package org.apache.cordova;
import android.webkit.HttpAuthHandler;
/**
* Specifies interface for HTTP auth handler object which is used to handle auth requests and
* specifying user credentials.
*/
public class CordovaHttpAuthHandler implements ICordovaHttpAuthHandler {
private final HttpAuthHandler handler;
public CordovaHttpAuthHandler(HttpAuthHandler handler) {
this.handler = handler;
}
/**
* Instructs the WebView to cancel the authentication request.
*/
public void cancel () {
this.handler.cancel();
}
/**
* Instructs the WebView to proceed with the authentication with the given credentials.
*
* @param username
* @param password
*/
public void proceed (String username, String password) {
this.handler.proceed(username, password);
}
}

View File

@@ -198,34 +198,4 @@ public class CordovaPlugin {
*/
public void onReset() {
}
/**
* Called when the system received an HTTP authentication request. Plugin can use
* the supplied HttpAuthHandler to process this auth challenge.
*
* @param view The WebView that is initiating the callback
* @param handler The HttpAuthHandler used to set the WebView's response
* @param host The host requiring authentication
* @param realm The realm for which authentication is required
*
* @return Returns True if plugin will resolve this auth challenge, otherwise False
*
*/
public boolean onReceivedHttpAuthRequest(CordovaWebView view, ICordovaHttpAuthHandler handler, String host, String realm) {
return false;
}
/**
* Called when he system received an SSL client certificate request. Plugin can use
* the supplied ClientCertRequest to process this certificate challenge.
*
* @param view The WebView that is initiating the callback
* @param request The client certificate request
*
* @return Returns True if plugin will resolve this auth challenge, otherwise False
*
*/
public boolean onReceivedClientCertRequest(CordovaWebView view, ICordovaClientCertRequest request) {
return false;
}
}

View File

@@ -61,6 +61,13 @@ public class CordovaPreferences {
String value = prefs.get(name);
if (value != null) {
return Boolean.parseBoolean(value);
} else if (preferencesBundleExtras != null) {
Object bundleValue = preferencesBundleExtras.get(name);
if (bundleValue instanceof String) {
return "true".equals(bundleValue);
}
// Gives a nice warning if type is wrong.
return preferencesBundleExtras.getBoolean(name, defaultValue);
}
return defaultValue;
}
@@ -71,6 +78,13 @@ public class CordovaPreferences {
if (value != null) {
// Use Integer.decode() can't handle it if the highest bit is set.
return (int)(long)Long.decode(value);
} else if (preferencesBundleExtras != null) {
Object bundleValue = preferencesBundleExtras.get(name);
if (bundleValue instanceof String) {
return Integer.valueOf((String)bundleValue);
}
// Gives a nice warning if type is wrong.
return preferencesBundleExtras.getInt(name, defaultValue);
}
return defaultValue;
}
@@ -80,7 +94,14 @@ public class CordovaPreferences {
String value = prefs.get(name);
if (value != null) {
return Double.valueOf(value);
}
} else if (preferencesBundleExtras != null) {
Object bundleValue = preferencesBundleExtras.get(name);
if (bundleValue instanceof String) {
return Double.valueOf((String)bundleValue);
}
// Gives a nice warning if type is wrong.
return preferencesBundleExtras.getDouble(name, defaultValue);
}
return defaultValue;
}
@@ -89,8 +110,66 @@ public class CordovaPreferences {
String value = prefs.get(name);
if (value != null) {
return value;
}
} else if (preferencesBundleExtras != null && !"errorurl".equals(name)) {
Object bundleValue = preferencesBundleExtras.get(name);
if (bundleValue != null) {
return bundleValue.toString();
}
}
return defaultValue;
}
// Plugins should not rely on values within the intent since this does not work
// for apps with multiple webviews. Instead, they should retrieve prefs from the
// Config object associated with their webview.
public void copyIntoIntentExtras(Activity action) {
for (String name : prefs.keySet()) {
String value = prefs.get(name);
if (value == null) {
continue;
}
if (name.equals("loglevel")) {
LOG.setLogLevel(value);
} else if (name.equals("splashscreen")) {
// Note: We should probably pass in the classname for the variable splash on splashscreen!
int resource = action.getResources().getIdentifier(value, "drawable", action.getClass().getPackage().getName());
action.getIntent().putExtra(name, resource);
}
else if(name.equals("backgroundcolor")) {
int asInt = (int)(long)Long.decode(value);
action.getIntent().putExtra(name, asInt);
}
else if(name.equals("loadurltimeoutvalue")) {
int asInt = Integer.decode(value);
action.getIntent().putExtra(name, asInt);
}
else if(name.equals("splashscreendelay")) {
int asInt = Integer.decode(value);
action.getIntent().putExtra(name, asInt);
}
else if(name.equals("keeprunning"))
{
boolean asBool = Boolean.parseBoolean(value);
action.getIntent().putExtra(name, asBool);
}
else if(name.equals("inappbrowserstorageenabled"))
{
boolean asBool = Boolean.parseBoolean(value);
action.getIntent().putExtra(name, asBool);
}
else if(name.equals("disallowoverscroll"))
{
boolean asBool = Boolean.parseBoolean(value);
action.getIntent().putExtra(name, asBool);
}
else
{
action.getIntent().putExtra(name, value);
}
}
// In the normal case, the intent extras are null until the first call to putExtra().
if (preferencesBundleExtras == null) {
preferencesBundleExtras = action.getIntent().getExtras();
}
}
}

View File

@@ -50,7 +50,6 @@ import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebSettings.LayoutAlgorithm;
import android.webkit.WebViewClient;
import android.webkit.CookieManager;
import android.widget.FrameLayout;
/*
@@ -62,7 +61,7 @@ import android.widget.FrameLayout;
public class CordovaWebView extends WebView {
public static final String TAG = "CordovaWebView";
public static final String CORDOVA_VERSION = "3.7.2";
public static final String CORDOVA_VERSION = "3.7.0-dev";
private HashSet<Integer> boundKeyCodes = new HashSet<Integer>();
@@ -95,7 +94,6 @@ public class CordovaWebView extends WebView {
// The URL passed to loadUrl(), not necessarily the URL of the current page.
String loadedUrl;
private CordovaPreferences preferences;
private App appPlugin;
class ActivityResult {
@@ -157,10 +155,7 @@ public class CordovaWebView extends WebView {
bridge = new CordovaBridge(pluginManager, new NativeToJsMessageQueue(this, cordova), this.cordova.getActivity().getPackageName());
resourceApi = new CordovaResourceApi(this.getContext(), pluginManager);
pluginManager.addService(App.PLUGIN_NAME, "org.apache.cordova.App");
// This will be removed in 4.0.x in favour of the plugin not being bundled.
pluginManager.addService(new PluginEntry("SplashScreenInternal", "org.apache.cordova.SplashScreenInternal", true));
pluginManager.init();
pluginManager.addService("App", "org.apache.cordova.App");
initWebViewSettings();
exposeJsInterface();
}
@@ -192,14 +187,7 @@ public class CordovaWebView extends WebView {
settings.setJavaScriptEnabled(true);
settings.setJavaScriptCanOpenWindowsAutomatically(true);
settings.setLayoutAlgorithm(LayoutAlgorithm.NORMAL);
// Enable third-party cookies if on Lolipop. TODO: Make this configurable
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
{
CookieManager cookieManager = CookieManager.getInstance();
cookieManager.setAcceptThirdPartyCookies(this, 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 });
@@ -227,12 +215,8 @@ public class CordovaWebView extends WebView {
// 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) {
if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1)
Level16Apis.enableUniversalAccess(settings);
}
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) {
Level17Apis.setMediaPlaybackRequiresUserGesture(settings, false);
}
// Enable database
// We keep this disabled because we use or shim to get around DOM_EXCEPTION_ERROR_16
String databasePath = getContext().getApplicationContext().getDir("database", Context.MODE_PRIVATE).getPath();
@@ -395,11 +379,8 @@ public class CordovaWebView extends WebView {
initIfNecessary();
if (recreatePlugins) {
// Don't re-initialize on first load.
if (loadedUrl != null) {
this.pluginManager.init();
}
this.loadedUrl = url;
this.pluginManager.init();
}
// Create a timeout timer for loadUrl
@@ -454,7 +435,7 @@ public class CordovaWebView extends WebView {
if (LOG.isLoggable(LOG.DEBUG) && !url.startsWith("javascript:")) {
LOG.d(TAG, ">>> loadUrlNow()");
}
if (url.startsWith("file://") || url.startsWith("javascript:") || url.startsWith("about:") || internalWhitelist.isUrlWhiteListed(url)) {
if (url.startsWith("file://") || url.startsWith("javascript:") || internalWhitelist.isUrlWhiteListed(url)) {
super.loadUrl(url);
}
}
@@ -477,6 +458,9 @@ public class CordovaWebView extends WebView {
else {
LOG.d(TAG, "loadUrlIntoView(%s, %d)", url, time);
// Send message to show splashscreen now if desired
this.postMessage("splashscreen", "show");
}
// Load url
@@ -635,13 +619,13 @@ public class CordovaWebView extends WebView {
if(boundKeyCodes.contains(keyCode))
{
if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
sendJavascriptEvent("volumedownbutton");
return true;
this.loadUrl("javascript:cordova.fireDocumentEvent('volumedownbutton');");
return true;
}
// If volumeup key
else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
sendJavascriptEvent("volumeupbutton");
return true;
this.loadUrl("javascript:cordova.fireDocumentEvent('volumeupbutton');");
return true;
}
else
{
@@ -683,7 +667,7 @@ public class CordovaWebView extends WebView {
// The webview is currently displayed
// If back key is bound, then send event to JavaScript
if (isButtonPlumbedToJs(KeyEvent.KEYCODE_BACK)) {
sendJavascriptEvent("backbutton");
this.loadUrl("javascript:cordova.fireDocumentEvent('backbutton');");
return true;
} else {
// If not bound
@@ -698,14 +682,14 @@ public class CordovaWebView extends WebView {
// Legacy
else if (keyCode == KeyEvent.KEYCODE_MENU) {
if (this.lastMenuEventTime < event.getEventTime()) {
sendJavascriptEvent("menubutton");
this.loadUrl("javascript:cordova.fireDocumentEvent('menubutton');");
}
this.lastMenuEventTime = event.getEventTime();
return super.onKeyUp(keyCode, event);
}
// If search key
else if (keyCode == KeyEvent.KEYCODE_SEARCH) {
sendJavascriptEvent("searchbutton");
this.loadUrl("javascript:cordova.fireDocumentEvent('searchbutton');");
return true;
}
@@ -713,18 +697,6 @@ public class CordovaWebView extends WebView {
return super.onKeyUp(keyCode, event);
}
private void sendJavascriptEvent(String event) {
if (appPlugin == null) {
appPlugin = (App)this.pluginManager.getPlugin(App.PLUGIN_NAME);
}
if (appPlugin == null) {
LOG.w(TAG, "Unable to fire event without existing plugin");
return;
}
appPlugin.fireJavascriptEvent(event);
}
public void setButtonPlumbedToJs(int keyCode, boolean override) {
switch (keyCode) {
case KeyEvent.KEYCODE_VOLUME_DOWN:
@@ -778,7 +750,7 @@ public class CordovaWebView extends WebView {
{
LOG.d(TAG, "Handle the pause");
// Send pause event to JavaScript
sendJavascriptEvent("pause");
this.loadUrl("javascript:try{cordova.fireDocumentEvent('pause');}catch(e){console.log('exception firing pause event from native');};");
// Forward to plugins
if (this.pluginManager != null) {
@@ -796,8 +768,9 @@ public class CordovaWebView extends WebView {
public void handleResume(boolean keepRunning, boolean activityResultKeepRunning)
{
sendJavascriptEvent("resume");
this.loadUrl("javascript:try{cordova.fireDocumentEvent('resume');}catch(e){console.log('exception firing resume event from native');};");
// Forward to plugins
if (this.pluginManager != null) {
this.pluginManager.onResume(keepRunning);
@@ -810,8 +783,8 @@ public class CordovaWebView extends WebView {
public void handleDestroy()
{
// Cancel pending timeout timer.
loadUrlTimeout++;
// Send destroy event to JavaScript
this.loadUrl("javascript:try{cordova.require('cordova/channel').onDestroy.fire();}catch(e){console.log('exception firing destroy event from native');};");
// Load blank page so that JavaScript onunload is called
this.loadUrl("about:blank");
@@ -855,19 +828,12 @@ public class CordovaWebView extends WebView {
// 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)
private static final class Level16Apis {
private static class Level16Apis {
static void enableUniversalAccess(WebSettings settings) {
settings.setAllowUniversalAccessFromFileURLs(true);
}
}
@TargetApi(17)
private static final class Level17Apis {
static void setMediaPlaybackRequiresUserGesture(WebSettings settings, boolean value) {
settings.setMediaPlaybackRequiresUserGesture(value);
}
}
public void printBackForwardList() {
WebBackForwardList currentList = this.copyBackForwardList();
int currentSize = currentList.getSize();

View File

@@ -21,6 +21,7 @@ package org.apache.cordova;
import java.util.Hashtable;
import org.apache.cordova.CordovaInterface;
import org.apache.cordova.LOG;
import org.json.JSONException;
import org.json.JSONObject;
@@ -32,7 +33,6 @@ import android.content.pm.PackageManager.NameNotFoundException;
import android.graphics.Bitmap;
import android.net.http.SslError;
import android.view.View;
import android.webkit.ClientCertRequest;
import android.webkit.HttpAuthHandler;
import android.webkit.SslErrorHandler;
import android.webkit.WebView;
@@ -115,45 +115,15 @@ public class CordovaWebViewClient extends WebViewClient {
@Override
public void onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler, String host, String realm) {
// Get the authentication token (if specified)
// Get the authentication token
AuthenticationToken token = this.getAuthenticationToken(host, realm);
if (token != null) {
handler.proceed(token.getUserName(), token.getPassword());
return;
}
// Check if there is some plugin which can resolve this auth challenge
PluginManager pluginManager = this.appView.pluginManager;
if (pluginManager != null && pluginManager.onReceivedHttpAuthRequest(this.appView, new CordovaHttpAuthHandler(handler), host, realm)) {
this.appView.loadUrlTimeout++;
return;
else {
// Handle 401 like we'd normally do!
super.onReceivedHttpAuthRequest(view, handler, host, realm);
}
// By default handle 401 like we'd normally do!
super.onReceivedHttpAuthRequest(view, handler, host, realm);
}
/**
* On received client cert request.
* The method forwards the request to any running plugins before using the default implementation.
*
* @param view
* @param request
*/
@Override
@TargetApi(21)
public void onReceivedClientCertRequest (WebView view, ClientCertRequest request)
{
// Check if there is some plugin which can resolve this certificate request
PluginManager pluginManager = this.appView.pluginManager;
if (pluginManager != null && pluginManager.onReceivedClientCertRequest(this.appView, new CordovaClientCertRequest(request))) {
this.appView.loadUrlTimeout++;
return;
}
// By default pass to WebViewClient
super.onReceivedClientCertRequest(view, request);
}
/**
@@ -193,8 +163,8 @@ public class CordovaWebViewClient extends WebViewClient {
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
// Ignore excessive calls, if url is not about:blank (CB-8317).
if (!isCurrentlyLoading && !url.startsWith("about:")) {
// Ignore excessive calls.
if (!isCurrentlyLoading) {
return;
}
isCurrentlyLoading = false;

View File

@@ -1,66 +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.
*/
package org.apache.cordova;
import java.security.Principal;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
/**
* Specifies interface for handling certificate requests.
*/
public interface ICordovaClientCertRequest {
/**
* Cancel this request
*/
public void cancel();
/*
* Returns the host name of the server requesting the certificate.
*/
public String getHost();
/*
* Returns the acceptable types of asymmetric keys (can be null).
*/
public String[] getKeyTypes();
/*
* Returns the port number of the server requesting the certificate.
*/
public int getPort();
/*
* Returns the acceptable certificate issuers for the certificate matching the private key (can be null).
*/
public Principal[] getPrincipals();
/*
* Ignore the request for now. Do not remember user's choice.
*/
public void ignore();
/*
* Proceed with the specified private key and client certificate chain. Remember the user's positive choice and use it for future requests.
*
* @param privateKey The privateKey
* @param chain The certificate chain
*/
public void proceed(PrivateKey privateKey, X509Certificate[] chain);
}

View File

@@ -1,38 +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.
*/
package org.apache.cordova;
/**
* Specifies interface for HTTP auth handler object which is used to handle auth requests and
* specifying user credentials.
*/
public interface ICordovaHttpAuthHandler {
/**
* Instructs the WebView to cancel the authentication request.
*/
public void cancel ();
/**
* Instructs the WebView to proceed with the authentication with the given credentials.
*
* @param username The user name
* @param password The password
*/
public void proceed (String username, String password);
}

View File

@@ -18,7 +18,10 @@
*/
package org.apache.cordova;
import org.apache.cordova.LOG;
import android.content.Context;
//import android.view.View.MeasureSpec;
import android.widget.LinearLayout;
/**
@@ -33,7 +36,6 @@ public class LinearLayoutSoftKeyboardDetect extends LinearLayout {
private int screenWidth = 0;
private int screenHeight = 0;
private CordovaActivity app = null;
private App appPlugin = null;
public LinearLayoutSoftKeyboardDetect(Context context, int width, int height) {
super(context);
@@ -48,7 +50,7 @@ public class LinearLayoutSoftKeyboardDetect extends LinearLayout {
* gets smaller fire a show keyboard event and when height gets bigger fire
* a hide keyboard event.
*
* Note: We are using the core App plugin to send events over the bridge to Javascript
* Note: We are using app.postMessage so that this is more compatible with the API
*
* @param widthMeasureSpec
* @param heightMeasureSpec
@@ -85,12 +87,14 @@ public class LinearLayoutSoftKeyboardDetect extends LinearLayout {
// If the height as gotten bigger then we will assume the soft keyboard has
// gone away.
else if (height > oldHeight) {
sendEvent("hidekeyboard");
if (app != null)
app.appView.sendJavascript("cordova.fireDocumentEvent('hidekeyboard');");
}
// If the height as gotten smaller then we will assume the soft keyboard has
// If the height as gotten smaller then we will assume the soft keyboard has
// been displayed.
else if (height < oldHeight) {
sendEvent("showkeyboard");
if (app != null)
app.appView.sendJavascript("cordova.fireDocumentEvent('showkeyboard');");
}
// Update the old height for the next event
@@ -98,15 +102,4 @@ public class LinearLayoutSoftKeyboardDetect extends LinearLayout {
oldWidth = width;
}
private void sendEvent(String event) {
if (appPlugin == null) {
appPlugin = (App)app.appView.pluginManager.getPlugin(App.PLUGIN_NAME);
}
if (appPlugin == null) {
LOG.w(TAG, "Unable to fire event without existing plugin");
return;
}
appPlugin.fireJavascriptEvent(event);
}
}

View File

@@ -110,12 +110,8 @@ public class PluginManager {
@Deprecated // Should not be exposed as public.
public void startupPlugins() {
for (PluginEntry entry : entryMap.values()) {
// Add a null entry to for each non-startup plugin to avoid ConcurrentModificationException
// When iterating plugins.
if (entry.onload) {
getPlugin(entry.service);
} else {
pluginMap.put(entry.service, null);
}
}
}
@@ -236,52 +232,10 @@ public class PluginManager {
*/
public void onPause(boolean multitasking) {
for (CordovaPlugin plugin : this.pluginMap.values()) {
if (plugin != null) {
plugin.onPause(multitasking);
}
plugin.onPause(multitasking);
}
}
/**
* Called when the system received an HTTP authentication request. Plugins can use
* the supplied HttpAuthHandler to process this auth challenge.
*
* @param view The WebView that is initiating the callback
* @param handler The HttpAuthHandler used to set the WebView's response
* @param host The host requiring authentication
* @param realm The realm for which authentication is required
*
* @return Returns True if there is a plugin which will resolve this auth challenge, otherwise False
*
*/
public boolean onReceivedHttpAuthRequest(CordovaWebView view, ICordovaHttpAuthHandler handler, String host, String realm) {
for (CordovaPlugin plugin : this.pluginMap.values()) {
if (plugin != null && plugin.onReceivedHttpAuthRequest(view, handler, host, realm)) {
return true;
}
}
return false;
}
/**
* Called when he system received an SSL client certificate request. Plugin can use
* the supplied ClientCertRequest to process this certificate challenge.
*
* @param view The WebView that is initiating the callback
* @param request The client certificate request
*
* @return Returns True if plugin will resolve this auth challenge, otherwise False
*
*/
public boolean onReceivedClientCertRequest(CordovaWebView view, ICordovaClientCertRequest request) {
for (CordovaPlugin plugin : this.pluginMap.values()) {
if (plugin != null && plugin.onReceivedClientCertRequest(view, request)) {
return true;
}
}
return false;
}
/**
* Called when the activity will start interacting with the user.
*
@@ -289,9 +243,7 @@ public class PluginManager {
*/
public void onResume(boolean multitasking) {
for (CordovaPlugin plugin : this.pluginMap.values()) {
if (plugin != null) {
plugin.onResume(multitasking);
}
plugin.onResume(multitasking);
}
}
@@ -300,9 +252,7 @@ public class PluginManager {
*/
public void onDestroy() {
for (CordovaPlugin plugin : this.pluginMap.values()) {
if (plugin != null) {
plugin.onDestroy();
}
plugin.onDestroy();
}
}
@@ -319,11 +269,9 @@ public class PluginManager {
return obj;
}
for (CordovaPlugin plugin : this.pluginMap.values()) {
if (plugin != null) {
obj = plugin.onMessage(id, data);
if (obj != null) {
return obj;
}
obj = plugin.onMessage(id, data);
if (obj != null) {
return obj;
}
}
return null;
@@ -334,9 +282,7 @@ public class PluginManager {
*/
public void onNewIntent(Intent intent) {
for (CordovaPlugin plugin : this.pluginMap.values()) {
if (plugin != null) {
plugin.onNewIntent(intent);
}
plugin.onNewIntent(intent);
}
}
@@ -374,19 +320,15 @@ public class PluginManager {
*/
public void onReset() {
for (CordovaPlugin plugin : this.pluginMap.values()) {
if (plugin != null) {
plugin.onReset();
}
plugin.onReset();
}
}
Uri remapUri(Uri uri) {
for (CordovaPlugin plugin : this.pluginMap.values()) {
if (plugin != null) {
Uri ret = plugin.remapUri(uri);
if (ret != null) {
return ret;
}
Uri ret = plugin.remapUri(uri);
if (ret != null) {
return ret;
}
}
return null;

View File

@@ -1,257 +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.
*/
package org.apache.cordova;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Color;
import android.os.Handler;
import android.view.Display;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.LinearLayout;
import org.json.JSONArray;
import org.json.JSONException;
// This file is a copy of SplashScreen.java from cordova-plugin-splashscreen, and is required only
// for pre-4.0 Cordova as a transition path to it being extracted into the plugin.
public class SplashScreenInternal extends CordovaPlugin {
private static final String LOG_TAG = "SplashScreenInternal";
private static Dialog splashDialog;
private static ProgressDialog spinnerDialog;
private static boolean firstShow = true;
@Override
protected void pluginInitialize() {
if (!firstShow) {
return;
}
// Make WebView invisible while loading URL
webView.setVisibility(View.INVISIBLE);
int drawableId = preferences.getInteger("SplashDrawableId", 0);
if (drawableId == 0) {
String splashResource = preferences.getString("SplashScreen", null);
if (splashResource != null) {
drawableId = cordova.getActivity().getResources().getIdentifier(splashResource, "drawable", cordova.getActivity().getClass().getPackage().getName());
if (drawableId == 0) {
drawableId = cordova.getActivity().getResources().getIdentifier(splashResource, "drawable", cordova.getActivity().getPackageName());
}
preferences.set("SplashDrawableId", drawableId);
}
}
firstShow = false;
loadSpinner();
showSplashScreen(true);
}
@Override
public void onPause(boolean multitasking) {
// hide the splash screen to avoid leaking a window
this.removeSplashScreen();
}
@Override
public void onDestroy() {
// hide the splash screen to avoid leaking a window
this.removeSplashScreen();
firstShow = true;
}
@Override
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
if (action.equals("hide")) {
cordova.getActivity().runOnUiThread(new Runnable() {
public void run() {
webView.postMessage("splashscreen", "hide");
}
});
} else if (action.equals("show")) {
cordova.getActivity().runOnUiThread(new Runnable() {
public void run() {
webView.postMessage("splashscreen", "show");
}
});
} else if (action.equals("spinnerStart")) {
final String title = args.getString(0);
final String message = args.getString(1);
cordova.getActivity().runOnUiThread(new Runnable() {
public void run() {
spinnerStart(title, message);
}
});
} else {
return false;
}
callbackContext.success();
return true;
}
@Override
public Object onMessage(String id, Object data) {
if ("splashscreen".equals(id)) {
if ("hide".equals(data.toString())) {
this.removeSplashScreen();
} else {
this.showSplashScreen(false);
}
} else if ("spinner".equals(id)) {
if ("stop".equals(data.toString())) {
this.spinnerStop();
webView.setVisibility(View.VISIBLE);
}
} else if ("onReceivedError".equals(id)) {
spinnerStop();
}
return null;
}
private void removeSplashScreen() {
cordova.getActivity().runOnUiThread(new Runnable() {
public void run() {
if (splashDialog != null && splashDialog.isShowing()) {
splashDialog.dismiss();
splashDialog = null;
}
}
});
}
/**
* Shows the splash screen over the full Activity
*/
@SuppressWarnings("deprecation")
private void showSplashScreen(final boolean hideAfterDelay) {
final int splashscreenTime = preferences.getInteger("SplashScreenDelay", 3000);
final int drawableId = preferences.getInteger("SplashDrawableId", 0);
// If the splash dialog is showing don't try to show it again
if (this.splashDialog != null && splashDialog.isShowing()) {
return;
}
if (drawableId == 0 || (splashscreenTime <= 0 && hideAfterDelay)) {
return;
}
cordova.getActivity().runOnUiThread(new Runnable() {
public void run() {
// Get reference to display
Display display = cordova.getActivity().getWindowManager().getDefaultDisplay();
Context context = webView.getContext();
// Create the layout for the dialog
LinearLayout root = new LinearLayout(context);
root.setMinimumHeight(display.getHeight());
root.setMinimumWidth(display.getWidth());
root.setOrientation(LinearLayout.VERTICAL);
// TODO: Use the background color of the webview's parent instead of using the
// preference.
root.setBackgroundColor(preferences.getInteger("backgroundColor", Color.BLACK));
root.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT, 0.0F));
root.setBackgroundResource(drawableId);
// Create and show the dialog
splashDialog = new Dialog(context, android.R.style.Theme_Translucent_NoTitleBar);
// check to see if the splash screen should be full screen
if ((cordova.getActivity().getWindow().getAttributes().flags & WindowManager.LayoutParams.FLAG_FULLSCREEN)
== WindowManager.LayoutParams.FLAG_FULLSCREEN) {
splashDialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
}
splashDialog.setContentView(root);
splashDialog.setCancelable(false);
splashDialog.show();
// Set Runnable to remove splash screen just in case
if (hideAfterDelay) {
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
public void run() {
removeSplashScreen();
}
}, splashscreenTime);
}
}
});
}
/*
* Load the spinner
*/
private void loadSpinner() {
// If loadingDialog property, then show the App loading dialog for first page of app
String loading = null;
if (webView.canGoBack()) {
loading = preferences.getString("LoadingDialog", null);
}
else {
loading = preferences.getString("LoadingPageDialog", null);
}
if (loading != null) {
String title = "";
String message = "Loading Application...";
if (loading.length() > 0) {
int comma = loading.indexOf(',');
if (comma > 0) {
title = loading.substring(0, comma);
message = loading.substring(comma + 1);
}
else {
title = "";
message = loading;
}
}
spinnerStart(title, message);
}
}
private void spinnerStart(final String title, final String message) {
cordova.getActivity().runOnUiThread(new Runnable() {
public void run() {
spinnerStop();
spinnerDialog = ProgressDialog.show(webView.getContext(), title, message, true, true,
new DialogInterface.OnCancelListener() {
public void onCancel(DialogInterface dialog) {
spinnerDialog = null;
}
});
}
});
}
private void spinnerStop() {
cordova.getActivity().runOnUiThread(new Runnable() {
public void run() {
if (spinnerDialog != null && spinnerDialog.isShowing()) {
spinnerDialog.dismiss();
spinnerDialog = null;
}
}
});
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "cordova-android",
"version": "3.7.2",
"version": "3.7.0-dev",
"description": "cordova-android release",
"main": "bin/create",
"repository": {
@@ -26,4 +26,4 @@
"jasmine-node": "~1",
"promise-matchers": "~0"
}
}
}