Skip to content

Commit

Permalink
Merge branch 'master' into TIMOB-24610
Browse files Browse the repository at this point in the history
  • Loading branch information
sgtcoolguy committed Nov 13, 2017
2 parents 3f57459 + d877ab0 commit a15a923
Show file tree
Hide file tree
Showing 6 changed files with 233 additions and 45 deletions.
42 changes: 28 additions & 14 deletions android/cli/commands/_build.js
Original file line number Diff line number Diff line change
Expand Up @@ -1660,16 +1660,16 @@ process.exit(1);
if (unresolvedDependencies.length) {
/*
let msg = 'could not find required module dependencies:';
for (let dependency of unresolvedDependencies) {
msg += __('\n id: %s version: %s platform: %s required by %s',
dependency.id,
dependency.version ? dependency.version : 'latest',
dependency.platform ? dependency.platform : 'all',
dependency.depended.id);
}
logger.error(msg);
process.exit(1);
*/
for (let dependency of unresolvedDependencies) {
msg += __('\n id: %s version: %s platform: %s required by %s',
dependency.id,
dependency.version ? dependency.version : 'latest',
dependency.platform ? dependency.platform : 'all',
dependency.depended.id);
}
logger.error(msg);
process.exit(1);
*/

// re-validate modules
return this.validateTiModules('android', this.deployType, validateTiModulesCallback.bind(this));
Expand Down Expand Up @@ -2912,7 +2912,7 @@ AndroidBuilder.prototype.getNativeModuleBindings = function getNativeModuleBindi
};

