diff --git a/bin/lib/create.js b/bin/lib/create.js index 40df3665..c96ea6cf 100755 --- a/bin/lib/create.js +++ b/bin/lib/create.js @@ -26,9 +26,10 @@ var fs = require('fs'); var check_reqs = require('./../templates/cordova/lib/check_reqs'); var ROOT = path.join(__dirname, '..', '..'); -var MIN_SDK_VERSION = 16; +var MIN_SDK_VERSION = 19; var CordovaError = require('cordova-common').CordovaError; +var AndroidStudio = require('../templates/cordova/lib/AndroidStudio'); var AndroidManifest = require('../templates/cordova/lib/AndroidManifest'); // Export all helper functions, and make sure internally within this module, we @@ -54,10 +55,16 @@ function getFrameworkDir (projectPath, shared) { return shared ? path.join(ROOT, 'framework') : path.join(projectPath, 'CordovaLib'); } -function copyJsAndLibrary (projectPath, shared, projectName) { +function copyJsAndLibrary (projectPath, shared, projectName, isLegacy) { var nestedCordovaLibPath = getFrameworkDir(projectPath, false); var srcCordovaJsPath = path.join(ROOT, 'bin', 'templates', 'project', 'assets', 'www', 'cordova.js'); - shell.cp('-f', srcCordovaJsPath, path.join(projectPath, 'assets', 'www', 'cordova.js')); + var app_path = path.join(projectPath, 'app', 'src', 'main'); + + if (isLegacy) { + app_path = projectPath; + } + + shell.cp('-f', srcCordovaJsPath, path.join(app_path, 'assets', 'www', 'cordova.js')); // Copy the cordova.js file to platforms//platform_www/ // The www dir is nuked on each prepare so we keep cordova.js in platform_www @@ -70,7 +77,7 @@ function copyJsAndLibrary (projectPath, shared, projectName) { // Don't fail if there are no old jars. exports.setShellFatal(false, function () { - shell.ls(path.join(projectPath, 'libs', 'cordova-*.jar')).forEach(function (oldJar) { + shell.ls(path.join(app_path, 'libs', 'cordova-*.jar')).forEach(function (oldJar) { console.log('Deleting ' + oldJar); shell.rm('-f', oldJar); }); @@ -136,16 +143,24 @@ function writeProjectProperties (projectPath, target_api) { fs.writeFileSync(dstPath, data); } -function prepBuildFiles (projectPath) { +// This makes no sense, what if you're building with a different build system? +function prepBuildFiles (projectPath, builder) { var buildModule = require(path.resolve(projectPath, 'cordova/lib/builders/builders')); - buildModule.getBuilder('gradle').prepBuildFiles(); + buildModule.getBuilder(builder).prepBuildFiles(); } -function copyBuildRules (projectPath) { +function copyBuildRules (projectPath, isLegacy) { var srcDir = path.join(ROOT, 'bin', 'templates', 'project'); - shell.cp('-f', path.join(srcDir, 'build.gradle'), projectPath); - shell.cp('-f', path.join(srcDir, 'wrapper.gradle'), projectPath); + if (isLegacy) { + // The project's build.gradle is identical to the earlier build.gradle, so it should still work + shell.cp('-f', path.join(srcDir, 'legacy', 'build.gradle'), projectPath); + shell.cp('-f', path.join(srcDir, 'wrapper.gradle'), projectPath); + } else { + shell.cp('-f', path.join(srcDir, 'build.gradle'), projectPath); + shell.cp('-f', path.join(srcDir, 'app', 'build.gradle'), path.join(projectPath, 'app')); + shell.cp('-f', path.join(srcDir, 'wrapper.gradle'), projectPath); + } } function copyScripts (projectPath) { @@ -271,25 +286,37 @@ exports.create = function (project_path, config, options, events) { exports.setShellFatal(true, function () { var project_template_dir = options.customTemplate || path.join(ROOT, 'bin', 'templates', 'project'); + var app_path = path.join(project_path, 'app', 'src', 'main'); + // copy project template - shell.cp('-r', path.join(project_template_dir, 'assets'), project_path); - shell.cp('-r', path.join(project_template_dir, 'res'), project_path); + shell.mkdir('-p', app_path); + shell.cp('-r', path.join(project_template_dir, 'assets'), app_path); + shell.cp('-r', path.join(project_template_dir, 'res'), app_path); shell.cp(path.join(project_template_dir, 'gitignore'), path.join(project_path, '.gitignore')); // Manually create directories that would be empty within the template (since git doesn't track directories). - shell.mkdir(path.join(project_path, 'libs')); + shell.mkdir(path.join(app_path, 'libs')); // copy cordova.js, cordova.jar exports.copyJsAndLibrary(project_path, options.link, safe_activity_name); + // Set up ther Android Studio paths + var java_path = path.join(app_path, 'java'); + var assets_path = path.join(app_path, 'assets'); + var resource_path = path.join(app_path, 'res'); + shell.mkdir('-p', java_path); + shell.mkdir('-p', assets_path); + shell.mkdir('-p', resource_path); + // interpolate the activity name and package var packagePath = package_name.replace(/\./g, path.sep); - var activity_dir = path.join(project_path, 'src', packagePath); + var activity_dir = path.join(java_path, packagePath); var activity_path = path.join(activity_dir, safe_activity_name + '.java'); + shell.mkdir('-p', activity_dir); shell.cp('-f', path.join(project_template_dir, 'Activity.java'), activity_path); shell.sed('-i', /__ACTIVITY__/, safe_activity_name, activity_path); - shell.sed('-i', /__NAME__/, project_name, path.join(project_path, 'res', 'values', 'strings.xml')); + shell.sed('-i', /__NAME__/, project_name, path.join(app_path, 'res', 'values', 'strings.xml')); shell.sed('-i', /__ID__/, package_name, activity_path); var manifest = new AndroidManifest(path.join(project_template_dir, 'AndroidManifest.xml')); @@ -297,7 +324,7 @@ exports.create = function (project_path, config, options, events) { .setTargetSdkVersion(target_api.split('-')[1]) .getActivity().setName(safe_activity_name); - var manifest_path = path.join(project_path, 'AndroidManifest.xml'); + var manifest_path = path.join(app_path, 'AndroidManifest.xml'); manifest.write(manifest_path); exports.copyScripts(project_path); @@ -305,7 +332,7 @@ exports.create = function (project_path, config, options, events) { }); // Link it to local android install. exports.writeProjectProperties(project_path, target_api); - exports.prepBuildFiles(project_path); + exports.prepBuildFiles(project_path, 'studio'); events.emit('log', generateDoneMessage('create', options.link)); }).thenResolve(project_path); }; @@ -326,7 +353,18 @@ exports.update = function (projectPath, options, events) { return Q() .then(function () { - var manifest = new AndroidManifest(path.join(projectPath, 'AndroidManifest.xml')); + var isAndroidStudio = AndroidStudio.isAndroidStudioProject(projectPath); + var isLegacy = !isAndroidStudio; + var manifest = null; + var builder = 'gradle'; + + if (isAndroidStudio) { + manifest = new AndroidManifest(path.join(projectPath, 'app', 'main', 'AndroidManifest.xml')); + builder = 'studio'; + } else { + manifest = new AndroidManifest(path.join(projectPath, 'AndroidManifest.xml')); + builder = 'gradle'; + } if (Number(manifest.getMinSdkVersion()) < MIN_SDK_VERSION) { events.emit('verbose', 'Updating minSdkVersion to ' + MIN_SDK_VERSION + ' in AndroidManifest.xml'); @@ -338,11 +376,11 @@ exports.update = function (projectPath, options, events) { var projectName = manifest.getActivity().getName(); var target_api = check_reqs.get_target(); - exports.copyJsAndLibrary(projectPath, options.link, projectName); + exports.copyJsAndLibrary(projectPath, options.link, projectName, isLegacy); exports.copyScripts(projectPath); - exports.copyBuildRules(projectPath); + exports.copyBuildRules(projectPath, isLegacy); exports.writeProjectProperties(projectPath, target_api); - exports.prepBuildFiles(projectPath); + exports.prepBuildFiles(projectPath, builder); events.emit('log', generateDoneMessage('update', options.link)); }).thenResolve(projectPath); }; diff --git a/bin/templates/cordova/Api.js b/bin/templates/cordova/Api.js index 107108ea..e97f5380 100644 --- a/bin/templates/cordova/Api.js +++ b/bin/templates/cordova/Api.js @@ -56,6 +56,7 @@ function setupEvents (externalEventEmitter) { function Api (platform, platformRootDir, events) { this.platform = PLATFORM; this.root = path.resolve(__dirname, '..'); + this.builder = 'gradle'; setupEvents(events); @@ -71,6 +72,7 @@ function Api (platform, platformRootDir, events) { strings: path.join(self.root, 'res/values/strings.xml'), manifest: path.join(self.root, 'AndroidManifest.xml'), build: path.join(self.root, 'build'), + javaSrc: path.join(self.root, 'src'), // NOTE: Due to platformApi spec we need to return relative paths here cordovaJs: 'bin/templates/project/assets/www/cordova.js', cordovaJsSrc: 'cordova-js-src' @@ -79,10 +81,13 @@ function Api (platform, platformRootDir, events) { // XXX Override some locations for Android Studio projects if (AndroidStudio.isAndroidStudioProject(self.root) === true) { selfEvents.emit('log', 'Android Studio project detected'); + this.builder = 'studio'; this.android_studio = true; this.locations.configXml = path.join(self.root, 'app/src/main/res/xml/config.xml'); - this.locations.strings = path.join(self.root, 'app/src/main/res/xml/strings.xml'); + this.locations.strings = path.join(self.root, 'app/src/main/res/values/strings.xml'); this.locations.manifest = path.join(self.root, 'app/src/main/AndroidManifest.xml'); + // We could have Java Source, we could have other languages + this.locations.javaSrc = path.join(self.root, 'app/src/main/java/'); this.locations.www = path.join(self.root, 'app/src/main/assets/www'); this.locations.res = path.join(self.root, 'app/src/main/res'); } @@ -226,7 +231,6 @@ Api.prototype.addPlugin = function (plugin, installOptions) { // CB-11964: Do a clean when installing the plugin code to get around // the Gradle bug introduced by the Android Gradle Plugin Version 2.2 // TODO: Delete when the next version of Android Gradle plugin comes out - // Since clean doesn't just clean the build, it also wipes out www, we need // to pass additional options. @@ -243,9 +247,9 @@ Api.prototype.addPlugin = function (plugin, installOptions) { return PluginManager.get(self.platform, self.locations, project).addPlugin(plugin, installOptions); }).then(function () { if (plugin.getFrameworks(this.platform).length === 0) return; - selfEvents.emit('verbose', 'Updating build files since android plugin contained '); - require('./lib/builders/builders').getBuilder('gradle').prepBuildFiles(); + // This should pick the correct builder, not just get gradle + require('./lib/builders/builders').getBuilder(this.builder).prepBuildFiles(); }.bind(this)) // CB-11022 Return truthy value to prevent running prepare after .thenResolve(true); @@ -278,7 +282,7 @@ Api.prototype.removePlugin = function (plugin, uninstallOptions) { if (plugin.getFrameworks(this.platform).length === 0) return; selfEvents.emit('verbose', 'Updating build files since android plugin contained '); - require('./lib/builders/builders').getBuilder('gradle').prepBuildFiles(); + require('./lib/builders/builders').getBuilder(this.builder).prepBuildFiles(); }.bind(this)) // CB-11022 Return truthy value to prevent running prepare after .thenResolve(true); @@ -331,6 +335,9 @@ Api.prototype.removePlugin = function (plugin, uninstallOptions) { */ Api.prototype.build = function (buildOptions) { var self = this; + if (this.android_studio) { + buildOptions.studio = true; + } return require('./lib/check_reqs').run().then(function () { return require('./lib/build').run.call(self, buildOptions); }).then(function (buildResults) { @@ -374,6 +381,14 @@ Api.prototype.run = function (runOptions) { */ Api.prototype.clean = function (cleanOptions) { var self = this; + if (this.android_studio) { + // This will lint, checking for null won't + if (typeof cleanOptions === 'undefined') { + cleanOptions = {}; + } + cleanOptions.studio = true; + } + return require('./lib/check_reqs').run().then(function () { return require('./lib/build').runClean.call(self, cleanOptions); }).then(function () { diff --git a/bin/templates/cordova/lib/AndroidStudio.js b/bin/templates/cordova/lib/AndroidStudio.js index 860301af..fbcb926f 100644 --- a/bin/templates/cordova/lib/AndroidStudio.js +++ b/bin/templates/cordova/lib/AndroidStudio.js @@ -11,8 +11,8 @@ var fs = require('fs'); var CordovaError = require('cordova-common').CordovaError; module.exports.isAndroidStudioProject = function isAndroidStudioProject (root) { - var eclipseFiles = ['AndroidManifest.xml', 'libs', 'res', 'project.properties', 'platform_www']; - var androidStudioFiles = ['app', 'gradle', 'app/src/main/res']; + var eclipseFiles = ['AndroidManifest.xml', 'libs', 'res']; + var androidStudioFiles = ['app', 'app/src/main']; // assume it is an AS project and not an Eclipse project var isEclipse = false; diff --git a/bin/templates/cordova/lib/build.js b/bin/templates/cordova/lib/build.js index 7c2238ac..e33cfae0 100644 --- a/bin/templates/cordova/lib/build.js +++ b/bin/templates/cordova/lib/build.js @@ -35,7 +35,7 @@ function parseOpts (options, resolvedTarget, projectRoot) { options = options || {}; options.argv = nopt({ gradle: Boolean, - ant: Boolean, + studio: Boolean, prepenv: Boolean, versionCode: String, minSdkVersion: String, @@ -47,15 +47,22 @@ function parseOpts (options, resolvedTarget, projectRoot) { keystoreType: String }, {}, options.argv, 0); + // Android Studio Build method is the default var ret = { buildType: options.release ? 'release' : 'debug', - buildMethod: process.env.ANDROID_BUILD || 'gradle', + buildMethod: process.env.ANDROID_BUILD || 'studio', prepEnv: options.argv.prepenv, arch: resolvedTarget && resolvedTarget.arch, extraArgs: [] }; - if (options.argv.ant || options.argv.gradle) { ret.buildMethod = options.argv.ant ? 'ant' : 'gradle'; } + if (options.argv.gradle || options.argv.studio) { + ret.buildMethod = options.argv.studio ? 'studio' : 'gradle'; + } + + // This comes from cordova/run + if (options.studio) ret.buildMethod = 'studio'; + if (options.gradle) ret.buildMethod = 'gradle'; if (options.nobuild) ret.buildMethod = 'none'; @@ -142,6 +149,7 @@ module.exports.runClean = function (options) { */ module.exports.run = function (options, optResolvedTarget) { var opts = parseOpts(options, optResolvedTarget, this.root); + console.log(opts.buildMethod); var builder = builders.getBuilder(opts.buildMethod); return builder.prepEnv(opts).then(function () { if (opts.prepEnv) { diff --git a/bin/templates/cordova/lib/builders/AntBuilder.js b/bin/templates/cordova/lib/builders/AntBuilder.js deleted file mode 100644 index cc08c232..00000000 --- a/bin/templates/cordova/lib/builders/AntBuilder.js +++ /dev/null @@ -1,153 +0,0 @@ -/* - Licensed to the Apache Software Foundation (ASF) under one - or more contributor license agreements. See the NOTICE file - distributed with this work for additional information - regarding copyright ownership. The ASF licenses this file - to you under the Apache License, Version 2.0 (the - "License"); you may not use this file except in compliance - with the License. You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, - software distributed under the License is distributed on an - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, either express or implied. See the License for the - specific language governing permissions and limitations - under the License. -*/ -/* eslint no-unused-vars: 0 */ - -var Q = require('q'); -var fs = require('fs'); -var path = require('path'); -var util = require('util'); -var shell = require('shelljs'); -var spawn = require('cordova-common').superspawn.spawn; -var CordovaError = require('cordova-common').CordovaError; -var check_reqs = require('../check_reqs'); - -var SIGNING_PROPERTIES = '-signing.properties'; -var MARKER = 'YOUR CHANGES WILL BE ERASED!'; -var TEMPLATE = - '# This file is automatically generated.\n' + - '# Do not modify this file -- ' + MARKER + '\n'; - -var GenericBuilder = require('./GenericBuilder'); - -function AntBuilder (projectRoot) { - GenericBuilder.call(this, projectRoot); - - this.binDirs = {ant: this.binDirs.ant}; -} - -util.inherits(AntBuilder, GenericBuilder); - -AntBuilder.prototype.getArgs = function (cmd, opts) { - var args = [cmd, '-f', path.join(this.root, 'build.xml')]; - // custom_rules.xml is required for incremental builds. - if (hasCustomRules(this.root)) { - args.push('-Dout.dir=ant-build', '-Dgen.absolute.dir=ant-gen'); - } - if (opts.packageInfo) { - args.push('-propertyfile=' + path.join(this.root, opts.buildType + SIGNING_PROPERTIES)); - } - return args; -}; - -AntBuilder.prototype.prepEnv = function (opts) { - var self = this; - return check_reqs.check_ant().then(function () { - // Copy in build.xml on each build so that: - // A) we don't require the Android SDK at project creation time, and - // B) we always use the SDK's latest version of it. - /* jshint -W069 */ - var sdkDir = process.env['ANDROID_HOME']; - /* jshint +W069 */ - var buildTemplate = fs.readFileSync(path.join(sdkDir, 'tools', 'lib', 'build.template'), 'utf8'); - function writeBuildXml (projectPath) { - var newData = buildTemplate.replace('PROJECT_NAME', self.extractRealProjectNameFromManifest()); - fs.writeFileSync(path.join(projectPath, 'build.xml'), newData); - if (!fs.existsSync(path.join(projectPath, 'local.properties'))) { - fs.writeFileSync(path.join(projectPath, 'local.properties'), TEMPLATE); - } - } - writeBuildXml(self.root); - var propertiesObj = self.readProjectProperties(); - var subProjects = propertiesObj.libs; - for (var i = 0; i < subProjects.length; ++i) { - writeBuildXml(path.join(self.root, subProjects[i])); - } - if (propertiesObj.systemLibs.length > 0) { - throw new CordovaError('Project contains at least one plugin that requires a system library. This is not supported with ANT. Use gradle instead.'); - } - - var propertiesFile = opts.buildType + SIGNING_PROPERTIES; - var propertiesFilePath = path.join(self.root, propertiesFile); - if (opts.packageInfo) { - fs.writeFileSync(propertiesFilePath, TEMPLATE + opts.packageInfo.toProperties()); - } else if (isAutoGenerated(propertiesFilePath)) { - shell.rm('-f', propertiesFilePath); - } - }); -}; - -/* - * Builds the project with ant. - * Returns a promise. - */ -AntBuilder.prototype.build = function (opts) { - // Without our custom_rules.xml, we need to clean before building. - var ret = Q(); - if (!hasCustomRules(this.root)) { - // clean will call check_ant() for us. - ret = this.clean(opts); - } - - var args = this.getArgs(opts.buildType === 'debug' ? 'debug' : 'release', opts); - return check_reqs.check_ant().then(function () { - return spawn('ant', args, {stdio: 'pipe'}); - }).progress(function (stdio) { - if (stdio.stderr) { - process.stderr.write(stdio.stderr); - } else { - process.stdout.write(stdio.stdout); - } - }).catch(function (error) { - if (error.toString().indexOf('Unable to resolve project target') >= 0) { - return check_reqs.check_android_target(error).then(function () { - // If due to some odd reason - check_android_target succeeds - // we should still fail here. - return Q.reject(error); - }); - } - return Q.reject(error); - }); -}; - -AntBuilder.prototype.clean = function (opts) { - var args = this.getArgs('clean', opts); - var self = this; - return check_reqs.check_ant().then(function () { - return spawn('ant', args, {stdio: 'inherit'}); - }).then(function () { - shell.rm('-rf', path.join(self.root, 'out')); - - ['debug', 'release'].forEach(function (config) { - var propertiesFilePath = path.join(self.root, config + SIGNING_PROPERTIES); - if (isAutoGenerated(propertiesFilePath)) { - shell.rm('-f', propertiesFilePath); - } - }); - }); -}; - -module.exports = AntBuilder; - -function hasCustomRules (projectRoot) { - return fs.existsSync(path.join(projectRoot, 'custom_rules.xml')); -} - -function isAutoGenerated (file) { - return fs.existsSync(file) && fs.readFileSync(file, 'utf8').indexOf(MARKER) > 0; -} diff --git a/bin/templates/cordova/lib/builders/GenericBuilder.js b/bin/templates/cordova/lib/builders/GenericBuilder.js index 6515533a..892aa38f 100644 --- a/bin/templates/cordova/lib/builders/GenericBuilder.js +++ b/bin/templates/cordova/lib/builders/GenericBuilder.js @@ -24,20 +24,15 @@ var fs = require('fs'); var path = require('path'); var shell = require('shelljs'); var events = require('cordova-common').events; -var CordovaError = require('cordova-common').CordovaError; function GenericBuilder (projectDir) { this.root = projectDir || path.resolve(__dirname, '../../..'); this.binDirs = { - ant: path.join(this.root, hasCustomRules(this.root) ? 'ant-build' : 'bin'), + studio: path.join(this.root, 'app', 'build', 'outputs', 'apk'), gradle: path.join(this.root, 'build', 'outputs', 'apk') }; } -function hasCustomRules (projectRoot) { - return fs.existsSync(path.join(projectRoot, 'custom_rules.xml')); -} - GenericBuilder.prototype.prepEnv = function () { return Q(); }; @@ -59,37 +54,6 @@ GenericBuilder.prototype.findOutputApks = function (build_type, arch) { }, []).sort(apkSorter); }; -GenericBuilder.prototype.readProjectProperties = function () { - function findAllUniq (data, r) { - var s = {}; - var m; - while ((m = r.exec(data))) { - s[m[1]] = 1; - } - return Object.keys(s); - } - - var data = fs.readFileSync(path.join(this.root, 'project.properties'), 'utf8'); - return { - libs: findAllUniq(data, /^\s*android\.library\.reference\.\d+=(.*)(?:\s|$)/mg), - gradleIncludes: findAllUniq(data, /^\s*cordova\.gradle\.include\.\d+=(.*)(?:\s|$)/mg), - systemLibs: findAllUniq(data, /^\s*cordova\.system\.library\.\d+=(.*)(?:\s|$)/mg) - }; -}; - -GenericBuilder.prototype.extractRealProjectNameFromManifest = function () { - var manifestPath = path.join(this.root, 'AndroidManifest.xml'); - var manifestData = fs.readFileSync(manifestPath, 'utf8'); - var m = /= 0) { + return check_reqs.check_android_target(error).then(function () { + // If due to some odd reason - check_android_target succeeds + // we should still fail here. + return Q.reject(error); + }); + } + return Q.reject(error); + }); +}; + +StudioBuilder.prototype.clean = function (opts) { + var builder = this; + var wrapper = path.join(this.root, 'gradlew'); + var args = builder.getArgs('clean', opts); + return Q().then(function () { + return spawn(wrapper, args, {stdio: 'inherit'}); + }) + .then(function () { + shell.rm('-rf', path.join(builder.root, 'out')); + + ['debug', 'release'].forEach(function (config) { + var propertiesFilePath = path.join(builder.root, config + SIGNING_PROPERTIES); + if (isAutoGenerated(propertiesFilePath)) { + shell.rm('-f', propertiesFilePath); + } + }); + }); +}; + +module.exports = StudioBuilder; + +function isAutoGenerated (file) { + return fs.existsSync(file) && fs.readFileSync(file, 'utf8').indexOf(MARKER) > 0; +} diff --git a/bin/templates/cordova/lib/builders/builders.js b/bin/templates/cordova/lib/builders/builders.js index a08a5881..aedf9be5 100644 --- a/bin/templates/cordova/lib/builders/builders.js +++ b/bin/templates/cordova/lib/builders/builders.js @@ -20,8 +20,8 @@ var CordovaError = require('cordova-common').CordovaError; var knownBuilders = { - ant: 'AntBuilder', gradle: 'GradleBuilder', + studio: 'StudioBuilder', none: 'GenericBuilder' }; diff --git a/bin/templates/cordova/lib/device.js b/bin/templates/cordova/lib/device.js index e30d044e..84b50947 100644 --- a/bin/templates/cordova/lib/device.js +++ b/bin/templates/cordova/lib/device.js @@ -81,7 +81,7 @@ module.exports.install = function (target, buildResults) { return module.exports.resolveTarget(target); }).then(function (resolvedTarget) { var apk_path = build.findBestApkForArchitecture(buildResults, resolvedTarget.arch); - var manifest = new AndroidManifest(path.join(__dirname, '../../AndroidManifest.xml')); + var manifest = new AndroidManifest(path.join(__dirname, '../../app/src/main/AndroidManifest.xml')); var pkgName = manifest.getPackageId(); var launchName = pkgName + '/.' + manifest.getActivity().getName(); events.emit('log', 'Using apk: ' + apk_path); diff --git a/bin/templates/cordova/lib/emulator.js b/bin/templates/cordova/lib/emulator.js index edb7c800..0a23084e 100644 --- a/bin/templates/cordova/lib/emulator.js +++ b/bin/templates/cordova/lib/emulator.js @@ -432,7 +432,12 @@ module.exports.resolveTarget = function (target) { module.exports.install = function (givenTarget, buildResults) { var target; - var manifest = new AndroidManifest(path.join(__dirname, '../../AndroidManifest.xml')); + // We need to find the proper path to the Android Manifest + var manifestPath = path.join(__dirname, '..', '..', 'app', 'src', 'main', 'AndroidManifest.xml'); + if (buildResults.buildMethod === 'gradle') { + manifestPath = path.join(__dirname, '../../AndroidManifest.xml'); + } + var manifest = new AndroidManifest(manifestPath); var pkgName = manifest.getPackageId(); // resolve the target emulator diff --git a/bin/templates/cordova/lib/pluginHandlers.js b/bin/templates/cordova/lib/pluginHandlers.js index d14baba6..842489ae 100644 --- a/bin/templates/cordova/lib/pluginHandlers.js +++ b/bin/templates/cordova/lib/pluginHandlers.js @@ -1,7 +1,4 @@ /* - * - * Copyright 2013 Anis Kadri - * * Licensed 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 @@ -33,8 +30,19 @@ var handlers = { var dest = path.join(obj.targetDir, path.basename(obj.src)); + // TODO: This code needs to be replaced, since the core plugins need to be re-mapped to a different location in + // a later plugins release. This is for legacy plugins to work with Cordova. + if (options && options.android_studio === true) { - dest = path.join('app/src/main/java', obj.targetDir.substring(4), path.basename(obj.src)); + // If a Java file is using the new directory structure, don't penalize it + if (!obj.targetDir.includes('app/src/main')) { + if (obj.src.endsWith('.java')) { + dest = path.join('app/src/main/java', obj.targetDir.substring(4), path.basename(obj.src)); + } else if (obj.src.endsWith('.xml')) { + // We are making a huge assumption here that XML files will be going to res/xml or values/xml + dest = path.join('app/src/main', obj.targetDir, path.basename(obj.src)); + } + } } if (options && options.force) { @@ -71,10 +79,18 @@ var handlers = { }, 'resource-file': { install: function (obj, plugin, project, options) { - copyFile(plugin.dir, obj.src, project.projectDir, path.normalize(obj.target), !!(options && options.link)); + var dest = path.normalize(obj.target); + if (options && options.android_studio === true) { + dest = path.join('app/src/main', dest); + } + copyFile(plugin.dir, obj.src, project.projectDir, dest, !!(options && options.link)); }, uninstall: function (obj, plugin, project, options) { - removeFile(project.projectDir, path.normalize(obj.target)); + var dest = path.normalize(obj.target); + if (options && options.android_studio === true) { + dest = path.join('app/src/main', dest); + } + removeFile(project.projectDir, dest); } }, 'framework': { diff --git a/bin/templates/cordova/lib/prepare.js b/bin/templates/cordova/lib/prepare.js index 39f9ae4e..ac63f8a6 100644 --- a/bin/templates/cordova/lib/prepare.js +++ b/bin/templates/cordova/lib/prepare.js @@ -199,7 +199,8 @@ function updateProjectAccordingTo (platformConfig, locations) { .setTargetSdkVersion(platformConfig.getPreference('android-targetSdkVersion', 'android')) .write(); - var javaPattern = path.join(locations.root, 'src', manifestId.replace(/\./g, '/'), '*.java'); + // Java file paths shouldn't be hard coded + var javaPattern = path.join(locations.javaSrc, manifestId.replace(/\./g, '/'), '*.java'); var java_files = shell.ls(javaPattern).filter(function (f) { return shell.grep(/extends\s+CordovaActivity/g, f); }); @@ -210,7 +211,7 @@ function updateProjectAccordingTo (platformConfig, locations) { events.emit('log', 'Multiple candidate Java files that extend CordovaActivity found. Guessing at the first one, ' + java_files[0]); } - var destFile = path.join(locations.root, 'src', androidPkgName.replace(/\./g, '/'), path.basename(java_files[0])); + var destFile = path.join(locations.root, 'app', 'src', 'main', 'java', androidPkgName.replace(/\./g, '/'), path.basename(java_files[0])); shell.mkdir('-p', path.dirname(destFile)); shell.sed(/package [\w\.]*;/, 'package ' + androidPkgName + ';', java_files[0]).to(destFile); events.emit('verbose', 'Wrote out Android package name "' + androidPkgName + '" to ' + destFile); diff --git a/bin/templates/project/app/build.gradle b/bin/templates/project/app/build.gradle new file mode 100644 index 00000000..c5f5b233 --- /dev/null +++ b/bin/templates/project/app/build.gradle @@ -0,0 +1,324 @@ +/* + 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. +*/ + +apply plugin: 'com.android.application' + +buildscript { + repositories { + mavenCentral() + jcenter() + maven { + url "https://maven.google.com" + } + } + + dependencies { + classpath 'com.android.tools.build:gradle:3.0.0' + } +} + +// Allow plugins to declare Maven dependencies via build-extras.gradle. +allprojects { + repositories { + mavenCentral(); + jcenter() + } +} + +task wrapper(type: Wrapper) { + gradleVersion = '4.1.0' +} + +// 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 = null; + } + // The value for android.buildToolsVersion. + if (!project.hasProperty('cdvBuildToolsVersion')) { + cdvBuildToolsVersion = null; + } + // 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 = null + } + // Whether to append a 0 "abi digit" to versionCode when only a single APK is build + if (!project.hasProperty('cdvVersionCodeForceAbiDigit')) { + cdvVersionCodeForceAbiDigit = null + } + // .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 + } + + // Plugin gradle extensions can append to this to have code run at the end. + cdvPluginPostBuildExtras = [] +} + +// PLUGIN GRADLE EXTENSIONS START +// PLUGIN GRADLE EXTENSIONS END + +def hasBuildExtras = file('build-extras.gradle').exists() +if (hasBuildExtras) { + apply from: 'build-extras.gradle' +} + +// Set property defaults after extension .gradle files. +if (ext.cdvCompileSdkVersion == null) { + ext.cdvCompileSdkVersion = privateHelpers.getProjectTarget() + //ext.cdvCompileSdkVersion = project.ext.defaultCompileSdkVersion +} +if (ext.cdvBuildToolsVersion == null) { + ext.cdvBuildToolsVersion = privateHelpers.findLatestInstalledBuildTools() + //ext.cdvBuildToolsVersion = project.ext.defaultBuildToolsVersion +} +if (ext.cdvDebugSigningPropertiesFile == null && file('debug-signing.properties').exists()) { + ext.cdvDebugSigningPropertiesFile = 'debug-signing.properties' +} +if (ext.cdvReleaseSigningPropertiesFile == null && file('release-signing.properties').exists()) { + ext.cdvReleaseSigningPropertiesFile = 'release-signing.properties' +} + +// Cast to appropriate types. +ext.cdvBuildMultipleApks = cdvBuildMultipleApks == null ? false : cdvBuildMultipleApks.toBoolean(); +ext.cdvVersionCodeForceAbiDigit = cdvVersionCodeForceAbiDigit == null ? false : cdvVersionCodeForceAbiDigit.toBoolean(); +ext.cdvMinSdkVersion = cdvMinSdkVersion == null ? null : defaultMinSdkVersion +ext.cdvVersionCode = cdvVersionCode == null ? null : Integer.parseInt('' + cdvVersionCode) + +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('cdvVersionCodeForceAbiDigit=' + cdvVersionCodeForceAbiDigit) + println('cdvMinSdkVersion=' + cdvMinSdkVersion) + println('cdvBuildMultipleApks=' + cdvBuildMultipleApks) + println('cdvReleaseSigningPropertiesFile=' + cdvReleaseSigningPropertiesFile) + println('cdvDebugSigningPropertiesFile=' + cdvDebugSigningPropertiesFile) + println('cdvBuildArch=' + cdvBuildArch) + println('computedVersionCode=' + android.defaultConfig.versionCode) + android.productFlavors.each { flavor -> + println('computed' + flavor.name.capitalize() + 'VersionCode=' + flavor.versionCode) + } +} + +android { + + defaultConfig { + versionCode cdvVersionCode ?: new BigInteger("" + privateHelpers.extractIntFromManifest("versionCode")) + applicationId privateHelpers.extractStringFromManifest("package") + + if (cdvMinSdkVersion != null) { + minSdkVersion cdvMinSdkVersion + } + } + + lintOptions { + abortOnError false; + } + + compileSdkVersion cdvCompileSdkVersion + buildToolsVersion cdvBuildToolsVersion + + //This code exists for Crosswalk and other Native APIs. + //By default, we multiply the existing version code in the Android Manifest by 10 and + //add a number for each architecture. If you are not using Crosswalk or SQLite, you can + //ignore this chunk of code, and your version codes will be respected. + + if (Boolean.valueOf(cdvBuildMultipleApks)) { + flavorDimensions "default" + + productFlavors { + armeabi { + versionCode defaultConfig.versionCode*10 + 1 + ndk { + abiFilters = ["armeabi"] + } + } + armv7 { + versionCode defaultConfig.versionCode*10 + 2 + ndk { + abiFilters = ["armeabi-v7a"] + } + } + arm64 { + versionCode defaultConfig.versionCode*10 + 3 + ndk { + abiFilters = ["arm64-v8a"] + } + } + x86 { + versionCode defaultConfig.versionCode*10 + 4 + ndk { + abiFilters = ["x86"] + } + } + x86_64 { + versionCode defaultConfig.versionCode*10 + 5 + ndk { + abiFilters = ["x86_64"] + } + } + } + } else if (Boolean.valueOf(cdvVersionCodeForceAbiDigit)) { + // This provides compatibility to the default logic for versionCode before cordova-android 5.2.0 + defaultConfig { + versionCode defaultConfig.versionCode*10 + } + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + if (cdvReleaseSigningPropertiesFile) { + signingConfigs { + release { + // These must be set or Gradle will complain (even if they are overridden). + keyAlias = "" + keyPassword = "__unset" // And these must be set to non-empty in order to have the signing step added to the task graph. + storeFile = null + storePassword = "__unset" + } + } + buildTypes { + release { + signingConfig signingConfigs.release + } + } + addSigningProps(cdvReleaseSigningPropertiesFile, signingConfigs.release) + } + if (cdvDebugSigningPropertiesFile) { + addSigningProps(cdvDebugSigningPropertiesFile, signingConfigs.debug) + } +} + +/* + * WARNING: Cordova Lib and platform scripts do management inside of this code here, + * if you are adding the dependencies manually, do so outside the comments, otherwise + * the Cordova tools will overwrite them + */ + + +dependencies { + implementation fileTree(dir: 'libs', include: '*.jar') + // SUB-PROJECT DEPENDENCIES START + debugCompile(project(path: ":CordovaLib", configuration: "debug")) + releaseCompile(project(path: ":CordovaLib", configuration: "release")) + // SUB-PROJECT DEPENDENCIES END +} + +def promptForReleaseKeyPassword() { + if (!cdvReleaseSigningPropertiesFile) { + return; + } + if ('__unset'.equals(android.signingConfigs.release.storePassword)) { + android.signingConfigs.release.storePassword = privateHelpers.promptForPassword('Enter key store password: ') + } + if ('__unset'.equals(android.signingConfigs.release.keyPassword)) { + android.signingConfigs.release.keyPassword = privateHelpers.promptForPassword('Enter key password: '); + } +} + +gradle.taskGraph.whenReady { taskGraph -> + taskGraph.getAllTasks().each() { task -> + if(['validateReleaseSigning', 'validateSigningRelease', 'validateSigningArmv7Release', 'validateSigningX76Release'].contains(task.name)) { + promptForReleaseKeyPassword() + } + } +} + +def addSigningProps(propsFilePath, signingConfig) { + def propsFile = file(propsFilePath) + def props = new Properties() + propsFile.withReader { reader -> + props.load(reader) + } + + def storeFile = new File(props.get('key.store') ?: 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 = props.get('key.alias') ?: privateHelpers.ensureValueExists(propsFilePath, props, 'keyAlias') + signingConfig.keyPassword = props.get('keyPassword', props.get('key.alias.password', signingConfig.keyPassword)) + signingConfig.storeFile = storeFile + signingConfig.storePassword = props.get('storePassword', props.get('key.store.password', signingConfig.storePassword)) + def storeType = props.get('storeType', props.get('key.store.type', '')) + if (!storeType) { + def filename = storeFile.getName().toLowerCase(); + if (filename.endsWith('.p12') || filename.endsWith('.pfx')) { + storeType = 'pkcs12' + } else { + storeType = signingConfig.storeType // "jks" + } + } + signingConfig.storeType = storeType +} + +for (def func : cdvPluginPostBuildExtras) { + func() +} + +// This can be defined within build-extras.gradle as: +// ext.postBuildExtras = { ... code here ... } +if (hasProperty('postBuildExtras')) { + postBuildExtras() +} diff --git a/bin/templates/project/build.gradle b/bin/templates/project/build.gradle index 07d4dfa6..fb8082da 100644 --- a/bin/templates/project/build.gradle +++ b/bin/templates/project/build.gradle @@ -1,23 +1,22 @@ -/* - 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 +/* 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 + 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. + 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. */ -apply plugin: 'com.android.application' +// Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { repositories { @@ -26,17 +25,14 @@ buildscript { url "https://maven.google.com" } } - - // 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 dependencies { + + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files classpath 'com.android.tools.build:gradle:3.0.0' } } -// Allow plugins to declare Maven dependencies via build-extras.gradle. allprojects { repositories { jcenter() @@ -44,298 +40,15 @@ allprojects { url "https://maven.google.com" } } -} - -task wrapper(type: Wrapper) { - gradleVersion = '4.1.0' -} - -// 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 = null; - } - // The value for android.buildToolsVersion. - if (!project.hasProperty('cdvBuildToolsVersion')) { - cdvBuildToolsVersion = null; - } - // 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 = null - } - // Whether to append a 0 "abi digit" to versionCode when only a single APK is build - if (!project.hasProperty('cdvVersionCodeForceAbiDigit')) { - cdvVersionCodeForceAbiDigit = null - } - // .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 - } - - // Plugin gradle extensions can append to this to have code run at the end. - cdvPluginPostBuildExtras = [] -} - -// PLUGIN GRADLE EXTENSIONS START -// PLUGIN GRADLE EXTENSIONS END - -def hasBuildExtras = file('build-extras.gradle').exists() -if (hasBuildExtras) { - apply from: 'build-extras.gradle' -} - -// Set property defaults after extension .gradle files. -if (ext.cdvCompileSdkVersion == null) { - ext.cdvCompileSdkVersion = privateHelpers.getProjectTarget() -} -if (ext.cdvBuildToolsVersion == null) { - ext.cdvBuildToolsVersion = privateHelpers.findLatestInstalledBuildTools() -} -if (ext.cdvDebugSigningPropertiesFile == null && file('debug-signing.properties').exists()) { - ext.cdvDebugSigningPropertiesFile = 'debug-signing.properties' -} -if (ext.cdvReleaseSigningPropertiesFile == null && file('release-signing.properties').exists()) { - ext.cdvReleaseSigningPropertiesFile = 'release-signing.properties' -} - -// Cast to appropriate types. -ext.cdvBuildMultipleApks = cdvBuildMultipleApks == null ? false : cdvBuildMultipleApks.toBoolean(); -ext.cdvVersionCodeForceAbiDigit = cdvVersionCodeForceAbiDigit == null ? false : cdvVersionCodeForceAbiDigit.toBoolean(); -ext.cdvMinSdkVersion = cdvMinSdkVersion == null ? null : Integer.parseInt('' + cdvMinSdkVersion) -ext.cdvVersionCode = cdvVersionCode == null ? null : Integer.parseInt('' + cdvVersionCode) - -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('cdvVersionCodeForceAbiDigit=' + cdvVersionCodeForceAbiDigit) - println('cdvMinSdkVersion=' + cdvMinSdkVersion) - println('cdvBuildMultipleApks=' + cdvBuildMultipleApks) - println('cdvReleaseSigningPropertiesFile=' + cdvReleaseSigningPropertiesFile) - println('cdvDebugSigningPropertiesFile=' + cdvDebugSigningPropertiesFile) - println('cdvBuildArch=' + cdvBuildArch) - println('computedVersionCode=' + android.defaultConfig.versionCode) - android.productFlavors.each { flavor -> - println('computed' + flavor.name.capitalize() + 'VersionCode=' + flavor.versionCode) + //This replaces project.properties w.r.t. build settings + project.ext { + defaultBuildToolsVersion="25.0.2" //String + defaultMinSdkVersion=19 //Integer - Minimum requirement is Android 4.4 + defaultTargetSdkVersion=26 //Integer - We ALWAYS target the latest by default + defaultCompileSdkVersion=26 //Integer - We ALWAYS compile with the latest by default } } -android { - sourceSets { - main { - manifest.srcFile 'AndroidManifest.xml' - java.srcDirs = ['src'] - resources.srcDirs = ['src'] - aidl.srcDirs = ['src'] - renderscript.srcDirs = ['src'] - res.srcDirs = ['res'] - assets.srcDirs = ['assets'] - jniLibs.srcDirs = ['libs'] - } - } - - defaultConfig { - versionCode cdvVersionCode ?: new BigInteger("" + privateHelpers.extractIntFromManifest("versionCode")) - applicationId privateHelpers.extractStringFromManifest("package") - - if (cdvMinSdkVersion != null) { - minSdkVersion cdvMinSdkVersion - } - } - - lintOptions { - abortOnError false; - } - - compileSdkVersion cdvCompileSdkVersion - buildToolsVersion cdvBuildToolsVersion - - if (Boolean.valueOf(cdvBuildMultipleApks)) { - flavorDimensions "default" - - productFlavors { - armeabi { - versionCode defaultConfig.versionCode*10 + 1 - ndk { - abiFilters = ["armeabi"] - } - } - armv7 { - versionCode defaultConfig.versionCode*10 + 2 - ndk { - abiFilters = ["armeabi-v7a"] - } - } - arm64 { - versionCode defaultConfig.versionCode*10 + 3 - ndk { - abiFilters = ["arm64-v8a"] - } - } - x86 { - versionCode defaultConfig.versionCode*10 + 4 - ndk { - abiFilters = ["x86"] - } - } - x86_64 { - versionCode defaultConfig.versionCode*10 + 5 - ndk { - abiFilters = ["x86_64"] - } - } - } - } else if (Boolean.valueOf(cdvVersionCodeForceAbiDigit)) { - // This provides compatibility to the default logic for versionCode before cordova-android 5.2.0 - defaultConfig { - versionCode defaultConfig.versionCode*10 - } - } - /* - - ELSE NOTHING! DON'T MESS WITH THE VERSION CODE IF YOU DON'T HAVE TO! - - else if (!cdvVersionCode) { - def minSdkVersion = cdvMinSdkVersion ?: privateHelpers.extractIntFromManifest("minSdkVersion") - // Vary versionCode by the two most common API levels: - // 14 is ICS, which is the lowest API level for many apps. - // 20 is Lollipop, which is the lowest API level for the updatable system webview. - if (minSdkVersion >= 20) { - defaultConfig.versionCode += 9 - } else if (minSdkVersion >= 14) { - defaultConfig.versionCode += 8 - } - } - */ - - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_6 - targetCompatibility JavaVersion.VERSION_1_6 - } - - if (cdvReleaseSigningPropertiesFile) { - signingConfigs { - release { - // These must be set or Gradle will complain (even if they are overridden). - keyAlias = "" - keyPassword = "__unset" // And these must be set to non-empty in order to have the signing step added to the task graph. - storeFile = null - storePassword = "__unset" - } - } - buildTypes { - release { - signingConfig signingConfigs.release - } - } - addSigningProps(cdvReleaseSigningPropertiesFile, signingConfigs.release) - } - if (cdvDebugSigningPropertiesFile) { - addSigningProps(cdvDebugSigningPropertiesFile, signingConfigs.debug) - } -} - -dependencies { - implementation fileTree(dir: 'libs', include: '*.jar') - // SUB-PROJECT DEPENDENCIES START - // SUB-PROJECT DEPENDENCIES END -} - -def promptForReleaseKeyPassword() { - if (!cdvReleaseSigningPropertiesFile) { - return; - } - if ('__unset'.equals(android.signingConfigs.release.storePassword)) { - android.signingConfigs.release.storePassword = privateHelpers.promptForPassword('Enter key store password: ') - } - if ('__unset'.equals(android.signingConfigs.release.keyPassword)) { - android.signingConfigs.release.keyPassword = privateHelpers.promptForPassword('Enter key password: '); - } -} - -gradle.taskGraph.whenReady { taskGraph -> - taskGraph.getAllTasks().each() { task -> - if (['validateReleaseSigning', 'validateSigningRelease', 'validateSigningArmv7Release', 'validateSigningX86Release'].contains(task.name)) { - promptForReleaseKeyPassword() - } - } -} - -def addSigningProps(propsFilePath, signingConfig) { - def propsFile = file(propsFilePath) - def props = new Properties() - propsFile.withReader { reader -> - props.load(reader) - } - - def storeFile = new File(props.get('key.store') ?: 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 = props.get('key.alias') ?: privateHelpers.ensureValueExists(propsFilePath, props, 'keyAlias') - signingConfig.keyPassword = props.get('keyPassword', props.get('key.alias.password', signingConfig.keyPassword)) - signingConfig.storeFile = storeFile - signingConfig.storePassword = props.get('storePassword', props.get('key.store.password', signingConfig.storePassword)) - def storeType = props.get('storeType', props.get('key.store.type', '')) - if (!storeType) { - def filename = storeFile.getName().toLowerCase(); - if (filename.endsWith('.p12') || filename.endsWith('.pfx')) { - storeType = 'pkcs12' - } else { - storeType = signingConfig.storeType // "jks" - } - } - signingConfig.storeType = storeType -} - -for (def func : cdvPluginPostBuildExtras) { - func() -} - -// This can be defined within build-extras.gradle as: -// ext.postBuildExtras = { ... code here ... } -if (hasProperty('postBuildExtras')) { - postBuildExtras() +task clean(type: Delete) { + delete rootProject.buildDir } diff --git a/bin/templates/project/legacy/build.gradle b/bin/templates/project/legacy/build.gradle new file mode 100644 index 00000000..ef229719 --- /dev/null +++ b/bin/templates/project/legacy/build.gradle @@ -0,0 +1,311 @@ +/* + 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. +*/ + +apply plugin: 'com.android.application' + +buildscript { + repositories { + mavenCentral() + jcenter() + } + + // 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 + dependencies { + classpath 'com.android.tools.build:gradle:2.2.3' + } +} + +// Allow plugins to declare Maven dependencies via build-extras.gradle. +allprojects { + repositories { + mavenCentral(); + jcenter() + } +} + +task wrapper(type: Wrapper) { + gradleVersion = '2.14.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 = null; + } + // The value for android.buildToolsVersion. + if (!project.hasProperty('cdvBuildToolsVersion')) { + cdvBuildToolsVersion = null; + } + // 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 = null + } + // .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 + } + + // Plugin gradle extensions can append to this to have code run at the end. + cdvPluginPostBuildExtras = [] +} + +// PLUGIN GRADLE EXTENSIONS START +// PLUGIN GRADLE EXTENSIONS END + +def hasBuildExtras = file('build-extras.gradle').exists() +if (hasBuildExtras) { + apply from: 'build-extras.gradle' +} + +// Set property defaults after extension .gradle files. +if (ext.cdvCompileSdkVersion == null) { + ext.cdvCompileSdkVersion = privateHelpers.getProjectTarget() +} +if (ext.cdvBuildToolsVersion == null) { + ext.cdvBuildToolsVersion = privateHelpers.findLatestInstalledBuildTools() +} +if (ext.cdvDebugSigningPropertiesFile == null && file('debug-signing.properties').exists()) { + ext.cdvDebugSigningPropertiesFile = 'debug-signing.properties' +} +if (ext.cdvReleaseSigningPropertiesFile == null && file('release-signing.properties').exists()) { + ext.cdvReleaseSigningPropertiesFile = 'release-signing.properties' +} + +// Cast to appropriate types. +ext.cdvBuildMultipleApks = cdvBuildMultipleApks == null ? false : cdvBuildMultipleApks.toBoolean(); +ext.cdvMinSdkVersion = cdvMinSdkVersion == null ? null : Integer.parseInt('' + cdvMinSdkVersion) +ext.cdvVersionCode = cdvVersionCode == null ? null : Integer.parseInt('' + cdvVersionCode) + +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) + android.productFlavors.each { flavor -> + println('computed' + flavor.name.capitalize() + 'VersionCode=' + flavor.versionCode) + } +} + +android { + sourceSets { + main { + manifest.srcFile 'AndroidManifest.xml' + java.srcDirs = ['src'] + resources.srcDirs = ['src'] + aidl.srcDirs = ['src'] + renderscript.srcDirs = ['src'] + res.srcDirs = ['res'] + assets.srcDirs = ['assets'] + jniLibs.srcDirs = ['libs'] + } + } + + defaultConfig { + versionCode cdvVersionCode ?: new BigInteger("" + privateHelpers.extractIntFromManifest("versionCode")) + applicationId privateHelpers.extractStringFromManifest("package") + + if (cdvMinSdkVersion != null) { + minSdkVersion cdvMinSdkVersion + } + } + + lintOptions { + abortOnError false; + } + + compileSdkVersion cdvCompileSdkVersion + buildToolsVersion cdvBuildToolsVersion + + if (Boolean.valueOf(cdvBuildMultipleApks)) { + productFlavors { + armv7 { + versionCode defaultConfig.versionCode*10 + 2 + ndk { + abiFilters "armeabi-v7a", "" + } + } + x86 { + versionCode defaultConfig.versionCode*10 + 4 + ndk { + abiFilters "x86", "" + } + } + all { + ndk { + abiFilters "all", "" + } + } + } + } + /* + + ELSE NOTHING! DON'T MESS WITH THE VERSION CODE IF YOU DON'T HAVE TO! + + else if (!cdvVersionCode) { + def minSdkVersion = cdvMinSdkVersion ?: privateHelpers.extractIntFromManifest("minSdkVersion") + // Vary versionCode by the two most common API levels: + // 14 is ICS, which is the lowest API level for many apps. + // 20 is Lollipop, which is the lowest API level for the updatable system webview. + if (minSdkVersion >= 20) { + defaultConfig.versionCode += 9 + } else if (minSdkVersion >= 14) { + defaultConfig.versionCode += 8 + } + } + */ + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_6 + targetCompatibility JavaVersion.VERSION_1_6 + } + + if (cdvReleaseSigningPropertiesFile) { + signingConfigs { + release { + // These must be set or Gradle will complain (even if they are overridden). + keyAlias = "" + keyPassword = "__unset" // And these must be set to non-empty in order to have the signing step added to the task graph. + storeFile = null + storePassword = "__unset" + } + } + buildTypes { + release { + signingConfig signingConfigs.release + } + } + addSigningProps(cdvReleaseSigningPropertiesFile, signingConfigs.release) + } + if (cdvDebugSigningPropertiesFile) { + addSigningProps(cdvDebugSigningPropertiesFile, signingConfigs.debug) + } +} + +dependencies { + compile fileTree(dir: 'libs', include: '*.jar') + // SUB-PROJECT DEPENDENCIES START + // SUB-PROJECT DEPENDENCIES END +} + +def promptForReleaseKeyPassword() { + if (!cdvReleaseSigningPropertiesFile) { + return; + } + if ('__unset'.equals(android.signingConfigs.release.storePassword)) { + android.signingConfigs.release.storePassword = privateHelpers.promptForPassword('Enter key store password: ') + } + if ('__unset'.equals(android.signingConfigs.release.keyPassword)) { + android.signingConfigs.release.keyPassword = privateHelpers.promptForPassword('Enter key password: '); + } +} + +gradle.taskGraph.whenReady { taskGraph -> + taskGraph.getAllTasks().each() { task -> + if (task.name == 'validateReleaseSigning' || task.name == 'validateSigningRelease') { + promptForReleaseKeyPassword() + } + } +} + +def addSigningProps(propsFilePath, signingConfig) { + def propsFile = file(propsFilePath) + def props = new Properties() + propsFile.withReader { reader -> + props.load(reader) + } + + def storeFile = new File(props.get('key.store') ?: 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 = props.get('key.alias') ?: privateHelpers.ensureValueExists(propsFilePath, props, 'keyAlias') + signingConfig.keyPassword = props.get('keyPassword', props.get('key.alias.password', signingConfig.keyPassword)) + signingConfig.storeFile = storeFile + signingConfig.storePassword = props.get('storePassword', props.get('key.store.password', signingConfig.storePassword)) + def storeType = props.get('storeType', props.get('key.store.type', '')) + if (!storeType) { + def filename = storeFile.getName().toLowerCase(); + if (filename.endsWith('.p12') || filename.endsWith('.pfx')) { + storeType = 'pkcs12' + } else { + storeType = signingConfig.storeType // "jks" + } + } + signingConfig.storeType = storeType +} + +for (def func : cdvPluginPostBuildExtras) { + func() +} + +// This can be defined within build-extras.gradle as: +// ext.postBuildExtras = { ... code here ... } +if (hasProperty('postBuildExtras')) { + postBuildExtras() +} diff --git a/bin/templates/project/project.properties b/bin/templates/project/project.properties index ddd3a060..6ea5ac23 100644 --- a/bin/templates/project/project.properties +++ b/bin/templates/project/project.properties @@ -1,15 +1,13 @@ -# This file is automatically generated by Android Tools. -# Do not modify this file -- YOUR CHANGES WILL BE ERASED! -# -# This file must be checked in Version Control Systems. -# -# To customize properties used by the Ant build system edit -# "ant.properties", and override values to adapt the script to your -# project structure. -# -# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): -#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt +# This file was originally created by the Android Tools, but is now +# used by cordova-android to manage the state of the various third party +# libraries used in your application +# This is the Library Module that contains the Cordova Library, this is not +# required when using an AAR android.library.reference.1=CordovaLib + +# This is the application project. This is only required for Android Studio Gradle projects +android.library.reference.2=app + # Project target. target=This_gets_replaced diff --git a/framework/AndroidManifest.xml b/framework/AndroidManifest.xml index 3feb903c..26a0aed0 100755 --- a/framework/AndroidManifest.xml +++ b/framework/AndroidManifest.xml @@ -19,5 +19,5 @@ --> - + diff --git a/framework/build.gradle b/framework/build.gradle index 1ad622c9..6d484086 100644 --- a/framework/build.gradle +++ b/framework/build.gradle @@ -50,8 +50,8 @@ android { publishNonDefault true compileOptions { - sourceCompatibility JavaVersion.VERSION_1_6 - targetCompatibility JavaVersion.VERSION_1_6 + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 } sourceSets { diff --git a/framework/cordova.gradle b/framework/cordova.gradle index 21a01bb5..0131a265 100644 --- a/framework/cordova.gradle +++ b/framework/cordova.gradle @@ -29,7 +29,11 @@ String doEnsureValueExists(filePath, props, key) { String doGetProjectTarget() { def props = new Properties() - file('project.properties').withReader { reader -> + def propertiesFile = 'project.properties'; + if(!(file(propertiesFile).exists())) { + propertiesFile = '../project.properties'; + } + file(propertiesFile).withReader { reader -> props.load(reader) } return doEnsureValueExists('project.properties', props, 'target') diff --git a/framework/src/org/apache/cordova/CordovaActivity.java b/framework/src/org/apache/cordova/CordovaActivity.java index 85eeb53a..e2ed1cb8 100755 --- a/framework/src/org/apache/cordova/CordovaActivity.java +++ b/framework/src/org/apache/cordova/CordovaActivity.java @@ -319,6 +319,7 @@ public class CordovaActivity extends Activity { /** * Called when view focus is changed */ + @SuppressLint("InlinedApi") @Override public void onWindowFocusChanged(boolean hasFocus) { super.onWindowFocusChanged(hasFocus); diff --git a/framework/src/org/apache/cordova/CordovaBridge.java b/framework/src/org/apache/cordova/CordovaBridge.java index 9459a113..28c407f3 100644 --- a/framework/src/org/apache/cordova/CordovaBridge.java +++ b/framework/src/org/apache/cordova/CordovaBridge.java @@ -18,6 +18,8 @@ */ package org.apache.cordova; +import android.annotation.SuppressLint; + import java.security.SecureRandom; import org.json.JSONArray; @@ -110,6 +112,9 @@ public class CordovaBridge { } /** Called by cordova.js to initialize the bridge. */ + //On old Androids SecureRandom isn't really secure, this is the least of your problems if + //you're running Android 4.3 and below in 2017 + @SuppressLint("TrulyRandom") int generateBridgeSecret() { SecureRandom randGen = new SecureRandom(); expectedBridgeSecret = randGen.nextInt(Integer.MAX_VALUE); diff --git a/framework/src/org/apache/cordova/CordovaClientCertRequest.java b/framework/src/org/apache/cordova/CordovaClientCertRequest.java index 5dd0ecae..ccda0272 100644 --- a/framework/src/org/apache/cordova/CordovaClientCertRequest.java +++ b/framework/src/org/apache/cordova/CordovaClientCertRequest.java @@ -22,10 +22,12 @@ import java.security.Principal; import java.security.PrivateKey; import java.security.cert.X509Certificate; +import android.annotation.SuppressLint; import android.webkit.ClientCertRequest; /** * Implementation of the ICordovaClientCertRequest for Android WebView. + * */ public class CordovaClientCertRequest implements ICordovaClientCertRequest { @@ -38,6 +40,7 @@ public class CordovaClientCertRequest implements ICordovaClientCertRequest { /** * Cancel this request */ + @SuppressLint("NewApi") public void cancel() { request.cancel(); @@ -46,6 +49,7 @@ public class CordovaClientCertRequest implements ICordovaClientCertRequest { /* * Returns the host name of the server requesting the certificate. */ + @SuppressLint("NewApi") public String getHost() { return request.getHost(); @@ -54,6 +58,7 @@ public class CordovaClientCertRequest implements ICordovaClientCertRequest { /* * Returns the acceptable types of asymmetric keys (can be null). */ + @SuppressLint("NewApi") public String[] getKeyTypes() { return request.getKeyTypes(); @@ -62,6 +67,7 @@ public class CordovaClientCertRequest implements ICordovaClientCertRequest { /* * Returns the port number of the server requesting the certificate. */ + @SuppressLint("NewApi") public int getPort() { return request.getPort(); @@ -70,6 +76,7 @@ public class CordovaClientCertRequest implements ICordovaClientCertRequest { /* * Returns the acceptable certificate issuers for the certificate matching the private key (can be null). */ + @SuppressLint("NewApi") public Principal[] getPrincipals() { return request.getPrincipals(); @@ -78,6 +85,7 @@ public class CordovaClientCertRequest implements ICordovaClientCertRequest { /* * Ignore the request for now. Do not remember user's choice. */ + @SuppressLint("NewApi") public void ignore() { request.ignore(); @@ -89,6 +97,7 @@ public class CordovaClientCertRequest implements ICordovaClientCertRequest { * @param privateKey The privateKey * @param chain The certificate chain */ + @SuppressLint("NewApi") public void proceed(PrivateKey privateKey, X509Certificate[] chain) { request.proceed(privateKey, chain); diff --git a/framework/src/org/apache/cordova/CordovaInterfaceImpl.java b/framework/src/org/apache/cordova/CordovaInterfaceImpl.java index 3c459025..9a6e9245 100644 --- a/framework/src/org/apache/cordova/CordovaInterfaceImpl.java +++ b/framework/src/org/apache/cordova/CordovaInterfaceImpl.java @@ -19,6 +19,7 @@ package org.apache.cordova; +import android.annotation.SuppressLint; import android.app.Activity; import android.content.Context; import android.content.Intent; @@ -227,6 +228,7 @@ public class CordovaInterfaceImpl implements CordovaInterface { requestPermissions(plugin, requestCode, permissions); } + @SuppressLint("NewApi") public void requestPermissions(CordovaPlugin plugin, int requestCode, String [] permissions) { int mappedRequestCode = permissionResultCallbacks.registerCallback(plugin, requestCode); getActivity().requestPermissions(permissions, mappedRequestCode); diff --git a/framework/src/org/apache/cordova/CordovaWebViewImpl.java b/framework/src/org/apache/cordova/CordovaWebViewImpl.java index 85a0b5f5..fb99c344 100644 --- a/framework/src/org/apache/cordova/CordovaWebViewImpl.java +++ b/framework/src/org/apache/cordova/CordovaWebViewImpl.java @@ -18,6 +18,7 @@ */ package org.apache.cordova; +import android.annotation.SuppressLint; import android.content.Context; import android.content.Intent; import android.net.Uri; @@ -91,6 +92,7 @@ public class CordovaWebViewImpl implements CordovaWebView { init(cordova, new ArrayList(), new CordovaPreferences()); } + @SuppressLint("Assert") @Override public void init(CordovaInterface cordova, List pluginEntries, CordovaPreferences preferences) { if (this.cordova != null) { diff --git a/framework/src/org/apache/cordova/engine/SystemWebViewEngine.java b/framework/src/org/apache/cordova/engine/SystemWebViewEngine.java index 9ea97bc8..d15b0eee 100755 --- a/framework/src/org/apache/cordova/engine/SystemWebViewEngine.java +++ b/framework/src/org/apache/cordova/engine/SystemWebViewEngine.java @@ -254,6 +254,9 @@ public class SystemWebViewEngine implements CordovaWebViewEngine { } } + // Yeah, we know, which is why we makes ure that we don't do this if the bridge is + // below JELLYBEAN_MR1. It'd be great if lint was just a little smarter. + @SuppressLint("AddJavascriptInterface") private static void exposeJsInterface(WebView webView, CordovaBridge bridge) { if ((Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1)) { LOG.i(TAG, "Disabled addJavascriptInterface() bridge since Android version is old."); diff --git a/package.json b/package.json index 23c68e5f..d8601d8e 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "license": "Apache-2.0", "dependencies": { "android-versions": "^1.2.1", - "cordova-common": "^2.1.0", + "cordova-common": "^2.2.0", "elementtree": "0.1.6", "nopt": "^3.0.1", "properties-parser": "^0.2.3", diff --git a/spec/unit/create.spec.js b/spec/unit/create.spec.js index bfd6295e..77eeaa36 100644 --- a/spec/unit/create.spec.js +++ b/spec/unit/create.spec.js @@ -127,6 +127,7 @@ describe('create', function () { var Manifest_mock = function () {}; var revert_manifest_mock; var project_path = path.join('some', 'path'); + var app_path = path.join(project_path, 'app', 'src', 'main'); var default_templates = path.join(__dirname, '..', '..', 'bin', 'templates', 'project'); var fake_android_target = 'android-1337'; beforeEach(function () { @@ -217,15 +218,15 @@ describe('create', function () { describe('happy path', function () { it('should copy project templates from a specified custom template', function (done) { create.create(project_path, config_mock, {customTemplate: '/template/path'}, events_mock).then(function () { - expect(shell.cp).toHaveBeenCalledWith('-r', path.join('/template/path', 'assets'), project_path); - expect(shell.cp).toHaveBeenCalledWith('-r', path.join('/template/path', 'res'), project_path); + expect(shell.cp).toHaveBeenCalledWith('-r', path.join('/template/path', 'assets'), app_path); + expect(shell.cp).toHaveBeenCalledWith('-r', path.join('/template/path', 'res'), app_path); expect(shell.cp).toHaveBeenCalledWith(path.join('/template/path', 'gitignore'), path.join(project_path, '.gitignore')); }).fail(fail).done(done); }); it('should copy project templates from the default templates location if no custom template is provided', function (done) { create.create(project_path, config_mock, {}, events_mock).then(function () { - expect(shell.cp).toHaveBeenCalledWith('-r', path.join(default_templates, 'assets'), project_path); - expect(shell.cp).toHaveBeenCalledWith('-r', path.join(default_templates, 'res'), project_path); + expect(shell.cp).toHaveBeenCalledWith('-r', path.join(default_templates, 'assets'), app_path); + expect(shell.cp).toHaveBeenCalledWith('-r', path.join(default_templates, 'res'), app_path); expect(shell.cp).toHaveBeenCalledWith(path.join(default_templates, 'gitignore'), path.join(project_path, '.gitignore')); }).fail(fail).done(done); }); @@ -237,13 +238,13 @@ describe('create', function () { it('should create a java src directory based on the provided project package name', function (done) { config_mock.packageName.and.returnValue('org.apache.cordova'); create.create(project_path, config_mock, {}, events_mock).then(function () { - expect(shell.mkdir).toHaveBeenCalledWith('-p', path.join(project_path, 'src', 'org', 'apache', 'cordova')); + expect(shell.mkdir).toHaveBeenCalledWith('-p', path.join(app_path, 'java', 'org', 'apache', 'cordova')); }).fail(fail).done(done); }); it('should copy, rename and interpolate the template Activity java class with the project-specific activity name and package name', function (done) { config_mock.packageName.and.returnValue('org.apache.cordova'); config_mock.android_activityName.and.returnValue('CEEDEEVEE'); - var activity_path = path.join(project_path, 'src', 'org', 'apache', 'cordova', 'CEEDEEVEE.java'); + var activity_path = path.join(app_path, 'java', 'org', 'apache', 'cordova', 'CEEDEEVEE.java'); create.create(project_path, config_mock, {}, events_mock).then(function () { expect(shell.cp).toHaveBeenCalledWith('-f', path.join(default_templates, 'Activity.java'), activity_path); expect(shell.sed).toHaveBeenCalledWith('-i', /__ACTIVITY__/, 'CEEDEEVEE', activity_path); @@ -253,7 +254,7 @@ describe('create', function () { it('should interpolate the project name into strings.xml', function (done) { config_mock.name.and.returnValue('IncredibleApp'); create.create(project_path, config_mock, {}, events_mock).then(function () { - expect(shell.sed).toHaveBeenCalledWith('-i', /__NAME__/, 'IncredibleApp', path.join(project_path, 'res', 'values', 'strings.xml')); + expect(shell.sed).toHaveBeenCalledWith('-i', /__NAME__/, 'IncredibleApp', path.join(app_path, 'res', 'values', 'strings.xml')); }).fail(fail).done(done); }); it('should copy template scripts into generated project', function (done) { @@ -273,7 +274,7 @@ describe('create', function () { }); it('should prepare build files', function (done) { create.create(project_path, config_mock, {}, events_mock).then(function () { - expect(create.prepBuildFiles).toHaveBeenCalledWith(project_path); + expect(create.prepBuildFiles).toHaveBeenCalledWith(project_path, 'studio'); }).fail(fail).done(done); }); }); diff --git a/spec/unit/pluginHandlers/handlers.spec.js b/spec/unit/pluginHandlers/handlers.spec.js index 6e1e79e0..dae57a91 100644 --- a/spec/unit/pluginHandlers/handlers.spec.js +++ b/spec/unit/pluginHandlers/handlers.spec.js @@ -71,7 +71,7 @@ describe('android project handler', function () { }); describe('of elements', function () { - it('Test#003 : should copy files', function () { + it('Test#003 : should copy files to the correct location on a non-Android Studio project', function () { android['resource-file'].install(valid_resources[0], dummyPluginInfo, dummyProject); expect(copyFileSpy).toHaveBeenCalledWith(dummyplugin, 'android-resource.xml', temp, path.join('res', 'xml', 'dummy.xml'), false); }); @@ -264,15 +264,20 @@ describe('android project handler', function () { android['resource-file'].uninstall(valid_resources[0], dummyPluginInfo, dummyProject); expect(removeFileSpy).toHaveBeenCalledWith(temp, path.join('res/xml/dummy.xml')); }); + it('Test#021 : should remove files for Android Studio projects', function () { + android['resource-file'].install(valid_resources[0], dummyPluginInfo, dummyProject, {android_studio: true}); + android['resource-file'].uninstall(valid_resources[0], dummyPluginInfo, dummyProject, {android_studio: true}); + expect(removeFileSpy).toHaveBeenCalledWith(temp, path.join('app/src/main/res/xml/dummy.xml')); + }); }); describe('of elements', function () { - it('Test#021 : should remove stuff by calling common.deleteJava', function () { + it('Test#022 : should remove stuff by calling common.deleteJava', function () { android['source-file'].install(valid_source[0], dummyPluginInfo, dummyProject); android['source-file'].uninstall(valid_source[0], dummyPluginInfo, dummyProject); expect(deleteJavaSpy).toHaveBeenCalledWith(temp, path.join('src/com/phonegap/plugins/dummyplugin/DummyPlugin.java')); }); - it('Test#022 : should remove stuff by calling common.deleteJava for Android Studio projects', function () { + it('Test#023 : should remove stuff by calling common.deleteJava for Android Studio projects', function () { android['source-file'].install(valid_source[0], dummyPluginInfo, dummyProject, {android_studio: true}); android['source-file'].uninstall(valid_source[0], dummyPluginInfo, dummyProject, {android_studio: true}); expect(deleteJavaSpy).toHaveBeenCalledWith(temp, path.join('app/src/main/java/com/phonegap/plugins/dummyplugin/DummyPlugin.java')); @@ -291,30 +296,30 @@ describe('android project handler', function () { spyOn(dummyProject, 'removeGradleReference'); }); - it('Test#023 : should throw if framework doesn\'t have "src" attribute', function () { + it('Test#024 : should throw if framework doesn\'t have "src" attribute', function () { expect(function () { android.framework.uninstall({}, dummyPluginInfo, dummyProject); }).toThrow(); }); - it('Test#024 : should uninstall framework without "parent" attribute into project root', function () { + it('Test#025 : should uninstall framework without "parent" attribute into project root', function () { var framework = {src: 'plugin-lib'}; android.framework.uninstall(framework, dummyPluginInfo, dummyProject); expect(dummyProject.removeSystemLibrary).toHaveBeenCalledWith(dummyProject.projectDir, someString); }); - it('Test#025 : should uninstall framework with "parent" attribute into parent framework dir', function () { + it('Test#026 : should uninstall framework with "parent" attribute into parent framework dir', function () { var childFramework = {src: 'plugin-lib2', parent: 'plugin-lib'}; android.framework.uninstall(childFramework, dummyPluginInfo, dummyProject); expect(dummyProject.removeSystemLibrary).toHaveBeenCalledWith(path.resolve(dummyProject.projectDir, childFramework.parent), someString); }); - it('Test#026 : should remove framework sources if "custom" attribute is set', function () { + it('Test#027 : should remove framework sources if "custom" attribute is set', function () { var framework = {src: 'plugin-lib', custom: true}; android.framework.uninstall(framework, dummyPluginInfo, dummyProject); expect(dummyProject.removeSubProject).toHaveBeenCalledWith(dummyProject.projectDir, someString); expect(removeFileSpy).toHaveBeenCalledWith(dummyProject.projectDir, someString); }); - it('Test#27 : should install gradleReference using project.removeGradleReference', function () { + it('Test#28 : should install gradleReference using project.removeGradleReference', function () { var framework = {src: 'plugin-lib', custom: true, type: 'gradleReference'}; android.framework.uninstall(framework, dummyPluginInfo, dummyProject); expect(removeFileSpy).toHaveBeenCalledWith(dummyProject.projectDir, someString); @@ -340,13 +345,13 @@ describe('android project handler', function () { }); }); - it('Test#028 : should put module to both www and platform_www when options.usePlatformWww flag is specified', function () { + it('Test#029 : should put module to both www and platform_www when options.usePlatformWww flag is specified', function () { android['js-module'].uninstall(jsModule, dummyPluginInfo, dummyProject, {usePlatformWww: true}); expect(shell.rm).toHaveBeenCalledWith('-Rf', wwwDest); expect(shell.rm).toHaveBeenCalledWith('-Rf', platformWwwDest); }); - it('Test#29 : should put module to www only when options.usePlatformWww flag is not specified', function () { + it('Test#030 : should put module to www only when options.usePlatformWww flag is not specified', function () { android['js-module'].uninstall(jsModule, dummyPluginInfo, dummyProject); expect(shell.rm).toHaveBeenCalledWith('-Rf', wwwDest); expect(shell.rm).not.toHaveBeenCalledWith('-Rf', platformWwwDest); @@ -370,13 +375,13 @@ describe('android project handler', function () { }); }); - it('Test#030 : should put module to both www and platform_www when options.usePlatformWww flag is specified', function () { + it('Test#031 : should put module to both www and platform_www when options.usePlatformWww flag is specified', function () { android.asset.uninstall(asset, dummyPluginInfo, dummyProject, {usePlatformWww: true}); expect(shell.rm).toHaveBeenCalledWith(jasmine.any(String), wwwDest); expect(shell.rm).toHaveBeenCalledWith(jasmine.any(String), platformWwwDest); }); - it('Test#31 : should put module to www only when options.usePlatformWww flag is not specified', function () { + it('Test#032 : should put module to www only when options.usePlatformWww flag is not specified', function () { android.asset.uninstall(asset, dummyPluginInfo, dummyProject); expect(shell.rm).toHaveBeenCalledWith(jasmine.any(String), wwwDest); expect(shell.rm).not.toHaveBeenCalledWith(jasmine.any(String), platformWwwDest);