mirror of
https://github.com/apache/cordova-android.git
synced 2026-01-30 00:05:28 +08:00
Compare commits
76 Commits
Gitignore_
...
3.7.x
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c1c0a63e5e | ||
|
|
d654de809a | ||
|
|
86f9b618ef | ||
|
|
ec1cc70c8b | ||
|
|
a64203f817 | ||
|
|
f4b315961b | ||
|
|
3bc755de7c | ||
|
|
c5ffdff49b | ||
|
|
2829ed77cc | ||
|
|
f3885692d9 | ||
|
|
d7fc37d365 | ||
|
|
c3bdebdb83 | ||
|
|
9514a3ed94 | ||
|
|
050dd088cf | ||
|
|
26f0401fda | ||
|
|
e4e04927b6 | ||
|
|
6b3ef11715 | ||
|
|
cf0fdf5ac0 | ||
|
|
33614d1273 | ||
|
|
1f735935b9 | ||
|
|
8c14e33bb6 | ||
|
|
7375154228 | ||
|
|
1427c13504 | ||
|
|
5538a231a8 | ||
|
|
9be110683b | ||
|
|
cfc36140bc | ||
|
|
72e947e5c7 | ||
|
|
7320ce8ea7 | ||
|
|
3e07f26bc4 | ||
|
|
5415440829 | ||
|
|
15e19489e3 | ||
|
|
c3610aa43c | ||
|
|
c1ac3aa483 | ||
|
|
291f111913 | ||
|
|
c2a6dcb6bd | ||
|
|
3439746645 | ||
|
|
b10fe465ab | ||
|
|
480af2644c | ||
|
|
ecd2e06883 | ||
|
|
7cfb33d0ef | ||
|
|
9224ab1592 | ||
|
|
931a996dab | ||
|
|
98fe46757f | ||
|
|
6b6e887c2f | ||
|
|
b92303b1c9 | ||
|
|
731a36d3a0 | ||
|
|
342bbaa3ae | ||
|
|
56a3ee5fe6 | ||
|
|
d80d532a2a | ||
|
|
3aca14d530 | ||
|
|
aa2d3962bf | ||
|
|
f7c717e393 | ||
|
|
268fea58ee | ||
|
|
ba140a8a84 | ||
|
|
27f1181d53 | ||
|
|
f953e6adb8 | ||
|
|
ffd14fe7d9 | ||
|
|
66fa12a091 | ||
|
|
132650df28 | ||
|
|
81a77949fc | ||
|
|
7fbb2b195f | ||
|
|
1feaa7fed7 | ||
|
|
ac284fd39c | ||
|
|
e78db000c6 | ||
|
|
032ea8a8d3 | ||
|
|
fc63f66e89 | ||
|
|
832e626573 | ||
|
|
ce5d9a2ee8 | ||
|
|
77c51d3ae7 | ||
|
|
53dae45430 | ||
|
|
16343ffe70 | ||
|
|
b37498d5f6 | ||
|
|
9f41906895 | ||
|
|
fbeb379f1b | ||
|
|
2dcd50c11b | ||
|
|
30681eb772 |
@@ -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,3 +35,4 @@ 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,6 +20,40 @@
|
||||
-->
|
||||
## 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)
|
||||
|
||||
@@ -31,7 +31,7 @@ var isWindows = process.platform == 'win32';
|
||||
|
||||
function forgivingWhichSync(cmd) {
|
||||
try {
|
||||
return which.sync(cmd);
|
||||
return fs.realpathSync(which.sync(cmd));
|
||||
} catch (e) {
|
||||
return '';
|
||||
}
|
||||
@@ -102,7 +102,7 @@ module.exports.check_java = function() {
|
||||
} else {
|
||||
// See if we can derive it from javac's location.
|
||||
// fs.realpathSync is require on Ubuntu, which symplinks from /usr/bin -> JDK
|
||||
var maybeJavaHome = path.dirname(path.dirname(fs.realpathSync(javacPath)));
|
||||
var maybeJavaHome = path.dirname(path.dirname(javacPath));
|
||||
if (fs.existsSync(path.join(maybeJavaHome, 'lib', 'tools.jar'))) {
|
||||
process.env['JAVA_HOME'] = maybeJavaHome;
|
||||
} else {
|
||||
@@ -156,20 +156,26 @@ module.exports.check_android = function() {
|
||||
}
|
||||
if (!hasAndroidHome && !androidCmdPath) {
|
||||
if (isWindows) {
|
||||
// Android Studio installer.
|
||||
// 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
|
||||
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']) {
|
||||
// or their HOME directory.
|
||||
// Stand-alone zip file that user might think to put under their home directory
|
||||
maybeSetAndroidHome(path.join(process.env['HOME'], 'android-sdk-macosx'));
|
||||
maybeSetAndroidHome(path.join(process.env['HOME'], 'android-sdk'));
|
||||
}
|
||||
@@ -179,9 +185,15 @@ module.exports.check_android = function() {
|
||||
}
|
||||
if (androidCmdPath && !hasAndroidHome) {
|
||||
var parentDir = path.dirname(androidCmdPath);
|
||||
var grandParentDir = path.dirname(parentDir);
|
||||
if (path.basename(parentDir) == 'tools') {
|
||||
process.env['ANDROID_HOME'] = path.dirname(parentDir);
|
||||
hasAndroidHome = true;
|
||||
} else if (fs.existsSync(path.join(grandParentDir, 'tools', 'android'))) {
|
||||
process.env['ANDROID_HOME'] = grandParentDir;
|
||||
hasAndroidHome = true;
|
||||
} else {
|
||||
throw new Error('ANDROID_HOME is not set and no "tools" directory found at ' + parentDir);
|
||||
}
|
||||
}
|
||||
if (hasAndroidHome && !adbInPath) {
|
||||
|
||||
@@ -72,6 +72,7 @@ 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.
|
||||
@@ -122,7 +123,6 @@ 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) {
|
||||
@@ -210,9 +210,11 @@ exports.createProject = function(project_path, package_name, project_name, proje
|
||||
project_template_dir :
|
||||
path.join(ROOT, 'bin', 'templates', 'project');
|
||||
|
||||
var safe_activity_name = project_name.replace(/\W/g, '');
|
||||
var package_as_path = package_name.replace(/\./g, path.sep);
|
||||
var activity_dir = path.join(project_path, 'src', package_as_path);
|
||||
// safe_activity_name is being hardcoded to avoid issues with unicode app name (https://issues.apache.org/jira/browse/CB-6511)
|
||||
// TODO: provide option to specify activity name via CLI (proposal: https://issues.apache.org/jira/browse/CB-7231)
|
||||
var safe_activity_name = 'MainActivity';
|
||||
var activity_path = path.join(activity_dir, safe_activity_name + '.java');
|
||||
var target_api = check_reqs.get_target();
|
||||
var manifest_path = path.join(project_path, 'AndroidManifest.xml');
|
||||
@@ -276,7 +278,7 @@ exports.createProject = function(project_path, package_name, project_name, proje
|
||||
copyBuildRules(project_path);
|
||||
});
|
||||
// Link it to local android install.
|
||||
writeProjectProperties(project_path, target_api);
|
||||
writeProjectProperties(project_path, target_api, use_shared_project);
|
||||
}).then(function() {
|
||||
console.log('Project successfully created.');
|
||||
});
|
||||
|
||||
175
bin/templates/cordova/lib/build.js
vendored
175
bin/templates/cordova/lib/build.js
vendored
@@ -24,6 +24,7 @@ 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');
|
||||
@@ -68,11 +69,13 @@ 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) {
|
||||
if (arch && ret.length > 1) {
|
||||
ret = ret.filter(function(p) {
|
||||
return p.indexOf('-' + arch) != -1;
|
||||
});
|
||||
@@ -173,46 +176,20 @@ var builders = {
|
||||
}
|
||||
},
|
||||
gradle: {
|
||||
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';
|
||||
getArgs: function(cmd, arch, extraArgs) {
|
||||
if (cmd == 'release') {
|
||||
cmd = 'cdvBuildRelease';
|
||||
} else if (cmd == 'debug') {
|
||||
cmd = 'assembleDebug';
|
||||
} else if (cmd == 'release') {
|
||||
cmd = 'assembleRelease';
|
||||
cmd = 'cdvBuildDebug';
|
||||
}
|
||||
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');
|
||||
// Excluding lint: 6s-> 1.6s
|
||||
for (var i = 0; i < lintSteps.length; ++i) {
|
||||
args.push('-x', lintSteps[i]);
|
||||
}
|
||||
args.push.apply(args, extraArgs);
|
||||
// Shaves another 100ms, but produces a "try at own risk" warning. Not worth it (yet):
|
||||
// args.push('-Dorg.gradle.parallel=true');
|
||||
return args;
|
||||
@@ -237,10 +214,11 @@ 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 1.12.
|
||||
// If the gradle distribution URL is set, make sure it points to version we want.
|
||||
// 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.
|
||||
var distributionUrlRegex = '/^distributionUrl=.*$/';
|
||||
var distributionUrl = 'distributionUrl=http\\://services.gradle.org/distributions/gradle-1.12-all.zip';
|
||||
// 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 gradleWrapperPropertiesPath = path.join(projectPath, 'gradle', 'wrapper', 'gradle-wrapper.properties');
|
||||
shell.sed('-i', distributionUrlRegex, distributionUrl, gradleWrapperPropertiesPath);
|
||||
|
||||
@@ -248,7 +226,9 @@ var builders = {
|
||||
var pluginBuildGradle = path.join(projectPath, 'cordova', 'lib', 'plugin-build.gradle');
|
||||
var subProjects = extractSubProjectPaths();
|
||||
for (var i = 0; i < subProjects.length; ++i) {
|
||||
shell.cp('-f', pluginBuildGradle, path.join(ROOT, subProjects[i], 'build.gradle'));
|
||||
if (subProjects[i] !== 'CordovaLib') {
|
||||
shell.cp('-f', pluginBuildGradle, path.join(ROOT, subProjects[i], 'build.gradle'));
|
||||
}
|
||||
}
|
||||
|
||||
var subProjectsAsGradlePaths = subProjects.map(function(p) { return ':' + p.replace(/[/\\]/g, ':') });
|
||||
@@ -273,20 +253,22 @@ var builders = {
|
||||
* Builds the project with gradle.
|
||||
* Returns a promise.
|
||||
*/
|
||||
build: function(build_type, arch) {
|
||||
build: function(build_type, arch, extraArgs) {
|
||||
var builder = this;
|
||||
var wrapper = path.join(ROOT, 'gradlew');
|
||||
var args = this.getArgs(build_type == 'debug' ? 'debug' : 'release', arch);
|
||||
var args = this.getArgs(build_type == 'debug' ? 'debug' : 'release', arch, extraArgs);
|
||||
return Q().then(function() {
|
||||
console.log('Running: ' + wrapper + ' ' + args.concat(extraArgs).join(' '));
|
||||
return spawn(wrapper, args);
|
||||
});
|
||||
},
|
||||
|
||||
clean: function() {
|
||||
clean: function(extraArgs) {
|
||||
var builder = this;
|
||||
var wrapper = path.join(ROOT, 'gradlew');
|
||||
var args = builder.getArgs('clean');
|
||||
var args = builder.getArgs('clean', null, extraArgs);
|
||||
return Q().then(function() {
|
||||
console.log('Running: ' + wrapper + ' ' + args.concat(extraArgs).join(' '));
|
||||
return spawn(wrapper, args);
|
||||
});
|
||||
},
|
||||
@@ -321,37 +303,61 @@ function parseOpts(options, resolvedTarget) {
|
||||
var ret = {
|
||||
buildType: 'debug',
|
||||
buildMethod: process.env['ANDROID_BUILD'] || 'ant',
|
||||
arch: null
|
||||
arch: null,
|
||||
extraArgs: []
|
||||
};
|
||||
|
||||
var multiValueArgs = {
|
||||
'versionCode': true,
|
||||
'minSdkVersion': true,
|
||||
'gradleArg': true
|
||||
};
|
||||
|
||||
// Iterate through command line options
|
||||
for (var i=0; options && (i < options.length); ++i) {
|
||||
if (/^--/.exec(options[i])) {
|
||||
var option = options[i].substring(2);
|
||||
switch(option) {
|
||||
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) {
|
||||
case 'debug':
|
||||
case 'release':
|
||||
ret.buildType = option;
|
||||
ret.buildType = flagName;
|
||||
break;
|
||||
case 'ant':
|
||||
case 'gradle':
|
||||
ret.buildMethod = option;
|
||||
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.
|
||||
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 :
|
||||
return Q.reject('Build option \'' + options[i] + '\' not recognized.');
|
||||
console.warn('Build option --\'' + flagName + '\' not recognized (ignoring).');
|
||||
}
|
||||
} else {
|
||||
return Q.reject('Build option \'' + options[i] + '\' not recognized.');
|
||||
console.warn('Build option \'' + options[i] + '\' not recognized (ignoring).');
|
||||
}
|
||||
}
|
||||
|
||||
var multiApk = ret.buildMethod == 'gradle' && process.env['BUILD_MULTIPLE_APKS'];
|
||||
if (multiApk && !/0|false|no/i.exec(multiApk)) {
|
||||
ret.arch = resolvedTarget && resolvedTarget.arch;
|
||||
}
|
||||
ret.arch = resolvedTarget && resolvedTarget.arch;
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -365,7 +371,7 @@ module.exports.runClean = function(options) {
|
||||
var builder = builders[opts.buildMethod];
|
||||
return builder.prepEnv()
|
||||
.then(function() {
|
||||
return builder.clean();
|
||||
return builder.clean(opts.extraArgs);
|
||||
}).then(function() {
|
||||
shell.rm('-rf', path.join(ROOT, 'out'));
|
||||
});
|
||||
@@ -380,7 +386,7 @@ module.exports.run = function(options, optResolvedTarget) {
|
||||
var builder = builders[opts.buildMethod];
|
||||
return builder.prepEnv()
|
||||
.then(function() {
|
||||
return builder.build(opts.buildType, opts.arch);
|
||||
return builder.build(opts.buildType, opts.arch, opts.extraArgs);
|
||||
}).then(function() {
|
||||
var apkPaths = builder.findOutputApks(opts.buildType, opts.arch);
|
||||
console.log('Built the following apk(s):');
|
||||
@@ -398,12 +404,44 @@ module.exports.run = function(options, optResolvedTarget) {
|
||||
* Returns "arm" or "x86".
|
||||
*/
|
||||
module.exports.detectArchitecture = function(target) {
|
||||
return exec('adb -s ' + target + ' shell cat /proc/cpuinfo')
|
||||
.then(function(output) {
|
||||
if (/intel/i.exec(output)) {
|
||||
return 'x86';
|
||||
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 'arm';
|
||||
throw err;
|
||||
});
|
||||
};
|
||||
|
||||
@@ -429,12 +467,15 @@ module.exports.findBestApkForArchitecture = function(buildResults, arch) {
|
||||
};
|
||||
|
||||
module.exports.help = function() {
|
||||
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('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(' \'--release\': will build project for release');
|
||||
console.log(' \'--ant\': Default build, will build project with ant');
|
||||
console.log(' \'--ant\': will build project with ant (default)');
|
||||
console.log(' \'--gradle\': will build project with gradle');
|
||||
console.log(' \'--nobuild\': will skip build process (can be used with run command)');
|
||||
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');
|
||||
process.exit(0);
|
||||
};
|
||||
|
||||
47
bin/templates/cordova/lib/device.js
vendored
47
bin/templates/cordova/lib/device.js
vendored
@@ -22,29 +22,50 @@
|
||||
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() {
|
||||
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', ''));
|
||||
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', ''));
|
||||
}
|
||||
}
|
||||
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 device_list;
|
||||
return list;
|
||||
});
|
||||
}
|
||||
|
||||
module.exports.resolveTarget = function(target) {
|
||||
return this.list()
|
||||
return this.list(true)
|
||||
.then(function(device_list) {
|
||||
if (!device_list || !device_list.length) {
|
||||
return Q.reject('ERROR: Failed to deploy to device, no devices found.');
|
||||
@@ -80,19 +101,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)
|
||||
return exec(cmd, os.tmpdir())
|
||||
.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);
|
||||
return exec(cmd, os.tmpdir());
|
||||
}, 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);
|
||||
return exec(cmd, os.tmpdir());
|
||||
}).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,6 +23,7 @@ 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, '..', '..'),
|
||||
@@ -108,7 +109,7 @@ module.exports.best_image = function() {
|
||||
|
||||
// Returns a promise.
|
||||
module.exports.list_started = function() {
|
||||
return exec('adb devices')
|
||||
return exec('adb devices', os.tmpdir())
|
||||
.then(function(output) {
|
||||
var response = output.split('\n');
|
||||
var started_emulator_list = [];
|
||||
@@ -123,7 +124,7 @@ module.exports.list_started = function() {
|
||||
|
||||
// Returns a promise.
|
||||
module.exports.list_targets = function() {
|
||||
return exec('android list targets')
|
||||
return exec('android list targets', os.tmpdir())
|
||||
.then(function(output) {
|
||||
var target_out = output.split('\n');
|
||||
var targets = [];
|
||||
@@ -201,7 +202,7 @@ module.exports.start = function(emulator_ID) {
|
||||
console.log('BOOT COMPLETE');
|
||||
|
||||
//unlock screen
|
||||
return exec('adb -s ' + emulator_id + ' shell input keyevent 82');
|
||||
return exec('adb -s ' + emulator_id + ' shell input keyevent 82', os.tmpdir());
|
||||
}).then(function() {
|
||||
//return the new emulator id for the started emulators
|
||||
return emulator_id;
|
||||
@@ -231,7 +232,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')
|
||||
return exec('adb -s ' + emulator_id + ' shell getprop init.svc.bootanim', os.tmpdir())
|
||||
.then(function(output) {
|
||||
if (output.match(/stopped/)) {
|
||||
return;
|
||||
@@ -309,7 +310,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 + '"')
|
||||
return exec('adb -s ' + resolvedTarget.target + ' install -r "' + apk_path + '"', os.tmpdir())
|
||||
.then(function(output) {
|
||||
if (output.match(/Failure/)) {
|
||||
return Q.reject('Failed to install apk to emulator: ' + output);
|
||||
@@ -319,13 +320,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');
|
||||
return exec('adb -s ' + resolvedTarget.target + ' shell input keyevent 82', os.tmpdir());
|
||||
}).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);
|
||||
return exec(cmd, os.tmpdir());
|
||||
}).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,6 +21,7 @@
|
||||
|
||||
var shell = require('shelljs'),
|
||||
path = require('path'),
|
||||
os = require('os'),
|
||||
Q = require('q'),
|
||||
child_process = require('child_process'),
|
||||
ROOT = path.join(__dirname, '..', '..');
|
||||
@@ -32,7 +33,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']);
|
||||
var adb = child_process.spawn('adb', ['logcat'], {cwd: os.tmpdir()});
|
||||
|
||||
adb.stdout.on('data', function(data) {
|
||||
var lines = data ? data.toString().split('\n') : [];
|
||||
|
||||
@@ -23,8 +23,22 @@ buildscript {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:0.12.+'
|
||||
// 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+'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,16 +46,18 @@ 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 cordova.cordovaSdkVersion
|
||||
buildToolsVersion cordova.cordovaBuildToolsVersion
|
||||
compileSdkVersion cdvCompileSdkVersion
|
||||
buildToolsVersion cdvBuildToolsVersion
|
||||
publishNonDefault true
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_7
|
||||
targetCompatibility JavaVersion.VERSION_1_7
|
||||
sourceCompatibility JavaVersion.VERSION_1_6
|
||||
targetCompatibility JavaVersion.VERSION_1_6
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
|
||||
46
bin/templates/cordova/lib/run.js
vendored
46
bin/templates/cordova/lib/run.js
vendored
@@ -23,11 +23,12 @@ 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 not device is found, it will use a started emulator.
|
||||
* If no 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.
|
||||
@@ -35,26 +36,51 @@ 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 (args[i] == '--debug') {
|
||||
buildFlags.push('--debug');
|
||||
} else if (args[i] == '--release') {
|
||||
buildFlags.push('--release');
|
||||
} else if (args[i] == '--nobuild') {
|
||||
buildFlags.push('--nobuild');
|
||||
if (/^--(debug|release|ant|gradle|nobuild|versionCode=|minSdkVersion=|gradleArg=)/.exec(args[i])) {
|
||||
buildFlags.push(args[i]);
|
||||
} else if (args[i] == '--device') {
|
||||
install_target = '--device';
|
||||
} else if (args[i] == '--emulator') {
|
||||
install_target = '--emulator';
|
||||
} else if (args[i].substring(0, 9) == '--target=') {
|
||||
} else if (/^--target=/.exec(args[i])) {
|
||||
install_target = args[i].substring(9, args[i].length);
|
||||
} else if (args[i] == '--list') {
|
||||
list = true;
|
||||
} else {
|
||||
console.error('ERROR : Run option \'' + args[i] + '\' not recognized.');
|
||||
process.exit(2);
|
||||
console.warn('Option \'' + args[i] + '\' not recognized (ignoring).');
|
||||
}
|
||||
}
|
||||
|
||||
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,13 +26,15 @@ 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.unshift('/s', '/c', cmd);
|
||||
cmd = 'cmd';
|
||||
args = [['/s', '/c', '"' + [cmd].concat(args).map(function(a){if (/^[^"].* .*[^"]/.test(a)) return '"' + a + '"'; return a;}).join(' ')+'"'].join(' ')];
|
||||
cmd = 'cmd';
|
||||
opts.windowsVerbatimArguments = true;
|
||||
}
|
||||
var child = child_process.spawn(cmd, args, {cwd: opt_cwd, stdio: 'inherit'});
|
||||
var child = child_process.spawn(cmd, args, opts);
|
||||
child.on('exit', function(code) {
|
||||
if (code) {
|
||||
d.reject('Error code ' + code + ' for command: ' + cmd + ' with args: ' + args);
|
||||
@@ -45,5 +47,4 @@ 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.0-dev";
|
||||
var VERSION = "3.7.2";
|
||||
|
||||
console.log(VERSION);
|
||||
|
||||
@@ -19,11 +19,6 @@
|
||||
|
||||
// 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 {
|
||||
@@ -31,16 +26,117 @@ buildscript {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:0.12.0+'
|
||||
// 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+'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
task wrapper(type: Wrapper) {
|
||||
gradleVersion = '1.12'
|
||||
// Allow plugins to declare Maven dependencies via build-extras.gradle.
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
ext.multiarch=false
|
||||
task wrapper(type: Wrapper) {
|
||||
gradleVersion = '2.2.1'
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
android {
|
||||
sourceSets {
|
||||
@@ -55,23 +151,29 @@ android {
|
||||
}
|
||||
}
|
||||
|
||||
def versionCodeOverride = cdvVersionCode ? Integer.parseInt(cdvVersionCode) : null
|
||||
def minSdkVersionOverride = cdvMinSdkVersion ? Integer.parseInt(cdvMinSdkVersion) : null
|
||||
|
||||
defaultConfig {
|
||||
versionCode Integer.parseInt(System.env.ANDROID_VERSION_CODE ?: ("" + getVersionCodeFromManifest() + "0"))
|
||||
versionCode versionCodeOverride ?: Integer.parseInt("" + privateHelpers.extractIntFromManifest("versionCode") + "0")
|
||||
if (minSdkVersionOverride != null) {
|
||||
minSdkVersion minSdkVersionOverride
|
||||
}
|
||||
}
|
||||
|
||||
compileSdkVersion cordova.cordovaSdkVersion
|
||||
buildToolsVersion cordova.cordovaBuildToolsVersion
|
||||
compileSdkVersion cdvCompileSdkVersion
|
||||
buildToolsVersion cdvBuildToolsVersion
|
||||
|
||||
if (multiarch || System.env.BUILD_MULTIPLE_APKS) {
|
||||
if (Boolean.valueOf(cdvBuildMultipleApks)) {
|
||||
productFlavors {
|
||||
armv7 {
|
||||
versionCode defaultConfig.versionCode + 2
|
||||
versionCode versionCodeOverride ?: defaultConfig.versionCode + 2
|
||||
ndk {
|
||||
abiFilters "armeabi-v7a", ""
|
||||
}
|
||||
}
|
||||
x86 {
|
||||
versionCode defaultConfig.versionCode + 4
|
||||
versionCode versionCodeOverride ?: defaultConfig.versionCode + 4
|
||||
ndk {
|
||||
abiFilters "x86", ""
|
||||
}
|
||||
@@ -82,21 +184,31 @@ 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_7
|
||||
targetCompatibility JavaVersion.VERSION_1_7
|
||||
sourceCompatibility JavaVersion.VERSION_1_6
|
||||
targetCompatibility JavaVersion.VERSION_1_6
|
||||
}
|
||||
|
||||
if (System.env.RELEASE_SIGNING_PROPERTIES_FILE) {
|
||||
if (cdvReleaseSigningPropertiesFile) {
|
||||
signingConfigs {
|
||||
release {
|
||||
// These must be set or Gradle will complain (even if they are overridden).
|
||||
keyAlias = ""
|
||||
keyPassword = ""
|
||||
keyPassword = "__unset" // And these must be set to non-empty in order to have the signing step added to the task graph.
|
||||
storeFile = null
|
||||
storePassword = ""
|
||||
storePassword = "__unset"
|
||||
}
|
||||
}
|
||||
buildTypes {
|
||||
@@ -104,10 +216,10 @@ android {
|
||||
signingConfig signingConfigs.release
|
||||
}
|
||||
}
|
||||
addSigningProps(System.env.RELEASE_SIGNING_PROPERTIES_FILE, signingConfigs.release)
|
||||
addSigningProps(cdvReleaseSigningPropertiesFile, signingConfigs.release)
|
||||
}
|
||||
if (System.env.DEBUG_SIGNING_PROPERTIES_FILE) {
|
||||
addSigningProps(System.env.DEBUG_SIGNING_PROPERTIES_FILE, signingConfigs.debug)
|
||||
if (cdvDebugSigningPropertiesFile) {
|
||||
addSigningProps(cdvDebugSigningPropertiesFile, signingConfigs.debug)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,41 +229,15 @@ 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 (!System.env.RELEASE_SIGNING_PROPERTIES_FILE) {
|
||||
if (!cdvReleaseSigningPropertiesFile) {
|
||||
return;
|
||||
}
|
||||
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.storePassword)) {
|
||||
android.signingConfigs.release.storePassword = privateHelpers.promptForPassword('Enter key store password: ')
|
||||
}
|
||||
if (!android.signingConfigs.release.keyPassword) {
|
||||
android.signingConfigs.release.keyPassword = promptForPassword('Enter key password: ');
|
||||
if ('__unset'.equals(android.signingConfigs.release.keyPassword)) {
|
||||
android.signingConfigs.release.keyPassword = privateHelpers.promptForPassword('Enter key password: ');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,33 +249,37 @@ 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 ->
|
||||
def props = new Properties()
|
||||
props.load(reader)
|
||||
signingConfig.keyAlias = ensureValueExists(propsFilePath, props, 'keyAlias')
|
||||
signingConfig.keyPassword = props.get('keyPassword')
|
||||
signingConfig.storeFile = RelativePath.parse(true, ensureValueExists(propsFilePath, props, 'storeFile')).getFile(propsFile.getParentFile())
|
||||
signingConfig.storePassword = props.get('storePassword')
|
||||
}
|
||||
def storeFile = new File(privateHelpers.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.storeFile = storeFile
|
||||
signingConfig.storePassword = props.get('storePassword', signingConfig.storePassword)
|
||||
def storeType = props.get('storeType')
|
||||
if (!storeType) {
|
||||
def filename = storeFile.getName().toLowerCase();
|
||||
if (filename.endsWith('.p12') || filename.endsWith('.pfx')) {
|
||||
storeType = 'pkcs12'
|
||||
}
|
||||
}
|
||||
if (storeType) {
|
||||
signingConfig.storeType = storeType
|
||||
}
|
||||
}
|
||||
|
||||
if (file('build-extras.gradle').exists()) {
|
||||
apply from: 'build-extras.gradle'
|
||||
// This can be defined within build-extras.gradle as:
|
||||
// ext.postBuildExtras = { ... code here ... }
|
||||
if (hasProperty('postBuildExtras')) {
|
||||
postBuildExtras()
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ local.properties
|
||||
/gradlew.bat
|
||||
/gradle
|
||||
# Ant builds
|
||||
ant-built
|
||||
ant-build
|
||||
ant-gen
|
||||
# Eclipse builds
|
||||
gen
|
||||
|
||||
200
framework/assets/www/cordova.js
vendored
200
framework/assets/www/cordova.js
vendored
@@ -1,5 +1,5 @@
|
||||
// Platform: android
|
||||
// 8ca0f3b2b87e0759c5236b91c80f18438544409c
|
||||
// 24ab6855470f2dc0662624b597c98585e56a1666
|
||||
/*
|
||||
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.0-dev';
|
||||
var PLATFORM_VERSION_BUILD_LABEL = '3.7.2';
|
||||
// file: src/scripts/require.js
|
||||
|
||||
/*jshint -W079 */
|
||||
@@ -263,11 +263,7 @@ var cordova = {
|
||||
* Called by native code when returning successful result from an action.
|
||||
*/
|
||||
callbackSuccess: function(callbackId, args) {
|
||||
try {
|
||||
cordova.callbackFromNative(callbackId, true, args.status, [args.message], args.keepCallback);
|
||||
} catch (e) {
|
||||
console.log("Error in success callback: " + callbackId + " = "+e);
|
||||
}
|
||||
cordova.callbackFromNative(callbackId, true, args.status, [args.message], args.keepCallback);
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -276,30 +272,40 @@ var cordova = {
|
||||
callbackError: function(callbackId, args) {
|
||||
// TODO: Deprecate callbackSuccess and callbackError in favour of callbackFromNative.
|
||||
// Derive success from status.
|
||||
try {
|
||||
cordova.callbackFromNative(callbackId, false, args.status, [args.message], args.keepCallback);
|
||||
} catch (e) {
|
||||
console.log("Error in error callback: " + callbackId + " = "+e);
|
||||
}
|
||||
cordova.callbackFromNative(callbackId, false, args.status, [args.message], args.keepCallback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Called by native code when returning the result from an action.
|
||||
*/
|
||||
callbackFromNative: function(callbackId, success, status, args, keepCallback) {
|
||||
var callback = cordova.callbacks[callbackId];
|
||||
if (callback) {
|
||||
if (success && status == cordova.callbackStatus.OK) {
|
||||
callback.success && callback.success.apply(null, args);
|
||||
} else if (!success) {
|
||||
callback.fail && callback.fail.apply(null, args);
|
||||
}
|
||||
|
||||
// Clear callback if not expecting any more results
|
||||
if (!keepCallback) {
|
||||
delete cordova.callbacks[callbackId];
|
||||
callbackFromNative: function(callbackId, isSuccess, status, args, keepCallback) {
|
||||
try {
|
||||
var callback = cordova.callbacks[callbackId];
|
||||
if (callback) {
|
||||
if (isSuccess && status == cordova.callbackStatus.OK) {
|
||||
callback.success && callback.success.apply(null, args);
|
||||
} else if (!isSuccess) {
|
||||
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];
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
var msg = "Error in " + (isSuccess ? "Success" : "Error") + " callbackId: " + callbackId + " : " + err;
|
||||
console && console.log && console.log(msg);
|
||||
cordova.fireWindowEvent("cordovacallbackerror", { 'message': msg });
|
||||
throw err;
|
||||
}
|
||||
},
|
||||
addConstructor: function(func) {
|
||||
channel.onCordovaReady.subscribe(function() {
|
||||
@@ -509,9 +515,14 @@ function each(objects, func, context) {
|
||||
|
||||
function clobber(obj, key, value) {
|
||||
exports.replaceHookForTesting(obj, key);
|
||||
obj[key] = value;
|
||||
var needsProperty = false;
|
||||
try {
|
||||
obj[key] = value;
|
||||
} catch (e) {
|
||||
needsProperty = true;
|
||||
}
|
||||
// Getters can only be overridden by getters.
|
||||
if (obj[key] !== value) {
|
||||
if (needsProperty || obj[key] !== value) {
|
||||
utils.defineGetter(obj, key, function() {
|
||||
return value;
|
||||
});
|
||||
@@ -626,7 +637,6 @@ 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.
|
||||
@@ -838,9 +848,6 @@ 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');
|
||||
@@ -1013,6 +1020,37 @@ androidExec.setNativeToJsBridgeMode = function(mode) {
|
||||
}
|
||||
};
|
||||
|
||||
function buildPayload(payload, message) {
|
||||
var payloadKind = message.charAt(0);
|
||||
if (payloadKind == 's') {
|
||||
payload.push(message.slice(1));
|
||||
} else if (payloadKind == 't') {
|
||||
payload.push(true);
|
||||
} else if (payloadKind == 'f') {
|
||||
payload.push(false);
|
||||
} else if (payloadKind == 'N') {
|
||||
payload.push(null);
|
||||
} else if (payloadKind == 'n') {
|
||||
payload.push(+message.slice(1));
|
||||
} else if (payloadKind == 'A') {
|
||||
var data = message.slice(1);
|
||||
payload.push(base64.toArrayBuffer(data));
|
||||
} else if (payloadKind == 'S') {
|
||||
payload.push(window.atob(message.slice(1)));
|
||||
} else if (payloadKind == 'M') {
|
||||
var multipartMessages = message.slice(1);
|
||||
while (multipartMessages !== "") {
|
||||
var spaceIdx = multipartMessages.indexOf(' ');
|
||||
var msgLen = +multipartMessages.slice(0, spaceIdx);
|
||||
var multipartMessage = multipartMessages.substr(spaceIdx + 1, msgLen);
|
||||
multipartMessages = multipartMessages.slice(spaceIdx + msgLen + 1);
|
||||
buildPayload(payload, multipartMessage);
|
||||
}
|
||||
} else {
|
||||
payload.push(JSON.parse(message));
|
||||
}
|
||||
}
|
||||
|
||||
// Processes a single message, as encoded by NativeToJsMessageQueue.java.
|
||||
function processMessage(message) {
|
||||
try {
|
||||
@@ -1026,32 +1064,10 @@ function processMessage(message) {
|
||||
var status = +message.slice(2, spaceIdx);
|
||||
var nextSpaceIdx = message.indexOf(' ', spaceIdx + 1);
|
||||
var callbackId = message.slice(spaceIdx + 1, nextSpaceIdx);
|
||||
var payloadKind = message.charAt(nextSpaceIdx + 1);
|
||||
var payload;
|
||||
if (payloadKind == 's') {
|
||||
payload = message.slice(nextSpaceIdx + 2);
|
||||
} else if (payloadKind == 't') {
|
||||
payload = true;
|
||||
} else if (payloadKind == 'f') {
|
||||
payload = false;
|
||||
} else if (payloadKind == 'N') {
|
||||
payload = null;
|
||||
} else if (payloadKind == 'n') {
|
||||
payload = +message.slice(nextSpaceIdx + 2);
|
||||
} else if (payloadKind == 'A') {
|
||||
var data = message.slice(nextSpaceIdx + 2);
|
||||
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 = arraybuffer.buffer;
|
||||
} else if (payloadKind == 'S') {
|
||||
payload = window.atob(message.slice(nextSpaceIdx + 2));
|
||||
} else {
|
||||
payload = JSON.parse(message.slice(nextSpaceIdx + 1));
|
||||
}
|
||||
cordova.callbackFromNative(callbackId, success, status, [payload], keepCallback);
|
||||
var payloadMessage = message.slice(nextSpaceIdx + 1);
|
||||
var payload = [];
|
||||
buildPayload(payload, payloadMessage);
|
||||
cordova.callbackFromNative(callbackId, success, status, payload, keepCallback);
|
||||
} else {
|
||||
console.log("processMessage failed: invalid message: " + JSON.stringify(message));
|
||||
}
|
||||
@@ -1153,6 +1169,7 @@ 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];
|
||||
|
||||
@@ -1184,21 +1201,19 @@ function replaceNavigator(origNavigator) {
|
||||
for (var key in origNavigator) {
|
||||
if (typeof origNavigator[key] == 'function') {
|
||||
newNavigator[key] = origNavigator[key].bind(origNavigator);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
(function(k) {
|
||||
Object.defineProperty(newNavigator, k, {
|
||||
get: function() {
|
||||
return origNavigator[k];
|
||||
},
|
||||
configurable: true,
|
||||
enumerable: true
|
||||
});
|
||||
})(key);
|
||||
utils.defineGetterSetter(newNavigator,key,function() {
|
||||
return origNavigator[k];
|
||||
});
|
||||
})(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
return newNavigator;
|
||||
}
|
||||
|
||||
if (window.navigator) {
|
||||
window.navigator = replaceNavigator(window.navigator);
|
||||
}
|
||||
@@ -1279,6 +1294,7 @@ 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];
|
||||
|
||||
@@ -1313,16 +1329,13 @@ function replaceNavigator(origNavigator) {
|
||||
for (var key in origNavigator) {
|
||||
if (typeof origNavigator[key] == 'function') {
|
||||
newNavigator[key] = origNavigator[key].bind(origNavigator);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
(function(k) {
|
||||
Object.defineProperty(newNavigator, k, {
|
||||
get: function() {
|
||||
return origNavigator[k];
|
||||
},
|
||||
configurable: true,
|
||||
enumerable: true
|
||||
});
|
||||
})(key);
|
||||
utils.defineGetterSetter(newNavigator,key,function() {
|
||||
return origNavigator[k];
|
||||
});
|
||||
})(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1371,7 +1384,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
|
||||
@@ -1506,12 +1519,14 @@ 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", "overrideBackbutton", [this.numHandlers == 1]);
|
||||
exec(null, null, APP_PLUGIN_NAME, "overrideBackbutton", [this.numHandlers == 1]);
|
||||
};
|
||||
|
||||
// Add hardware MENU and SEARCH button handlers
|
||||
@@ -1522,7 +1537,7 @@ module.exports = {
|
||||
// generic button bind used for volumeup/volumedown buttons
|
||||
var volumeButtonChannel = cordova.addDocumentEventHandler(buttonName + 'button');
|
||||
volumeButtonChannel.onHasSubscribersChange = function() {
|
||||
exec(null, null, "App", "overrideButton", [buttonName, this.numHandlers == 1]);
|
||||
exec(null, null, APP_PLUGIN_NAME, "overrideButton", [buttonName, this.numHandlers == 1]);
|
||||
};
|
||||
}
|
||||
// Inject a listener for the volume buttons on the document.
|
||||
@@ -1532,11 +1547,38 @@ 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(null, null, "App", "show", []);
|
||||
exec(onMessageFromNative, null, APP_PLUGIN_NAME, 'messageChannel', []);
|
||||
exec(null, null, APP_PLUGIN_NAME, "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
|
||||
@@ -1935,4 +1977,4 @@ window.cordova = require('cordova');
|
||||
|
||||
require('cordova/init');
|
||||
|
||||
})();
|
||||
})();
|
||||
|
||||
@@ -23,30 +23,35 @@ buildscript {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
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.+'
|
||||
// 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+'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'android-library'
|
||||
|
||||
android {
|
||||
compileSdkVersion cordova.cordovaSdkVersion
|
||||
buildToolsVersion cordova.cordovaBuildToolsVersion
|
||||
compileSdkVersion cdvCompileSdkVersion
|
||||
buildToolsVersion cdvBuildToolsVersion
|
||||
publishNonDefault true
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_7
|
||||
targetCompatibility JavaVersion.VERSION_1_7
|
||||
sourceCompatibility JavaVersion.VERSION_1_6
|
||||
targetCompatibility JavaVersion.VERSION_1_6
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
|
||||
@@ -18,16 +18,21 @@
|
||||
*/
|
||||
|
||||
import java.util.regex.Pattern
|
||||
import groovy.swing.SwingBuilder
|
||||
|
||||
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
|
||||
String doEnsureValueExists(filePath, props, key) {
|
||||
if (props.get(key) == null) {
|
||||
throw new GradleException(filePath + ': Missing key required "' + key + '"')
|
||||
}
|
||||
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() {
|
||||
@@ -37,7 +42,7 @@ String[] getAvailableBuildTools() {
|
||||
.sort { a, b -> compareVersions(b, a) }
|
||||
}
|
||||
|
||||
String latestBuildToolsAvailable(String minBuildToolsVersion) {
|
||||
String doFindLatestInstalledBuildTools(String minBuildToolsVersion) {
|
||||
def availableBuildToolsVersions
|
||||
try {
|
||||
availableBuildToolsVersions = getAvailableBuildTools()
|
||||
@@ -115,6 +120,46 @@ String getAndroidSdkDir() {
|
||||
androidSdkDir
|
||||
}
|
||||
|
||||
ext.cordovaSdkVersion = System.env.MIN_SDK_VERSION ?: getProjectTarget("android-19")
|
||||
ext.cordovaBuildToolsVersion = latestBuildToolsAvailable("19.1.0")
|
||||
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) }
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
# Indicates whether an apk should be generated for each density.
|
||||
split.density=false
|
||||
# Project target.
|
||||
target=android-19
|
||||
target=android-21
|
||||
apk-configurations=
|
||||
renderscript.opt.level=O0
|
||||
android.library=true
|
||||
|
||||
@@ -41,8 +41,19 @@ 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
|
||||
@@ -100,6 +111,11 @@ 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) {
|
||||
@@ -249,7 +265,7 @@ public class App extends CordovaPlugin {
|
||||
public void exitApp() {
|
||||
this.webView.postMessage("exit", null);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Listen for telephony events: RINGING, OFFHOOK and IDLE
|
||||
@@ -287,7 +303,21 @@ public class App extends CordovaPlugin {
|
||||
};
|
||||
|
||||
// Register the receiver
|
||||
this.cordova.getActivity().registerReceiver(this.telephonyReceiver, intentFilter);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -296,6 +326,6 @@ public class App extends CordovaPlugin {
|
||||
*/
|
||||
public void onDestroy()
|
||||
{
|
||||
this.cordova.getActivity().unregisterReceiver(this.telephonyReceiver);
|
||||
webView.getContext().unregisterReceiver(this.telephonyReceiver);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,6 @@ 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,28 +20,23 @@ 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.apache.cordova.CordovaInterface;
|
||||
import org.apache.cordova.CordovaPlugin;
|
||||
import org.apache.cordova.LOG;
|
||||
import org.json.JSONArray;
|
||||
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;
|
||||
@@ -52,7 +47,6 @@ 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;
|
||||
|
||||
@@ -99,7 +93,6 @@ 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;
|
||||
@@ -108,7 +101,8 @@ 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 CordovaPlugin activityResultCallback = null;
|
||||
protected int activityResultRequestCode;
|
||||
protected CordovaPlugin activityResultCallback;
|
||||
protected boolean activityResultKeepRunning;
|
||||
|
||||
/*
|
||||
@@ -116,9 +110,10 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
||||
*/
|
||||
|
||||
// Draw a splash screen using an image located in the drawable resource directory.
|
||||
// This is not the same as calling super.loadSplashscreen(url)
|
||||
@Deprecated // Use "SplashScreen" preference instead.
|
||||
protected int splashscreen = 0;
|
||||
protected int splashscreenTime = 3000;
|
||||
@Deprecated // Use "SplashScreenDelay" preference instead.
|
||||
protected int splashscreenTime = -1;
|
||||
|
||||
// LoadUrl timeout value in msec (default of 20 sec)
|
||||
protected int loadUrlTimeoutValue = 20000;
|
||||
@@ -237,7 +232,6 @@ 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();
|
||||
@@ -266,9 +260,6 @@ 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)) {
|
||||
@@ -333,12 +324,14 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
||||
public void init(CordovaWebView webView, CordovaWebViewClient webViewClient, CordovaChromeClient webChromeClient) {
|
||||
LOG.d(TAG, "CordovaActivity.init()");
|
||||
|
||||
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);
|
||||
if (splashscreenTime >= 0) {
|
||||
preferences.set("SplashScreenDelay", splashscreenTime);
|
||||
}
|
||||
if (splashscreen != 0) {
|
||||
preferences.set("SplashDrawableId", splashscreen);
|
||||
}
|
||||
|
||||
appView = webView != null ? webView : makeWebView();
|
||||
|
||||
// TODO: Have the views set this themselves.
|
||||
if (preferences.getBoolean("DisallowOverscroll", false)) {
|
||||
@@ -346,9 +339,18 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
||||
}
|
||||
createViews();
|
||||
|
||||
// TODO: Make this a preference (CB-6153)
|
||||
// Setup the hardware volume controls to handle volume control
|
||||
setVolumeControlStream(AudioManager.STREAM_MUSIC);
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -358,36 +360,10 @@ 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);
|
||||
|
||||
//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);
|
||||
}
|
||||
appView.loadUrlIntoView(url, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -397,44 +373,12 @@ 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() {
|
||||
@@ -585,9 +529,6 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
||||
{
|
||||
this.appView.handlePause(this.keepRunning);
|
||||
}
|
||||
|
||||
// hide the splash screen to avoid leaking a window
|
||||
this.removeSplashScreen();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -642,9 +583,6 @@ 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();
|
||||
}
|
||||
@@ -694,28 +632,20 @@ 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) {
|
||||
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;
|
||||
}
|
||||
});
|
||||
JSONArray args = new JSONArray();
|
||||
args.put(title);
|
||||
args.put(message);
|
||||
doSplashScreenAction("spinnerStart", args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop spinner - Must be called from UI thread
|
||||
*/
|
||||
@Deprecated // Call this directly on SplashScreen plugin instead.
|
||||
public void spinnerStop() {
|
||||
if (this.spinnerDialog != null && this.spinnerDialog.isShowing()) {
|
||||
this.spinnerDialog.dismiss();
|
||||
this.spinnerDialog = null;
|
||||
}
|
||||
doSplashScreenAction("spinnerStop", null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -736,7 +666,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) {
|
||||
this.activityResultCallback = command;
|
||||
setActivityResultCallback(command);
|
||||
this.activityResultKeepRunning = this.keepRunning;
|
||||
|
||||
// If multitasking turned on, then disable it for activities that return results
|
||||
@@ -744,8 +674,19 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
||||
this.keepRunning = false;
|
||||
}
|
||||
|
||||
// Start activity
|
||||
super.startActivityForResult(intent, requestCode);
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -755,37 +696,34 @@ 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 data An Intent, which can return result data to the caller (various data can be attached to Intent "extras").
|
||||
* @param intent 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");
|
||||
LOG.d(TAG, "Incoming Result. Request code = " + requestCode);
|
||||
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.
|
||||
this.activityResultCallback = appView.pluginManager.getPlugin(initCallbackClass);
|
||||
callback = this.activityResultCallback;
|
||||
callback = appView.pluginManager.getPlugin(initCallbackClass);
|
||||
}
|
||||
if(callback != null) {
|
||||
initCallbackClass = null;
|
||||
activityResultCallback = 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;
|
||||
}
|
||||
|
||||
@@ -807,8 +745,6 @@ 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);
|
||||
}
|
||||
});
|
||||
@@ -912,62 +848,34 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
||||
}
|
||||
}
|
||||
|
||||
protected Dialog splashDialog;
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the Dialog that displays the splash screen
|
||||
*/
|
||||
@Deprecated
|
||||
public void removeSplashScreen() {
|
||||
if (splashDialog != null && splashDialog.isShowing()) {
|
||||
splashDialog.dismiss();
|
||||
splashDialog = null;
|
||||
}
|
||||
doSplashScreenAction("hide", null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the splash screen over the full Activity
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
@Deprecated
|
||||
protected void showSplashScreen(final int time) {
|
||||
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);
|
||||
preferences.set("SplashScreenDelay", time);
|
||||
doSplashScreenAction("show", null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1012,28 +920,7 @@ public class CordovaActivity extends Activity implements CordovaInterface {
|
||||
LOG.d(TAG, "onMessage(" + id + "," + data + ")");
|
||||
}
|
||||
|
||||
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)) {
|
||||
if ("onReceivedError".equals(id)) {
|
||||
JSONObject d = (JSONObject) data;
|
||||
try {
|
||||
this.onReceivedError(d.getInt("errorCode"), d.getString("description"), d.getString("url"));
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
*/
|
||||
package org.apache.cordova;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
|
||||
import org.apache.cordova.PluginManager;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
@@ -35,12 +37,14 @@ public class CordovaBridge {
|
||||
private NativeToJsMessageQueue jsMessageQueue;
|
||||
private volatile int expectedBridgeSecret = -1; // written by UI thread, read by JS thread.
|
||||
private String loadedUrl;
|
||||
private String appContentUrlPrefix;
|
||||
|
||||
public CordovaBridge(PluginManager pluginManager, NativeToJsMessageQueue jsMessageQueue) {
|
||||
public CordovaBridge(PluginManager pluginManager, NativeToJsMessageQueue jsMessageQueue, String packageName) {
|
||||
this.pluginManager = pluginManager;
|
||||
this.jsMessageQueue = jsMessageQueue;
|
||||
this.appContentUrlPrefix = "content://" + packageName + ".";
|
||||
}
|
||||
|
||||
|
||||
public String jsExec(int bridgeSecret, String service, String action, String callbackId, String arguments) throws JSONException, IllegalAccessException {
|
||||
if (!verifySecret("exec()", bridgeSecret)) {
|
||||
return null;
|
||||
@@ -95,6 +99,8 @@ 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;
|
||||
@@ -107,7 +113,8 @@ public class CordovaBridge {
|
||||
|
||||
/** Called by cordova.js to initialize the bridge. */
|
||||
int generateBridgeSecret() {
|
||||
expectedBridgeSecret = (int)(Math.random() * Integer.MAX_VALUE);
|
||||
SecureRandom randGen = new SecureRandom();
|
||||
expectedBridgeSecret = randGen.nextInt(Integer.MAX_VALUE);
|
||||
return expectedBridgeSecret;
|
||||
}
|
||||
|
||||
@@ -162,7 +169,9 @@ public class CordovaBridge {
|
||||
// Protect against random iframes being able to talk through the bridge.
|
||||
// Trust only file URLs and the start URL's domain.
|
||||
// The extra origin.startsWith("http") is to protect against iframes with data: having "" as origin.
|
||||
if (origin.startsWith("file:") || (origin.startsWith("http") && loadedUrl.startsWith(origin))) {
|
||||
if (origin.startsWith("file:") ||
|
||||
origin.startsWith(this.appContentUrlPrefix) ||
|
||||
(origin.startsWith("http") && loadedUrl.startsWith(origin))) {
|
||||
// Enable the bridge
|
||||
int bridgeMode = Integer.parseInt(defaultValue.substring(9));
|
||||
jsMessageQueue.setBridgeMode(bridgeMode);
|
||||
|
||||
@@ -22,10 +22,14 @@ 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;
|
||||
@@ -67,10 +71,7 @@ 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;
|
||||
@@ -309,7 +310,10 @@ 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, "*/*");
|
||||
}
|
||||
@@ -318,20 +322,41 @@ public class CordovaChromeClient extends WebChromeClient {
|
||||
this.openFileChooser(uploadMsg, acceptType, null);
|
||||
}
|
||||
|
||||
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture)
|
||||
public void openFileChooser(final ValueCallback<Uri> uploadMsg, String acceptType, String capture)
|
||||
{
|
||||
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);
|
||||
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);
|
||||
}
|
||||
|
||||
public ValueCallback<Uri> getValueCallback() {
|
||||
return this.mUploadMessage;
|
||||
|
||||
@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 void destroyLastDialog(){
|
||||
if(lastHandledDialog != null){
|
||||
lastHandledDialog.cancel();
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
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);
|
||||
}
|
||||
}
|
||||
51
framework/src/org/apache/cordova/CordovaHttpAuthHandler.java
Normal file
51
framework/src/org/apache/cordova/CordovaHttpAuthHandler.java
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
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,4 +198,34 @@ 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,13 +61,6 @@ 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;
|
||||
}
|
||||
@@ -78,13 +71,6 @@ 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;
|
||||
}
|
||||
@@ -94,14 +80,7 @@ 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;
|
||||
}
|
||||
|
||||
@@ -110,66 +89,8 @@ 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,6 +50,7 @@ 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;
|
||||
|
||||
/*
|
||||
@@ -61,7 +62,7 @@ import android.widget.FrameLayout;
|
||||
public class CordovaWebView extends WebView {
|
||||
|
||||
public static final String TAG = "CordovaWebView";
|
||||
public static final String CORDOVA_VERSION = "3.7.0-dev";
|
||||
public static final String CORDOVA_VERSION = "3.7.2";
|
||||
|
||||
private HashSet<Integer> boundKeyCodes = new HashSet<Integer>();
|
||||
|
||||
@@ -94,6 +95,7 @@ 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 {
|
||||
|
||||
@@ -152,10 +154,13 @@ public class CordovaWebView extends WebView {
|
||||
super.setWebViewClient(webViewClient);
|
||||
|
||||
pluginManager = new PluginManager(this, this.cordova, pluginEntries);
|
||||
bridge = new CordovaBridge(pluginManager, new NativeToJsMessageQueue(this, cordova));
|
||||
bridge = new CordovaBridge(pluginManager, new NativeToJsMessageQueue(this, cordova), this.cordova.getActivity().getPackageName());
|
||||
resourceApi = new CordovaResourceApi(this.getContext(), pluginManager);
|
||||
|
||||
pluginManager.addService("App", "org.apache.cordova.App");
|
||||
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();
|
||||
initWebViewSettings();
|
||||
exposeJsInterface();
|
||||
}
|
||||
@@ -187,7 +192,14 @@ 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 });
|
||||
@@ -215,8 +227,12 @@ 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.ICE_CREAM_SANDWICH_MR1)
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
|
||||
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();
|
||||
@@ -379,14 +395,17 @@ 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
|
||||
final CordovaWebView me = this;
|
||||
final int currentLoadUrlTimeout = me.loadUrlTimeout;
|
||||
final int loadUrlTimeoutValue = Integer.parseInt(this.getProperty("LoadUrlTimeoutValue", "20000"));
|
||||
final int loadUrlTimeoutValue = preferences.getInteger("LoadUrlTimeoutValue", 20000);
|
||||
|
||||
// Timeout error method
|
||||
final Runnable loadError = new Runnable() {
|
||||
@@ -435,7 +454,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:") || internalWhitelist.isUrlWhiteListed(url)) {
|
||||
if (url.startsWith("file://") || url.startsWith("javascript:") || url.startsWith("about:") || internalWhitelist.isUrlWhiteListed(url)) {
|
||||
super.loadUrl(url);
|
||||
}
|
||||
}
|
||||
@@ -458,9 +477,6 @@ 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
|
||||
@@ -593,39 +609,19 @@ public class CordovaWebView extends WebView {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get string property for activity.
|
||||
*
|
||||
* @param name
|
||||
* @param defaultValue
|
||||
* @return the String value for the named property
|
||||
*/
|
||||
public String getProperty(String name, String defaultValue) {
|
||||
Bundle bundle = this.cordova.getActivity().getIntent().getExtras();
|
||||
if (bundle == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
name = name.toLowerCase(Locale.getDefault());
|
||||
Object p = bundle.get(name);
|
||||
if (p == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
return p.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event)
|
||||
{
|
||||
if(boundKeyCodes.contains(keyCode))
|
||||
{
|
||||
if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
|
||||
this.loadUrl("javascript:cordova.fireDocumentEvent('volumedownbutton');");
|
||||
return true;
|
||||
sendJavascriptEvent("volumedownbutton");
|
||||
return true;
|
||||
}
|
||||
// If volumeup key
|
||||
else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
|
||||
this.loadUrl("javascript:cordova.fireDocumentEvent('volumeupbutton');");
|
||||
return true;
|
||||
sendJavascriptEvent("volumeupbutton");
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -667,7 +663,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)) {
|
||||
this.loadUrl("javascript:cordova.fireDocumentEvent('backbutton');");
|
||||
sendJavascriptEvent("backbutton");
|
||||
return true;
|
||||
} else {
|
||||
// If not bound
|
||||
@@ -682,14 +678,14 @@ public class CordovaWebView extends WebView {
|
||||
// Legacy
|
||||
else if (keyCode == KeyEvent.KEYCODE_MENU) {
|
||||
if (this.lastMenuEventTime < event.getEventTime()) {
|
||||
this.loadUrl("javascript:cordova.fireDocumentEvent('menubutton');");
|
||||
sendJavascriptEvent("menubutton");
|
||||
}
|
||||
this.lastMenuEventTime = event.getEventTime();
|
||||
return super.onKeyUp(keyCode, event);
|
||||
}
|
||||
// If search key
|
||||
else if (keyCode == KeyEvent.KEYCODE_SEARCH) {
|
||||
this.loadUrl("javascript:cordova.fireDocumentEvent('searchbutton');");
|
||||
sendJavascriptEvent("searchbutton");
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -697,6 +693,18 @@ 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:
|
||||
@@ -750,7 +758,7 @@ public class CordovaWebView extends WebView {
|
||||
{
|
||||
LOG.d(TAG, "Handle the pause");
|
||||
// Send pause event to JavaScript
|
||||
this.loadUrl("javascript:try{cordova.fireDocumentEvent('pause');}catch(e){console.log('exception firing pause event from native');};");
|
||||
sendJavascriptEvent("pause");
|
||||
|
||||
// Forward to plugins
|
||||
if (this.pluginManager != null) {
|
||||
@@ -768,9 +776,8 @@ 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);
|
||||
@@ -783,8 +790,8 @@ public class CordovaWebView extends WebView {
|
||||
|
||||
public void handleDestroy()
|
||||
{
|
||||
// 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');};");
|
||||
// Cancel pending timeout timer.
|
||||
loadUrlTimeout++;
|
||||
|
||||
// Load blank page so that JavaScript onunload is called
|
||||
this.loadUrl("about:blank");
|
||||
@@ -828,12 +835,19 @@ 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 class Level16Apis {
|
||||
private static final 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,7 +21,6 @@ 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;
|
||||
@@ -33,6 +32,7 @@ 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,15 +115,45 @@ public class CordovaWebViewClient extends WebViewClient {
|
||||
@Override
|
||||
public void onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler, String host, String realm) {
|
||||
|
||||
// Get the authentication token
|
||||
// Get the authentication token (if specified)
|
||||
AuthenticationToken token = this.getAuthenticationToken(host, realm);
|
||||
if (token != null) {
|
||||
handler.proceed(token.getUserName(), token.getPassword());
|
||||
return;
|
||||
}
|
||||
else {
|
||||
// Handle 401 like we'd normally do!
|
||||
super.onReceivedHttpAuthRequest(view, handler, host, realm);
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -163,8 +193,8 @@ public class CordovaWebViewClient extends WebViewClient {
|
||||
@Override
|
||||
public void onPageFinished(WebView view, String url) {
|
||||
super.onPageFinished(view, url);
|
||||
// Ignore excessive calls.
|
||||
if (!isCurrentlyLoading) {
|
||||
// Ignore excessive calls, if url is not about:blank (CB-8317).
|
||||
if (!isCurrentlyLoading && !url.startsWith("about:")) {
|
||||
return;
|
||||
}
|
||||
isCurrentlyLoading = false;
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
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);
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
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,10 +18,7 @@
|
||||
*/
|
||||
package org.apache.cordova;
|
||||
|
||||
import org.apache.cordova.LOG;
|
||||
|
||||
import android.content.Context;
|
||||
//import android.view.View.MeasureSpec;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
/**
|
||||
@@ -36,6 +33,7 @@ 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);
|
||||
@@ -50,7 +48,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 app.postMessage so that this is more compatible with the API
|
||||
* Note: We are using the core App plugin to send events over the bridge to Javascript
|
||||
*
|
||||
* @param widthMeasureSpec
|
||||
* @param heightMeasureSpec
|
||||
@@ -87,14 +85,12 @@ 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) {
|
||||
if (app != null)
|
||||
app.appView.sendJavascript("cordova.fireDocumentEvent('hidekeyboard');");
|
||||
sendEvent("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) {
|
||||
if (app != null)
|
||||
app.appView.sendJavascript("cordova.fireDocumentEvent('showkeyboard');");
|
||||
sendEvent("showkeyboard");
|
||||
}
|
||||
|
||||
// Update the old height for the next event
|
||||
@@ -102,4 +98,15 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ public class NativeToJsMessageQueue {
|
||||
|
||||
// Set this to true to force plugin results to be encoding as
|
||||
// JS instead of the custom format (useful for benchmarking).
|
||||
// Doesn't work for multipart messages.
|
||||
private static final boolean FORCE_ENCODE_USING_EVAL = false;
|
||||
|
||||
// Disable sending back native->JS messages during an exec() when the active
|
||||
@@ -419,53 +420,43 @@ public class NativeToJsMessageQueue {
|
||||
this.pluginResult = pluginResult;
|
||||
}
|
||||
|
||||
static int calculateEncodedLengthHelper(PluginResult pluginResult) {
|
||||
switch (pluginResult.getMessageType()) {
|
||||
case PluginResult.MESSAGE_TYPE_BOOLEAN: // f or t
|
||||
case PluginResult.MESSAGE_TYPE_NULL: // N
|
||||
return 1;
|
||||
case PluginResult.MESSAGE_TYPE_NUMBER: // n
|
||||
return 1 + pluginResult.getMessage().length();
|
||||
case PluginResult.MESSAGE_TYPE_STRING: // s
|
||||
return 1 + pluginResult.getStrMessage().length();
|
||||
case PluginResult.MESSAGE_TYPE_BINARYSTRING:
|
||||
return 1 + pluginResult.getMessage().length();
|
||||
case PluginResult.MESSAGE_TYPE_ARRAYBUFFER:
|
||||
return 1 + pluginResult.getMessage().length();
|
||||
case PluginResult.MESSAGE_TYPE_MULTIPART:
|
||||
int ret = 1;
|
||||
for (int i = 0; i < pluginResult.getMultipartMessagesSize(); i++) {
|
||||
int length = calculateEncodedLengthHelper(pluginResult.getMultipartMessage(i));
|
||||
int argLength = String.valueOf(length).length();
|
||||
ret += argLength + 1 + length;
|
||||
}
|
||||
return ret;
|
||||
case PluginResult.MESSAGE_TYPE_JSON:
|
||||
default:
|
||||
return pluginResult.getMessage().length();
|
||||
}
|
||||
}
|
||||
|
||||
int calculateEncodedLength() {
|
||||
if (pluginResult == null) {
|
||||
return jsPayloadOrCallbackId.length() + 1;
|
||||
}
|
||||
int statusLen = String.valueOf(pluginResult.getStatus()).length();
|
||||
int ret = 2 + statusLen + 1 + jsPayloadOrCallbackId.length() + 1;
|
||||
switch (pluginResult.getMessageType()) {
|
||||
case PluginResult.MESSAGE_TYPE_BOOLEAN: // f or t
|
||||
case PluginResult.MESSAGE_TYPE_NULL: // N
|
||||
ret += 1;
|
||||
break;
|
||||
case PluginResult.MESSAGE_TYPE_NUMBER: // n
|
||||
ret += 1 + pluginResult.getMessage().length();
|
||||
break;
|
||||
case PluginResult.MESSAGE_TYPE_STRING: // s
|
||||
ret += 1 + pluginResult.getStrMessage().length();
|
||||
break;
|
||||
case PluginResult.MESSAGE_TYPE_BINARYSTRING:
|
||||
ret += 1 + pluginResult.getMessage().length();
|
||||
break;
|
||||
case PluginResult.MESSAGE_TYPE_ARRAYBUFFER:
|
||||
ret += 1 + pluginResult.getMessage().length();
|
||||
break;
|
||||
case PluginResult.MESSAGE_TYPE_JSON:
|
||||
default:
|
||||
ret += pluginResult.getMessage().length();
|
||||
return ret + calculateEncodedLengthHelper(pluginResult);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void encodeAsMessage(StringBuilder sb) {
|
||||
if (pluginResult == null) {
|
||||
sb.append('J')
|
||||
.append(jsPayloadOrCallbackId);
|
||||
return;
|
||||
}
|
||||
int status = pluginResult.getStatus();
|
||||
boolean noResult = status == PluginResult.Status.NO_RESULT.ordinal();
|
||||
boolean resultOk = status == PluginResult.Status.OK.ordinal();
|
||||
boolean keepCallback = pluginResult.getKeepCallback();
|
||||
|
||||
sb.append((noResult || resultOk) ? 'S' : 'F')
|
||||
.append(keepCallback ? '1' : '0')
|
||||
.append(status)
|
||||
.append(' ')
|
||||
.append(jsPayloadOrCallbackId)
|
||||
.append(' ');
|
||||
static void encodeAsMessageHelper(StringBuilder sb, PluginResult pluginResult) {
|
||||
switch (pluginResult.getMessageType()) {
|
||||
case PluginResult.MESSAGE_TYPE_BOOLEAN:
|
||||
sb.append(pluginResult.getMessage().charAt(0)); // t or f.
|
||||
@@ -489,12 +480,42 @@ public class NativeToJsMessageQueue {
|
||||
sb.append('A');
|
||||
sb.append(pluginResult.getMessage());
|
||||
break;
|
||||
case PluginResult.MESSAGE_TYPE_MULTIPART:
|
||||
sb.append('M');
|
||||
for (int i = 0; i < pluginResult.getMultipartMessagesSize(); i++) {
|
||||
PluginResult multipartMessage = pluginResult.getMultipartMessage(i);
|
||||
sb.append(String.valueOf(calculateEncodedLengthHelper(multipartMessage)));
|
||||
sb.append(' ');
|
||||
encodeAsMessageHelper(sb, multipartMessage);
|
||||
}
|
||||
break;
|
||||
case PluginResult.MESSAGE_TYPE_JSON:
|
||||
default:
|
||||
sb.append(pluginResult.getMessage()); // [ or {
|
||||
}
|
||||
}
|
||||
|
||||
void encodeAsMessage(StringBuilder sb) {
|
||||
if (pluginResult == null) {
|
||||
sb.append('J')
|
||||
.append(jsPayloadOrCallbackId);
|
||||
return;
|
||||
}
|
||||
int status = pluginResult.getStatus();
|
||||
boolean noResult = status == PluginResult.Status.NO_RESULT.ordinal();
|
||||
boolean resultOk = status == PluginResult.Status.OK.ordinal();
|
||||
boolean keepCallback = pluginResult.getKeepCallback();
|
||||
|
||||
sb.append((noResult || resultOk) ? 'S' : 'F')
|
||||
.append(keepCallback ? '1' : '0')
|
||||
.append(status)
|
||||
.append(' ')
|
||||
.append(jsPayloadOrCallbackId)
|
||||
.append(' ');
|
||||
|
||||
encodeAsMessageHelper(sb, pluginResult);
|
||||
}
|
||||
|
||||
void encodeAsJsMessage(StringBuilder sb) {
|
||||
if (pluginResult == null) {
|
||||
sb.append(jsPayloadOrCallbackId);
|
||||
|
||||
@@ -110,8 +110,12 @@ 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -232,10 +236,52 @@ public class PluginManager {
|
||||
*/
|
||||
public void onPause(boolean multitasking) {
|
||||
for (CordovaPlugin plugin : this.pluginMap.values()) {
|
||||
plugin.onPause(multitasking);
|
||||
if (plugin != null) {
|
||||
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.
|
||||
*
|
||||
@@ -243,7 +289,9 @@ public class PluginManager {
|
||||
*/
|
||||
public void onResume(boolean multitasking) {
|
||||
for (CordovaPlugin plugin : this.pluginMap.values()) {
|
||||
plugin.onResume(multitasking);
|
||||
if (plugin != null) {
|
||||
plugin.onResume(multitasking);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -252,7 +300,9 @@ public class PluginManager {
|
||||
*/
|
||||
public void onDestroy() {
|
||||
for (CordovaPlugin plugin : this.pluginMap.values()) {
|
||||
plugin.onDestroy();
|
||||
if (plugin != null) {
|
||||
plugin.onDestroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -269,9 +319,11 @@ public class PluginManager {
|
||||
return obj;
|
||||
}
|
||||
for (CordovaPlugin plugin : this.pluginMap.values()) {
|
||||
obj = plugin.onMessage(id, data);
|
||||
if (obj != null) {
|
||||
return obj;
|
||||
if (plugin != null) {
|
||||
obj = plugin.onMessage(id, data);
|
||||
if (obj != null) {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
@@ -282,7 +334,9 @@ public class PluginManager {
|
||||
*/
|
||||
public void onNewIntent(Intent intent) {
|
||||
for (CordovaPlugin plugin : this.pluginMap.values()) {
|
||||
plugin.onNewIntent(intent);
|
||||
if (plugin != null) {
|
||||
plugin.onNewIntent(intent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -320,15 +374,19 @@ public class PluginManager {
|
||||
*/
|
||||
public void onReset() {
|
||||
for (CordovaPlugin plugin : this.pluginMap.values()) {
|
||||
plugin.onReset();
|
||||
if (plugin != null) {
|
||||
plugin.onReset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Uri remapUri(Uri uri) {
|
||||
for (CordovaPlugin plugin : this.pluginMap.values()) {
|
||||
Uri ret = plugin.remapUri(uri);
|
||||
if (ret != null) {
|
||||
return ret;
|
||||
if (plugin != null) {
|
||||
Uri ret = plugin.remapUri(uri);
|
||||
if (ret != null) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
19
framework/src/org/apache/cordova/PluginResult.java
Executable file → Normal file
19
framework/src/org/apache/cordova/PluginResult.java
Executable file → Normal file
@@ -18,6 +18,8 @@
|
||||
*/
|
||||
package org.apache.cordova;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
@@ -29,6 +31,7 @@ public class PluginResult {
|
||||
private boolean keepCallback = false;
|
||||
private String strMessage;
|
||||
private String encodedMessage;
|
||||
private List<PluginResult> multipartMessages;
|
||||
|
||||
public PluginResult(Status status) {
|
||||
this(status, PluginResult.StatusMessages[status.ordinal()]);
|
||||
@@ -80,6 +83,13 @@ public class PluginResult {
|
||||
this.encodedMessage = Base64.encodeToString(data, Base64.NO_WRAP);
|
||||
}
|
||||
|
||||
// The keepCallback and status of multipartMessages are ignored.
|
||||
public PluginResult(Status status, List<PluginResult> multipartMessages) {
|
||||
this.status = status.ordinal();
|
||||
this.messageType = MESSAGE_TYPE_MULTIPART;
|
||||
this.multipartMessages = multipartMessages;
|
||||
}
|
||||
|
||||
public void setKeepCallback(boolean b) {
|
||||
this.keepCallback = b;
|
||||
}
|
||||
@@ -99,6 +109,14 @@ public class PluginResult {
|
||||
return encodedMessage;
|
||||
}
|
||||
|
||||
public int getMultipartMessagesSize() {
|
||||
return multipartMessages.size();
|
||||
}
|
||||
|
||||
public PluginResult getMultipartMessage(int index) {
|
||||
return multipartMessages.get(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* If messageType == MESSAGE_TYPE_STRING, then returns the message string.
|
||||
* Otherwise, returns null.
|
||||
@@ -150,6 +168,7 @@ public class PluginResult {
|
||||
// Use BINARYSTRING when your string may contain null characters.
|
||||
// This is required to work around a bug in the platform :(.
|
||||
public static final int MESSAGE_TYPE_BINARYSTRING = 7;
|
||||
public static final int MESSAGE_TYPE_MULTIPART = 8;
|
||||
|
||||
public static String[] StatusMessages = new String[] {
|
||||
"No result",
|
||||
|
||||
257
framework/src/org/apache/cordova/SplashScreenInternal.java
Normal file
257
framework/src/org/apache/cordova/SplashScreenInternal.java
Normal file
@@ -0,0 +1,257 @@
|
||||
/*
|
||||
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.0-dev",
|
||||
"version": "3.7.2",
|
||||
"description": "cordova-android release",
|
||||
"main": "bin/create",
|
||||
"repository": {
|
||||
@@ -14,7 +14,7 @@
|
||||
],
|
||||
"scripts": {
|
||||
"test": "jasmine-node --color spec",
|
||||
"test-build": "rm -rf \"test create\"; ./bin/create \"test create\" com.test.app Test && \"./test create/cordova/build\" && rm -rf \"test create\""
|
||||
"test-build": "rm -rf \"test create\"; ./bin/create \"test create\" com.test.app 応用 && \"./test create/cordova/build\" && rm -rf \"test create\""
|
||||
},
|
||||
"author": "Apache Software Foundation",
|
||||
"license": "Apache version 2.0",
|
||||
@@ -26,4 +26,4 @@
|
||||
"jasmine-node": "~1",
|
||||
"promise-matchers": "~0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user