AndroidBuilder.prototype.processTiSymbols = function processTiSymbols(next) {
const depMap = JSON.parse(fs.readFileSync(path.join(this.platformPath, 'dependency.json'))),
var depMap = this.dependencyMap,
modulesMap = JSON.parse(fs.readFileSync(path.join(this.platformPath, 'modules.json'))),
modulesPath = path.join(this.platformPath, 'modules'),
moduleBindings = {},
Expand Down Expand Up @@ -2974,7 +2974,9 @@ AndroidBuilder.prototype.processTiSymbols = function processTiSymbols(next) {
let jar = moduleJarMap[namespace];
if (jar) {
jar = jar === 'titanium.jar' ? path.join(this.platformPath, jar) : path.join(this.platformPath, 'modules', jar);
if (fs.existsSync(jar) && !jarLibraries[jar]) {
if (this.isExternalAndroidLibraryAvailable(jar)) {
this.logger.debug('Excluding library ' + jar.cyan);
} else if (fs.existsSync(jar) && !jarLibraries[jar]) {
this.logger.debug(__('Adding library %s', jar.cyan));
jarLibraries[jar] = 1;
}
Expand All @@ -2983,7 +2985,13 @@ AndroidBuilder.prototype.processTiSymbols = function processTiSymbols(next) {
}

depMap.libraries[namespace] && depMap.libraries[namespace].forEach(function (jar) {
if (fs.existsSync(jar = path.join(this.platformPath, jar)) && !jarLibraries[jar]) {
jar = path.join(this.platformPath, jar);
if (this.isExternalAndroidLibraryAvailable(jar)) {
this.logger.debug('Excluding dependency library ' + jar.cyan);
return;
}

if (fs.existsSync(jar) && !jarLibraries[jar]) {
this.logger.debug(__('Adding dependency library %s', jar.cyan));
jarLibraries[jar] = 1;
}
Expand Down Expand Up @@ -3169,7 +3177,13 @@ AndroidBuilder.prototype.copyModuleResources = function copyModuleResources(next
resPkgFile = jarFile.replace(/\.jar$/, '.respackage');

if (fs.existsSync(resPkgFile) && fs.existsSync(resFile)) {
this.resPackages[resFile] = fs.readFileSync(resPkgFile).toString().split('\n').shift().trim();
const packageName = fs.readFileSync(resPkgFile).toString().split(/\r?\n/).shift().trim();
if (!this.hasAndroidLibrary(packageName)) {
this.resPackages[resFile] = packageName;
} else {
this.logger.info(__('Excluding core module resources of %s (%s) because Android Library with same package name is available.', jarFile, packageName));
return done();
}
}

if (!fs.existsSync(jarFile) || !fs.existsSync(resFile)) {
Expand Down
33 changes: 30 additions & 3 deletions android/cli/commands/_buildModule.js
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ AndroidModuleBuilder.prototype.run = function run(logger, config, cli, finished)
cli.emit('build.module.pre.compile', this, next);
},

'replaceBundledSupportLibraries',
'processResources',
'compileAidlFiles',
'compileModuleJavaSrc',
Expand Down Expand Up @@ -426,6 +427,27 @@ AndroidModuleBuilder.prototype.loginfo = function loginfo() {
this.logger.info(__('Resources Dir: %s', this.resourcesDir.cyan));
};

/**
* Replaces any .jar file in the Class Path that comes bundled with our SDK
* with a user provided one if available.
*
* We need to do this in this in an extra step because by the time our bundled
* Support Libraries will be added, we haven't parsed any other Android
* Libraries yet.
*
* @param {Function} next Callback function
*/
AndroidModuleBuilder.prototype.replaceBundledSupportLibraries = function replaceBundledSupportLibraries(next) {
Object.keys(this.classPaths).forEach(function (libraryPathAndFilename) {
if (this.isExternalAndroidLibraryAvailable(libraryPathAndFilename)) {
this.logger.debug('Excluding library ' + libraryPathAndFilename.cyan);
delete this.classPaths[libraryPathAndFilename];
}
}, this);

next();
};

/**
* Processes resources for this module.
*
Expand Down Expand Up @@ -497,8 +519,13 @@ AndroidModuleBuilder.prototype.processResources = function processResources(next
const resArchivePathAndFilename = path.join(modulesPath, file.replace(/\.jar$/, '.res.zip'));
const respackagePathAndFilename = path.join(modulesPath, file.replace(/\.jar$/, '.respackage'));
if (fs.existsSync(resArchivePathAndFilename) && fs.existsSync(respackagePathAndFilename)) {
extraPackages.push(fs.readFileSync(respackagePathAndFilename).toString().split('\n').shift().trim());
resArchives.push(resArchivePathAndFilename);
const packageName = fs.readFileSync(respackagePathAndFilename).toString().split(/\r?\n/).shift().trim();
if (!this.hasAndroidLibrary(packageName)) {
extraPackages.push(packageName);
resArchives.push(resArchivePathAndFilename);
} else {
this.logger.info(__('Excluding core module resources of %s (%s) because Android Library with same package name is available.', file, packageName));
}
}
}, this);

Expand Down Expand Up @@ -1200,7 +1227,7 @@ AndroidModuleBuilder.prototype.compileJsClosure = function (next) {

this.logger.info(__('Generating v8 bindings'));

const dependsMap = JSON.parse(fs.readFileSync(this.dependencyJsonFile));
const dependsMap = this.dependencyMap;
Array.prototype.push.apply(this.metaData, dependsMap.required);

Object.keys(dependsMap.dependencies).forEach(function (key) {
Expand Down
37 changes: 37 additions & 0 deletions android/cli/hooks/aar-transform.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,43 @@ exports.init = function (logger, config, cli, appc) {
scanModuleAndStartTransform(builder, logger, callback);
}
});

cli.on('build.android.dexer', {
priority: 1100,
/**
* Fixes an issue with Hyperloop 2.1.0 which causes a crash when trying to
* override the Android Support Libraries with local .aar files. Hyperloop
* 2.1.0 will always manually add our bundled Android Support Libraries
* to the dexer paths even if they were replaced by the builder. To fix this
* we check the altered dexer paths again and remove any replaced libraries.
*
* @param {Object} data Hook data
* @param {Function} callback Callback function
*/
pre: function (data, callback) {
const builder = data.ctx;
const dexerOptions = data.args[1].slice(0, 6);
const dexerPaths = data.args[1].slice(6);
let hyperloopModule = null;
builder.nativeLibModules.forEach(function (module) {
if (module.id === 'hyperloop' && module.version === '2.1.0') {
hyperloopModule = module;
}
});
if (hyperloopModule && builder.androidLibraries.length > 0) {
let fixedDexerPaths = [];
dexerPaths.forEach(function (entryPathAndFilename) {
if (!this.isExternalAndroidLibraryAvailable(entryPathAndFilename)) {
fixedDexerPaths.push(entryPathAndFilename);
} else {
logger.trace('Removed duplicate library ' + entryPathAndFilename + ' from dexer paths.');
}
}, builder);
data.args[1] = dexerOptions.concat(fixedDexerPaths);
}
callback();
}
});
};

/**
Expand Down
76 changes: 76 additions & 0 deletions android/cli/lib/base-builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,30 @@ const appc = require('node-appc'),
__ = i18nLib.__,
xml = appc.xml;

/**
* The base Android builder that includes common functionality that is used
* in both app and module builds.
*/
function AndroidBaseBuilder() {
Builder.apply(this, arguments);

this.androidLibraries = [];
this.dependencyMap = JSON.parse(fs.readFileSync(path.join(this.platformPath, 'dependency.json')));
}

util.inherits(AndroidBaseBuilder, Builder);

/**
* Utility function to merge the contents of XML files.
*
* If the destination file does not exists, the source file will simply
* be copied over. If both source and destination already exists AND the source
* document contains Android resources, the files will be merged. Otherwise the
* destination file will be overwritten.
*
* @param {string|Document} srcOrDoc Path to source XML file or already parsed Document object
* @param {string} dest Path of the destination XML file
*/
AndroidBaseBuilder.prototype.writeXmlFile = function writeXmlFile(srcOrDoc, dest) {
let destExists = fs.existsSync(dest),
destDoc;
Expand Down Expand Up @@ -99,4 +115,64 @@ AndroidBaseBuilder.prototype.writeXmlFile = function writeXmlFile(srcOrDoc, dest
fs.writeFileSync(dest, '<?xml version="1.0" encoding="UTF-8"?>\n' + dom.documentElement.toString());
};

/**
* Checks wether an Android Library with the given package name is available.
*
* @param {string} packageName Package name of the Android Library to search for
* @return {Boolean} True if the Android Library is available, false if not
*/
AndroidBaseBuilder.prototype.hasAndroidLibrary = function hasAndroidLibrary(packageName) {
return this.androidLibraries.some(function (libraryInfo) {
return libraryInfo.packageName === packageName;
});
};

/**
* Checks if one of our bundled Android Support Libraries (.jar) is also available
* as an Android Library (.aar) provided by the user.
*
* This is used during the build process to allow users to replace any of our
* bundled Android Support Libraries with one of their own choosing. Currently
* supported Android Support Library versions are 24.2.0 - 25.x.
*
* To find out which .jar library can be replaces by which Android Library,
* we depend on a hardcoded list of Android Library package names and the bundled
* library filenames they can replace. This list is manually taken from
* android/dependency.json and needs to be maintained if anything changes there.
*
* @param {string} libraryPathAndFilename Path and filename to the .jar file to check
* @return {Boolean} True if the given library is available as an Android Library, false if not
*/
AndroidBaseBuilder.prototype.isExternalAndroidLibraryAvailable = function isExternalAndroidLibraryAvailable(libraryPathAndFilename) {
const replaceableAndroidLibraries = {
'android.support.graphics.drawable': [ 'android-support-vector-drawable.jar' ],
'android.support.graphics.drawable.animated': [ 'android-support-animated-vector-drawable.jar' ],
'android.support.v4': [ 'android-support-v4.jar' ],
'android.support.compat': [ 'android-support-compat.jar' ],
'android.support.coreui': [ 'android-support-core-ui.jar' ],
'android.support.coreutils': [ 'android-support-core-utils.jar' ],
'android.support.design': [ 'android-support-design.jar' ],
'android.support.fragment': [ 'android-support-fragment.jar' ],
'android.support.mediacompat': [ 'android-support-media-compat.jar' ],
'android.support.transition': [ 'android-support-transition.jar' ],
'android.support.v7.appcompat': [ 'android-support-v7-appcompat.jar' ],
'android.support.v7.cardview': [ 'android-support-v7-cardview.jar' ],
'android.support.v7.recyclerview': [ 'android-support-v7-recyclerview.jar' ]
};
return this.androidLibraries.some(function (libraryInfo) {
if (!replaceableAndroidLibraries[libraryInfo.packageName]) {
return false;
}

const libraryFilename = path.basename(libraryPathAndFilename);
const shouldExcludeLibrary = replaceableAndroidLibraries[libraryInfo.packageName].indexOf(libraryFilename) !== -1;
if (shouldExcludeLibrary) {
this.logger.trace(__('Android library %s (%s) available, marking %s to be excluded.', libraryInfo.task.aarPathAndFilename, libraryInfo.packageName.cyan, libraryPathAndFilename.cyan));
return true;
}

return false;
}, this);
};

module.exports = AndroidBaseBuilder;
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,12 @@
import org.appcelerator.titanium.TiC;
import org.appcelerator.titanium.util.TiConvert;

import android.content.ClipData;
import android.graphics.Bitmap;
import android.content.ComponentName;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.text.TextUtils;

@Kroll.proxy(propertyAccessors = {
Expand Down Expand Up @@ -189,17 +191,22 @@ public void handleCreationDict(KrollDict dict)
// setType and setData are inexplicably intertwined
// calling setType by itself clears the type and vice-versa
// if you have both you _must_ call setDataAndType
if (type != null) {
Log.d(TAG, "Setting type: " + type, Log.DEBUG_MODE);
if (data != null) {
if (data != null) {
Uri dataUri = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && data.startsWith("file://")) {
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setDataAndType(TiFileProvider.createUriFrom(data), type);
dataUri = TiFileProvider.createUriFrom(data);
} else {
intent.setType(type);
dataUri = Uri.parse(data);
}
} else if (data != null) {
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setData(TiFileProvider.createUriFrom(data));
if (type != null) {
Log.d(TAG, "setting type: " + type, Log.DEBUG_MODE);
intent.setDataAndType(dataUri, type);
} else {
intent.setData(dataUri);
}
} else {
intent.setType(type);
}
}

Expand Down Expand Up @@ -258,25 +265,50 @@ public int getFlags()
@Kroll.method
public void putExtraUri(String key, Object value)
{
if (value == null) {
return;
}

if (value instanceof String) {
intent.putExtra(key, Uri.parse((String) value));
} else if (value instanceof Object[]) {
try {
Object[] objVal = (Object[]) value;
String[] stringArray = Arrays.copyOf(objVal, objVal.length, String[].class);
ArrayList<Uri> imageUris = new ArrayList<Uri>();
for(String s: stringArray) {
imageUris.add(Uri.parse(s));
}
intent.putParcelableArrayListExtra(key, imageUris);
} catch (Exception ex) {
Log.e(TAG, "Error unimplemented put conversion ", ex.getMessage());
}
}
if (value == null) {
return;
}

if (value instanceof String) {
String extraString = (String) value;

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && extraString.startsWith("file://")) {
Uri contentUri = TiFileProvider.createUriFrom(extraString);
ClipData clipData = ClipData.newRawUri("FILE", contentUri);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setClipData(clipData);
intent.putExtra(key, contentUri);
} else {
intent.putExtra(key, Uri.parse(extraString));
}
} else if (value instanceof Object[]) {
try {
Object[] objVal = (Object[]) value;
String[] stringArray = Arrays.copyOf(objVal, objVal.length, String[].class);
ArrayList<Uri> imageUris = new ArrayList<Uri>();
ClipData clipData = null;
for(String s : stringArray) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && s.startsWith("file://")) {
Uri contentUri = TiFileProvider.createUriFrom(s);
imageUris.add(contentUri);
if (clipData == null) {
clipData = ClipData.newRawUri("FILES", contentUri);
} else {
clipData.addItem(new ClipData.Item(contentUri));
}
} else {
imageUris.add(Uri.parse(s));
}
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setClipData(clipData);
}
intent.putParcelableArrayListExtra(key, imageUris);
} catch (Exception ex) {
Log.e(TAG, "Error unimplemented put conversion ", ex.getMessage());
}
}
}

@Kroll.method
Expand Down

0 comments on commit a15a923

Please sign in to comment.