Skip to content

Commit

Permalink
Merge pull request #4919 from cb1kenobi/timob-15670
Browse files Browse the repository at this point in the history
[TIMOB-15670] [TIMOB-15640] [TIMOB-15664] [TIMOB-15662] [TIMOB-11778] and more!
  • Loading branch information
ayeung committed Nov 7, 2013
2 parents 5f8647f + fb2b974 commit 0f21eec
Show file tree
Hide file tree
Showing 14 changed files with 318 additions and 153 deletions.
111 changes: 63 additions & 48 deletions android/cli/commands/_build.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,20 +48,6 @@ function hash(s) {
return crypto.createHash('md5').update(s || '').digest('hex');
}

function assertIssue(logger, issues, name) {
var i = 0,
len = issues.length;
for (; i < len; i++) {
if ((typeof name == 'string' && issues[i].id == name) || (typeof name == 'object' && name.test(issues[i].id))) {
issues[i].message.split('\n').forEach(function (line) {
logger.error(line.replace(/(__(.+?)__)/g, '$2'.bold));
});
logger.log();
process.exit(1);
}
}
}

function AndroidBuilder() {
Builder.apply(this, arguments);

Expand Down Expand Up @@ -115,18 +101,32 @@ AndroidBuilder.prototype.config = function config(logger, config, cli) {
androidDetect(config, { packageJson: _t.packageJson }, function (androidInfo) {
_t.androidInfo = androidInfo;

// check that the Android SDK is found and sane
assertIssue(logger, androidInfo.issues, 'ANDROID_SDK_NOT_FOUND');
assertIssue(logger, androidInfo.issues, 'ANDROID_SDK_MISSING_PROGRAMS');

// make sure we have an Android SDK and some Android targets
if (!Object.keys(androidInfo.targets).filter(function (id) {
var t = androidInfo.targets[id];
return t.type == 'platform' && t['api-level'] > _t.minSupportedApiLevel;
}).length) {
logger.error(__('No Android SDK targets found.') + '\n');
logger.log(__('Please download SDK targets (api level %s or newer) via Android SDK Manager and try again.', _t.minSupportedApiLevel) + '\n');
process.exit(1);
if (!cli.argv.prompt) {
function assertIssue(logger, issues, name) {
var i = 0,
len = issues.length;
for (; i < len; i++) {
if ((typeof name == 'string' && issues[i].id == name) || (typeof name == 'object' && name.test(issues[i].id))) {
issues[i].message.split('\n').forEach(function (line) {
logger.error(line.replace(/(__(.+?)__)/g, '$2'.bold));
});
logger.log();
}
}
}

// check that the Android SDK is found and sane
assertIssue(logger, androidInfo.issues, 'ANDROID_SDK_NOT_FOUND');
assertIssue(logger, androidInfo.issues, 'ANDROID_SDK_MISSING_PROGRAMS');

// make sure we have an Android SDK and some Android targets
if (!Object.keys(androidInfo.targets).filter(function (id) {
var t = androidInfo.targets[id];
return t.type == 'platform' && t['api-level'] > _t.minSupportedApiLevel;
}).length) {
logger.error(__('No Android SDK targets found.') + '\n');
logger.log(__('Please download SDK targets (api level %s or newer) via Android SDK Manager and try again.', _t.minSupportedApiLevel) + '\n');
}
}

// if --android-sdk was not specified, then we simply try to set a default android sdk
Expand Down Expand Up @@ -294,7 +294,7 @@ AndroidBuilder.prototype.config = function config(logger, config, cli) {
config.set('android.sdkPath', value);

// path looks good, do a full scan again
androidDetect(config, { packageJson: packageJson, bypassCache: true }, function (androidInfo) {
androidDetect(config, { packageJson: _t.packageJson, bypassCache: true }, function (androidInfo) {
_t.androidInfo = androidInfo;
callback(null, value);
});
Expand Down Expand Up @@ -597,9 +597,12 @@ AndroidBuilder.prototype.config = function config(logger, config, cli) {
}));
},
validate: function (outputDir, callback) {
callback(outputDir ? null : new Error(__('Invalid output directory')), outputDir);
callback(outputDir || !_t.conf.options['output-dir'].required ? null : new Error(__('Invalid output directory')), outputDir);
}
},
'profiler-host': {
hidden: true
},
'store-password': {
abbr: 'P',
desc: __('the password for the keystore'),
Expand Down Expand Up @@ -762,6 +765,10 @@ AndroidBuilder.prototype.validate = function validate(logger, config, cli) {
this.proguard = false;
}

if (cli.argv['skip-js-minify']) {
this.minifyJS = false;
}

// check the Android specific app id rules
if (!config.get('android.skipAppIdValidation')) {
if (!/^([a-zA-Z_]{1}[a-zA-Z0-9_-]*(\.[a-zA-Z0-9_-]*)*)$/.test(cli.tiapp.id)) {
Expand Down Expand Up @@ -1913,7 +1920,7 @@ AndroidBuilder.prototype.copyResources = function copyResources(next) {
drawableDpiRegExp = /^(high|medium|low)$/,
drawableExtRegExp = /((\.9)?\.(png|jpg))$/,
splashScreenRegExp = /^default\.(9\.png|png|jpg)$/,
relSplashScreenRegExp = /^images\/default\.(9\.png|png|jpg)$/,
relSplashScreenRegExp = /^default\.(9\.png|png|jpg)$/,
drawableResources = {},
jsFiles = {},
jsFilesToEncrypt = this.jsFilesToEncrypt = [],
Expand Down Expand Up @@ -1965,8 +1972,9 @@ AndroidBuilder.prototype.copyResources = function copyResources(next) {

appc.async.series(this, files.map(function (filename) {
return function (next) {
var from = path.join(src, filename),
to = path.join(dest, filename);
var destDir = dest,
from = path.join(src, filename),
to = path.join(destDir, filename);

// check that the file actually exists and isn't a broken symlink
if (!fs.existsSync(from)) return next();
Expand All @@ -1980,7 +1988,7 @@ AndroidBuilder.prototype.copyResources = function copyResources(next) {
}

// if this is a directory, recurse
if (isDir) return recursivelyCopy.call(this, from, path.join(dest, filename), null, opts, next);
if (isDir) return recursivelyCopy.call(this, from, path.join(destDir, filename), null, opts, next);

// we have a file, now we need to see what sort of file

Expand All @@ -1994,26 +2002,26 @@ AndroidBuilder.prototype.copyResources = function copyResources(next) {
extMatch = destFilename.match(drawableExtRegExp),
ext = extMatch && extMatch[1] || '';

dest = path.join(
destDir = path.join(
this.buildResDir,
drawableDpiRegExp.test(m[1]) ? 'drawable-' + m[1][0] + 'dpi' : 'drawable-' + m[1].substring(4)
);

if (splashScreenRegExp.test(filename)) {
// we have a splash screen image
to = path.join(dest, 'background' + ext);
to = path.join(destDir, 'background' + ext);
} else {
to = path.join(dest, name.replace(/[^a-z0-9_]/g, '_').substring(0, 80) + '_' + hash(name).substring(0, 10) + ext);
to = path.join(destDir, name.replace(/[^a-z0-9_]/g, '_').substring(0, 80) + '_' + hash(name).substring(0, 10) + ext);
}
isDrawable = true;
} else if (m = relPath.match(relSplashScreenRegExp)) {
// we have a splash screen
// if it's a 9 patch, then the image goes in drawable-nodpi, not drawable
if (m[1] == '.9.png') {
dest = path.join(this.buildResDir, 'drawable-nodpi');
to = path.join(dest, filename.replace('default.', 'background.'));
if (m[1] == '9.png') {
destDir = path.join(this.buildResDir, 'drawable-nodpi');
to = path.join(destDir, filename.replace('default.', 'background.'));
} else {
dest = this.buildResDrawableDir;
destDir = this.buildResDrawableDir;
to = path.join(this.buildResDrawableDir, filename.replace('default.', 'background.'));
}
isDrawable = true;
Expand All @@ -2033,7 +2041,7 @@ AndroidBuilder.prototype.copyResources = function copyResources(next) {
}

// if the destination directory does not exists, create it
fs.existsSync(dest) || wrench.mkdirSyncRecursive(dest);
fs.existsSync(destDir) || wrench.mkdirSyncRecursive(destDir);

var ext = filename.match(extRegExp);

Expand Down Expand Up @@ -2322,7 +2330,11 @@ AndroidBuilder.prototype.copyResources = function copyResources(next) {
titaniumPrepHook(
path.join(this.platformPath, titaniumPrep),
[ this.appid, this.buildAssetsDir ].concat(jsFilesToEncrypt),
{},
{
env: appc.util.mix({
'JAVA_HOME': this.jdkInfo.home
}, process.env)
},
next
);
});
Expand Down Expand Up @@ -3541,20 +3553,23 @@ AndroidBuilder.prototype.createSignedApk = function createSignedApk(next) {
'-digestalg', 'SHA1',
'-keystore', this.keystore,
'-storepass', this.keystoreStorePassword
],
signerArgsSafe;
];

this.logger.info(__('Using %s signature algorithm', (m ? m[1] : 'MD5withRSA').cyan));

this.keystoreKeyPassword && signerArgs.push('-keypass', this.keystoreKeyPassword);
signerArgs.push('-signedjar', this.apkFile, this.unsignedApkFile, this.keystoreAlias);

signerArgsSafe = [].concat(signerArgs);
signerArgsSafe[7] = signerArgsSafe[7].replace(/./g, '*');
this.keystoreKeyPassword && (signerArgsSafe[9] = signerArgsSafe[9].replace(/./g, '*'));

var jarsignerHook = this.cli.createHook('build.android.jarsigner', this, function (exe, args, opts, done) {
this.logger.info(__('Signing apk: %s', (exe + ' "' + args.join('" "') + '"').cyan));
var safeArgs = [];
for (var i = 0, l = args.length; i < l; i++) {
safeArgs.push(args[i]);
if (args[i] == '-storepass' || args[i] == 'keypass') {
safeArgs.push(args[++i].replace(/./g, '*'));
}
}

this.logger.info(__('Signing apk: %s', (exe + ' "' + safeArgs.join('" "') + '"').cyan));
appc.subprocess.run(exe, args, opts, function (code, out, err) {
if (code) {
this.logger.error(__('Failed to sign apk:'));
Expand Down
4 changes: 4 additions & 0 deletions android/cli/lib/AndroidManifest.js
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,10 @@ function AndroidManifest(filename) {
this[tag][subtag][key] = src[tag][subtag][key];
}, this);
break;
default:
if (tagAttrs.application.test(subtag)) {
this[tag][subtag] = src[tag][subtag];
}
}
}, this);
break;
Expand Down
5 changes: 2 additions & 3 deletions android/cli/locales/en.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@
"Could not find all required Titanium Modules:": "Could not find all required Titanium Modules:",
"Found incompatible Titanium Modules:": "Found incompatible Titanium Modules:",
"Found conflicting Titanium modules:": "Found conflicting Titanium modules:",
"Titanium module \"%s\" requested for both iOS and CommonJS platforms, but only one may be used at a time.": "Titanium module \"%s\" requested for both iOS and CommonJS platforms, but only one may be used at a time.",
"Titanium module \"%s\" requested for both Android and CommonJS platforms, but only one may be used at a time.": "Titanium module \"%s\" requested for both Android and CommonJS platforms, but only one may be used at a time.",
"Module %s version %s is missing module file: %s": "Module %s version %s is missing module file: %s",
"Module %s version %s is missing jar file: %s": "Module %s version %s is missing jar file: %s",
"The module \"%%s\" does not support the ABI: %%s": {
Expand All @@ -136,7 +136,6 @@
},
"It only supports the following ABIs: %s": "It only supports the following ABIs: %s",
"Module %s version %s is missing bindings json file": "Module %s version %s is missing bindings json file",
"Detected third-party native Android module: %s version %s": "Detected third-party native Android module: %s version %s",
"Conflicting jar files detected:": "Conflicting jar files detected:",
"The following modules have different \"%s\" files": "The following modules have different \"%s\" files",
" %s (version %s) (hash=%s)": " %s (version %s) (hash=%s)",
Expand Down Expand Up @@ -223,7 +222,6 @@
"Android SDK %s missing framework aidl, skipping": "Android SDK %s missing framework aidl, skipping",
"No aidl files to compile, continuing": "No aidl files to compile, continuing",
"Compiling aidl file: %s": "Compiling aidl file: %s",
"Running %s": "Running %s",
"Generating i18n files": "Generating i18n files",
"Merging %s strings => %s": "Merging %s strings => %s",
"Writing %s strings => %s": "Writing %s strings => %s",
Expand Down Expand Up @@ -254,6 +252,7 @@
"Using %s signature algorithm": "Using %s signature algorithm",
"Signing apk: %s": "Signing apk: %s",
"Failed to sign apk:": "Failed to sign apk:",
"Aligning zip file: %s": "Aligning zip file: %s",
"Failed to zipalign apk:": "Failed to zipalign apk:",
"Writing build manifest: %s": "Writing build manifest: %s",
"No APK file to deploy, skipping": "No APK file to deploy, skipping",
Expand Down
4 changes: 4 additions & 0 deletions android/cli/tests/resources/AndroidManifest_application2.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest>
<application android:debuggable="true" android:hardwareAccelerated="true" />
</manifest>
94 changes: 94 additions & 0 deletions android/cli/tests/test-androidmanifest.js
Original file line number Diff line number Diff line change
Expand Up @@ -14838,6 +14838,100 @@ describe('AndroidManifest', function () {
});
});

describe('Merge multiple <application> tags', function () {
var am = new AndroidManifest(path.resolve('./resources/AndroidManifest_application.xml'));
var am2 = new AndroidManifest(path.resolve('./resources/AndroidManifest_application2.xml'));

am.merge(am2);

it('should match object', function () {
am.should.eql({
"application": {
"allowTaskReparenting": false,
"allowBackup": true,
"backupAgent": ".MyBackupAgent",
"debuggable": true,
"description": "this is a test",
"enabled": true,
"hasCode": true,
"hardwareAccelerated": true,
"icon": "@drawable/icon",
"killAfterRestore": true,
"largeHeap": false,
"label": "test",
"logo": "@drawable/logo",
"manageSpaceActivity": ".TestActivity",
"name": "test",
"permission": "testPermission",
"persistent": true,
"process": "test",
"restoreAnyVersion": false,
"requiredAccountType": "com.google",
"restrictedAccountType": "com.google",
"supportsRtl": false,
"taskAffinity": "test",
"testOnly": false,
"theme": "testTheme",
"uiOptions": "none",
"vmSafeMode": false
}
});
});

it('toString()', function () {
am.toString().should.equal('[object Object]');
});

it("toString('json')", function () {
am.toString('json').should.equal('{"application":{"allowTaskReparenting":false,"allowBackup":true,"backupAgent":".MyBackupAgent","debuggable":true,"description":"this is a test","enabled":true,"hasCode":true,"hardwareAccelerated":true,"icon":"@drawable/icon","killAfterRestore":true,"largeHeap":false,"label":"test","logo":"@drawable/logo","manageSpaceActivity":".TestActivity","name":"test","permission":"testPermission","persistent":true,"process":"test","restoreAnyVersion":false,"requiredAccountType":"com.google","restrictedAccountType":"com.google","supportsRtl":false,"taskAffinity":"test","testOnly":false,"theme":"testTheme","uiOptions":"none","vmSafeMode":false}}');
});

it("toString('pretty-json')", function () {
am.toString('pretty-json').should.equal([
'{',
' "application": {',
' "allowTaskReparenting": false,',
' "allowBackup": true,',
' "backupAgent": ".MyBackupAgent",',
' "debuggable": true,',
' "description": "this is a test",',
' "enabled": true,',
' "hasCode": true,',
' "hardwareAccelerated": true,',
' "icon": "@drawable/icon",',
' "killAfterRestore": true,',
' "largeHeap": false,',
' "label": "test",',
' "logo": "@drawable/logo",',
' "manageSpaceActivity": ".TestActivity",',
' "name": "test",',
' "permission": "testPermission",',
' "persistent": true,',
' "process": "test",',
' "restoreAnyVersion": false,',
' "requiredAccountType": "com.google",',
' "restrictedAccountType": "com.google",',
' "supportsRtl": false,',
' "taskAffinity": "test",',
' "testOnly": false,',
' "theme": "testTheme",',
' "uiOptions": "none",',
' "vmSafeMode": false',
' }',
'}'
].join('\n'));
});

it("toString('xml')", function () {
am.toString('xml').should.equal([
'<?xml version="1.0" encoding="UTF-8"?>',
'<manifest>',
' <application android:allowTaskReparenting="false" android:allowBackup="true" android:backupAgent=".MyBackupAgent" android:debuggable="true" android:description="this is a test" android:enabled="true" android:hasCode="true" android:hardwareAccelerated="true" android:icon="@drawable/icon" android:killAfterRestore="true" android:largeHeap="false" android:label="test" android:logo="@drawable/logo" android:manageSpaceActivity=".TestActivity" android:name="test" android:permission="testPermission" android:persistent="true" android:process="test" android:restoreAnyVersion="false" android:requiredAccountType="com.google" android:restrictedAccountType="com.google" android:supportsRtl="false" android:taskAffinity="test" android:testOnly="false" android:theme="testTheme" android:uiOptions="none" android:vmSafeMode="false"/>',
'</manifest>'
].join('\r\n'));
});
});

describe('Merge AndroidManifest.xml Sample 2, 3, and 4', function () {
var am2 = new AndroidManifest(path.resolve('./resources/AndroidManifest-sample2.xml'));
var am3 = new AndroidManifest(path.resolve('./resources/AndroidManifest-sample3.xml'));
Expand Down
6 changes: 3 additions & 3 deletions android/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@
"Allen Yeung <ayeung@appcelerator.com>"
],
"vendorDependencies": {
"android sdk": ">=10 <=18",
"android build tools": ">=17 <18.x",
"android platform tools": ">=17 <=18.x",
"android sdk": ">=10 <=19",
"android build tools": ">=17 <19.x",
"android platform tools": ">=17 <=19.x",
"android tools": "22.x",
"android ndk": ">=r8e <=r9",
"node": ">0.8.0 <=0.10.x",
Expand Down
2 changes: 1 addition & 1 deletion cli/locales/en.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"builds a project": "builds a project",
"only perform the build; if true, does not install or run the app": "only perform the build; if true, does not install or run the app",
"force a full rebuild": "force a full rebuild",
"build using the old Python-based builder.py": "build using the old Python-based builder.py",
"build using the old Python-based builder.py; deprecated": "build using the old Python-based builder.py; deprecated",
"bypasses JavaScript minification; %s builds are never minified; only supported for %s and %s": "bypasses JavaScript minification; %s builds are never minified; only supported for %s and %s",
"the target build platform": "the target build platform",
"platform": "platform",
Expand Down

0 comments on commit 0f21eec

Please sign in to comment.