mirror of
https://github.com/apache/cordova-android.git
synced 2026-02-21 00:02:46 +08:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6201a6f6ec |
@@ -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!
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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'));
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
175
bin/templates/cordova/lib/build.js
vendored
175
bin/templates/cordova/lib/build.js
vendored
@@ -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);
|
||||
};
|
||||
|
||||
47
bin/templates/cordova/lib/device.js
vendored
47
bin/templates/cordova/lib/device.js
vendored
@@ -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) {
|
||||
|
||||
15
bin/templates/cordova/lib/emulator.js
vendored
15
bin/templates/cordova/lib/emulator.js
vendored
@@ -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) {
|
||||
|
||||
3
bin/templates/cordova/lib/log.js
vendored
3
bin/templates/cordova/lib/log.js
vendored
@@ -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') : [];
|
||||
|
||||
@@ -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 {
|
||||
|
||||
46
bin/templates/cordova/lib/run.js
vendored
46
bin/templates/cordova/lib/run.js
vendored
@@ -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) {
|
||||
|
||||
11
bin/templates/cordova/lib/spawn.js
vendored
11
bin/templates/cordova/lib/spawn.js
vendored
@@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,6 @@
|
||||
*/
|
||||
|
||||
// Coho updates this line:
|
||||
var VERSION = "3.7.2";
|
||||
var VERSION = "3.7.0-dev";
|
||||
|
||||
console.log(VERSION);
|
||||
|
||||
@@ -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'
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
|
||||
106
framework/assets/www/cordova.js
vendored
106
framework/assets/www/cordova.js
vendored
@@ -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');
|
||||
|
||||
})();
|
||||
})();
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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"));
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user