Skip to content

Commit 4c115e6

Browse files
Fatme Havaluovarosen-vladimirov
Fatme Havaluova
authored andcommitted
Support for android native libraries in plugin
1 parent 74a647e commit 4c115e6

File tree

8 files changed

+183
-91
lines changed

8 files changed

+183
-91
lines changed

bin/nativescript.js

100644100755
File mode changed.

lib/definitions/libref.ts

+1
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@ interface ILibRef {
22
idx: number;
33
path: string;
44
adjustedPath?: string;
5+
key?: string;
56
}

lib/definitions/plugins.d.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ interface IPluginsService {
77
}
88

99
interface IPluginData extends INodeModuleData {
10-
platformsData: IPluginPlatformsData;
10+
platformsData: IPluginPlatformsData;
11+
pluginPlatformsFolderPath(platform: string): string;
1112
}
1213

1314
interface INodeModuleData {

lib/definitions/project.d.ts

+2
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,6 @@ interface IPlatformProjectService {
3131
addLibrary(platformData: IPlatformData, libraryPath: string): IFuture<void>;
3232
canUpdatePlatform(currentVersion: string, newVersion: string): IFuture<boolean>;
3333
updatePlatform(currentVersion: string, newVersion: string): IFuture<void>;
34+
preparePluginNativeCode(pluginData: IPluginData): IFuture<void>;
35+
removePluginNativeCode(pluginData: IPluginData): IFuture<void>;
3436
}

lib/services/android-project-service.ts

+98-31
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ class AndroidProjectService implements IPlatformProjectService {
1616
private static RES_DIRNAME = "res";
1717
private static VALUES_DIRNAME = "values";
1818
private static VALUES_VERSION_DIRNAME_PREFIX = AndroidProjectService.VALUES_DIRNAME + "-v";
19+
private static ANDROID_PLATFORM_NAME = "android";
20+
private static LIBS_FOLDER_NAME = "libs";
21+
1922

2023
private targetApi: string;
2124

@@ -51,7 +54,7 @@ class AndroidProjectService implements IPlatformProjectService {
5154
frameworkFilesExtensions: [".jar", ".dat", ".so"],
5255
configurationFileName: "AndroidManifest.xml",
5356
configurationFilePath: path.join(this.$projectData.platformsDir, "android", "AndroidManifest.xml"),
54-
mergeXmlConfig: [{ "nodename": "manifest", "attrname": "*" }]
57+
mergeXmlConfig: [{ "nodename": "manifest", "attrname": "*" }, {"nodename": "application", "attrname": "*"}]
5558
};
5659
}
5760

@@ -181,35 +184,37 @@ class AndroidProjectService implements IPlatformProjectService {
181184
public isPlatformPrepared(projectRoot: string): IFuture<boolean> {
182185
return this.$fs.exists(path.join(projectRoot, "assets", constants.APP_FOLDER_NAME));
183186
}
184-
185-
private parseProjectProperties(projDir: string, destDir: string): void {
186-
let projProp = path.join(projDir, "project.properties");
187-
188-
if (!this.$fs.exists(projProp).wait()) {
189-
this.$logger.warn("Warning: File %s does not exist", projProp);
190-
return;
191-
}
192-
193-
let lines = this.$fs.readText(projProp, "utf-8").wait().split(os.EOL);
194-
195-
let regEx = /android\.library\.reference\.(\d+)=(.*)/;
196-
lines.forEach(elem => {
197-
let match = elem.match(regEx);
198-
if (match) {
199-
let libRef: ILibRef = { idx: parseInt(match[1]), path: match[2].trim() };
200-
libRef.adjustedPath = this.$fs.isRelativePath(libRef.path) ? path.join(projDir, libRef.path) : libRef.path;
201-
this.parseProjectProperties(libRef.adjustedPath, destDir);
187+
188+
private parseProjectProperties(projDir: string, destDir: string): IFuture<void> {
189+
return (() => {
190+
let projProp = path.join(projDir, "project.properties");
191+
192+
if (!this.$fs.exists(projProp).wait()) {
193+
this.$logger.warn("Warning: File %s does not exist", projProp);
194+
return;
202195
}
203-
});
204-
205-
this.$logger.info("Copying %s", projDir);
206-
shell.cp("-Rf", projDir, destDir);
207-
208-
let targetDir = path.join(destDir, path.basename(projDir));
209-
// TODO: parametrize targetSdk
210-
let targetSdk = "android-17";
211-
this.$logger.info("Generate build.xml for %s", targetDir);
212-
this.runAndroidUpdate(targetDir, targetSdk).wait();
196+
197+
let lines = this.$fs.readText(projProp, "utf-8").wait().split(os.EOL);
198+
199+
let regEx = /android\.library\.reference\.(\d+)=(.*)/;
200+
lines.forEach(elem => {
201+
let match = elem.match(regEx);
202+
if (match) {
203+
let libRef: ILibRef = { idx: parseInt(match[1]), path: match[2].trim() };
204+
libRef.adjustedPath = this.$fs.isRelativePath(libRef.path) ? path.join(projDir, libRef.path) : libRef.path;
205+
this.parseProjectProperties(libRef.adjustedPath, destDir).wait();
206+
}
207+
});
208+
209+
this.$logger.info("Copying %s", projDir);
210+
shell.cp("-Rf", projDir, destDir);
211+
212+
let targetDir = path.join(destDir, path.basename(projDir));
213+
// TODO: parametrize targetSdk
214+
let targetSdk = "android-17";
215+
this.$logger.info("Generate build.xml for %s", targetDir);
216+
this.runAndroidUpdate(targetDir, targetSdk).wait();
217+
}).future<void>()();
213218
}
214219

215220
private getProjectReferences(projDir: string): ILibRef[]{
@@ -225,6 +230,7 @@ class AndroidProjectService implements IPlatformProjectService {
225230
if (match) {
226231
let libRef: ILibRef = { idx: parseInt(match[1]), path: match[2] };
227232
libRef.adjustedPath = path.join(projDir, libRef.path);
233+
libRef.key = match[0].split("=")[0];
228234
refs.push(libRef);
229235
}
230236
});
@@ -254,7 +260,7 @@ class AndroidProjectService implements IPlatformProjectService {
254260
let targetPath = path.join(projDir, "lib", platformData.normalizedPlatformName);
255261
this.$fs.ensureDirectoryExists(targetPath).wait();
256262

257-
this.parseProjectProperties(libraryPath, targetPath);
263+
this.parseProjectProperties(libraryPath, targetPath).wait();
258264

259265
shell.cp("-f", path.join(libraryPath, "*.jar"), targetPath);
260266
let projectLibsDir = path.join(platformData.projectRoot, "libs");
@@ -269,10 +275,71 @@ class AndroidProjectService implements IPlatformProjectService {
269275
}
270276
}).future<void>()();
271277
}
272-
278+
273279
public getFrameworkFilesExtensions(): string[] {
274280
return [".jar", ".dat"];
275281
}
282+
283+
public preparePluginNativeCode(pluginData: IPluginData): IFuture<void> {
284+
return (() => {
285+
let pluginPlatformsFolderPath = this.getPluginPlatformsFolderPath(pluginData);
286+
287+
// Handle *.jars inside libs folder
288+
let libsFolderPath = path.join(pluginPlatformsFolderPath, AndroidProjectService.LIBS_FOLDER_NAME);
289+
if(this.$fs.exists(libsFolderPath).wait()) {
290+
let libsFolderContents = this.$fs.readDirectory(libsFolderPath).wait();
291+
_(libsFolderContents)
292+
.filter(libsFolderItem => path.extname(libsFolderItem) === ".jar")
293+
.map(jar => this.addLibrary(this.platformData, path.join(libsFolderPath, jar)).wait());
294+
}
295+
296+
// Handle android libraries
297+
let androidLibraries = this.getAllAndroidLibrariesForPlugin(pluginData).wait();
298+
_.each(androidLibraries, androidLibraryName => this.addLibrary(this.platformData, path.join(pluginPlatformsFolderPath, androidLibraryName)).wait());
299+
300+
}).future<void>()();
301+
}
302+
303+
public removePluginNativeCode(pluginData: IPluginData): IFuture<void> {
304+
return (() => {
305+
let projectReferences = this.getProjectReferences(this.platformData.projectRoot);
306+
let androidLibraries = this.getAllAndroidLibrariesForPlugin(pluginData).wait();
307+
308+
let file = path.join(this.platformData.projectRoot, "project.properties");
309+
let editor = this.$propertiesParser.createEditor(file).wait();
310+
311+
_.each(androidLibraries, androidLibraryName => {
312+
// Remove library from project.properties
313+
let androidLibraryNameLowerCase = androidLibraryName.toLowerCase();
314+
let projectReference = _.find(projectReferences, projectReference => _.last(projectReference.adjustedPath.split(path.sep)).toLowerCase() === androidLibraryNameLowerCase);
315+
if(projectReference && projectReference.key) {
316+
editor.unset(projectReference.key);
317+
}
318+
319+
// Remove library from lib folder
320+
this.$fs.deleteDirectory(path.join(this.$projectData.projectDir, "lib", this.platformData.normalizedPlatformName, androidLibraryName)).wait();
321+
});
322+
323+
this.$propertiesParser.saveEditor().wait();
324+
325+
}).future<void>()();
326+
}
327+
328+
private getPluginPlatformsFolderPath(pluginData: IPluginData) {
329+
return pluginData.pluginPlatformsFolderPath(AndroidProjectService.ANDROID_PLATFORM_NAME);
330+
}
331+
332+
private getAllAndroidLibrariesForPlugin(pluginData: IPluginData): IFuture<string[]> {
333+
return (() => {
334+
let pluginPlatformsFolderPath = this.getPluginPlatformsFolderPath(pluginData);
335+
let platformsContents = this.$fs.readDirectory(pluginPlatformsFolderPath).wait();
336+
return _(platformsContents)
337+
.filter(platformItemName => platformItemName !== AndroidProjectService.LIBS_FOLDER_NAME &&
338+
this.$fs.exists(path.join(pluginPlatformsFolderPath, platformItemName, "project.properties")).wait())
339+
.map(androidLibraryName => androidLibraryName)
340+
.value();
341+
}).future<string[]>()();
342+
}
276343

277344
private copy(projectRoot: string, frameworkDir: string, files: string, cpArg: string): IFuture<void> {
278345
return (() => {

lib/services/ios-project-service.ts

+10
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,16 @@ class IOSProjectService implements IPlatformProjectService {
226226
this.replaceFileContent(pbxprojFilePath).wait();
227227
}).future<void>()();
228228
}
229+
230+
public preparePluginNativeCode(pluginData: IPluginData): IFuture<void> {
231+
return (() => {
232+
}).future<void>()();
233+
}
234+
235+
public removePluginNativeCode(pluginData: IPluginData): IFuture<void> {
236+
return (() => {
237+
}).future<void>()();
238+
}
229239

230240
private buildPathToXcodeProjectFile(version: string): string {
231241
return path.join(this.$npmInstallationManager.getCachedPackagePath(this.platformData.frameworkPackageName, version), constants.PROJECT_FRAMEWORK_FOLDER_NAME, util.format("%s.xcodeproj", IOSProjectService.IOS_PROJECT_NAME_PLACEHOLDER), "project.pbxproj");

lib/services/plugins-service.ts

+69-58
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,24 @@ export class PluginsService implements IPluginsService {
4343

4444
public remove(pluginName: string): IFuture<void> {
4545
return (() => {
46+
let removePluginNativeCodeAction = (modulesDestinationPath: string, platform: string, platformData: IPlatformData) => {
47+
let pluginData = this.convertToPluginData(this.getNodeModuleData(pluginName).wait());
48+
pluginData.isPlugin = true;
49+
return platformData.platformProjectService.removePluginNativeCode(pluginData);
50+
};
51+
this.executeForAllInstalledPlatforms(removePluginNativeCodeAction).wait();
52+
4653
this.executeNpmCommand(PluginsService.UNINSTALL_COMMAND_NAME, pluginName).wait();
4754
let showMessage = true;
4855
let action = (modulesDestinationPath: string, platform: string, platformData: IPlatformData) => {
49-
shelljs.rm("-rf", path.join(modulesDestinationPath, pluginName));
50-
this.$logger.out(`Successfully removed plugin ${pluginName} for ${platform}.`);
51-
showMessage = false;
56+
return (() => {
57+
shelljs.rm("-rf", path.join(modulesDestinationPath, pluginName));
58+
59+
this.$logger.out(`Successfully removed plugin ${pluginName} for ${platform}.`);
60+
showMessage = false;
61+
}).future<void>()();
5262
};
53-
this.executeForAllInstalledPlatforms(action);
63+
this.executeForAllInstalledPlatforms(action).wait();
5464

5565
if(showMessage) {
5666
this.$logger.out(`Succsessfully removed plugin ${pluginName}`);
@@ -61,55 +71,53 @@ export class PluginsService implements IPluginsService {
6171
public prepare(pluginData: IPluginData): IFuture<void> {
6272
return (() => {
6373
let action = (pluginDestinationPath: string, platform: string, platformData: IPlatformData) => {
64-
// Process .js files
65-
let installedFrameworkVersion = this.getInstalledFrameworkVersion(platform).wait();
66-
let pluginPlatformsData = pluginData.platformsData;
67-
if(pluginPlatformsData) {
68-
let pluginVersion = (<any>pluginPlatformsData)[platform];
69-
if(!pluginVersion) {
70-
this.$logger.warn(`${pluginData.name} is not supported for ${platform}.`);
71-
return;
72-
}
73-
74-
if(semver.gt(pluginVersion, installedFrameworkVersion)) {
75-
this.$logger.warn(`${pluginData.name} ${pluginVersion} for ${platform} is not compatible with the currently installed framework version ${installedFrameworkVersion}.`);
76-
return;
74+
return (() => {
75+
// Process .js files
76+
let installedFrameworkVersion = this.getInstalledFrameworkVersion(platform).wait();
77+
let pluginPlatformsData = pluginData.platformsData;
78+
if(pluginPlatformsData) {
79+
let pluginVersion = (<any>pluginPlatformsData)[platform];
80+
if(!pluginVersion) {
81+
this.$logger.warn(`${pluginData.name} is not supported for ${platform}.`);
82+
return;
83+
}
84+
85+
if(semver.gt(pluginVersion, installedFrameworkVersion)) {
86+
this.$logger.warn(`${pluginData.name} ${pluginVersion} for ${platform} is not compatible with the currently installed framework version ${installedFrameworkVersion}.`);
87+
return;
88+
}
7789
}
78-
}
79-
80-
this.$fs.ensureDirectoryExists(pluginDestinationPath).wait();
81-
shelljs.cp("-R", pluginData.fullPath, pluginDestinationPath);
82-
83-
let pluginPlatformsFolderPath = path.join(pluginDestinationPath, pluginData.name, "platforms", platform);
84-
let pluginConfigurationFilePath = path.join(pluginPlatformsFolderPath, platformData.configurationFileName);
85-
let configurationFilePath = platformData.configurationFilePath;
8690

87-
if(this.$fs.exists(pluginConfigurationFilePath).wait()) {
88-
// Validate plugin configuration file
89-
let pluginConfigurationFileContent = this.$fs.readText(pluginConfigurationFilePath).wait();
90-
this.validateXml(pluginConfigurationFileContent, pluginConfigurationFilePath);
91+
this.$fs.ensureDirectoryExists(pluginDestinationPath).wait();
92+
shelljs.cp("-R", pluginData.fullPath, pluginDestinationPath);
93+
94+
let pluginPlatformsFolderPath = path.join(pluginDestinationPath, pluginData.name, "platforms", platform);
95+
let pluginConfigurationFilePath = path.join(pluginPlatformsFolderPath, platformData.configurationFileName);
96+
let configurationFilePath = platformData.configurationFilePath;
9197

92-
// Validate configuration file
93-
let configurationFileContent = this.$fs.readText(configurationFilePath).wait();
94-
this.validateXml(configurationFileContent, configurationFilePath);
98+
if(this.$fs.exists(pluginConfigurationFilePath).wait()) {
99+
// Validate plugin configuration file
100+
let pluginConfigurationFileContent = this.$fs.readText(pluginConfigurationFilePath).wait();
101+
this.validateXml(pluginConfigurationFileContent, pluginConfigurationFilePath);
102+
103+
// Validate configuration file
104+
let configurationFileContent = this.$fs.readText(configurationFilePath).wait();
105+
this.validateXml(configurationFileContent, configurationFilePath);
106+
107+
// Merge xml
108+
let resultXml = this.mergeXml(configurationFileContent, pluginConfigurationFileContent, platformData.mergeXmlConfig || []).wait();
109+
this.validateXml(resultXml);
110+
this.$fs.writeFile(configurationFilePath, resultXml).wait();
111+
}
95112

96-
// Merge xml
97-
let resultXml = this.mergeXml(configurationFileContent, pluginConfigurationFileContent, platformData.mergeXmlConfig || []).wait();
98-
this.validateXml(resultXml);
99-
this.$fs.writeFile(configurationFilePath, resultXml).wait();
100-
}
101-
102-
if(this.$fs.exists(pluginPlatformsFolderPath).wait()) {
103-
shelljs.rm("-rf", pluginPlatformsFolderPath);
104-
}
105-
106-
// TODO: Add libraries
107-
108-
// Show message
109-
this.$logger.out(`Successfully prepared plugin ${pluginData.name} for ${platform}.`);
113+
platformData.platformProjectService.preparePluginNativeCode(pluginData).wait();
114+
115+
// Show message
116+
this.$logger.out(`Successfully prepared plugin ${pluginData.name} for ${platform}.`);
117+
}).future<void>()();
110118
};
111119

112-
this.executeForAllInstalledPlatforms(action);
120+
this.executeForAllInstalledPlatforms(action).wait();
113121
}).future<void>()();
114122
}
115123

@@ -159,12 +167,13 @@ export class PluginsService implements IPluginsService {
159167
}).future<INodeModuleData>()();
160168
}
161169

162-
private convertToPluginData(cacheData: ICacheData): IPluginData {
170+
private convertToPluginData(cacheData: any): IPluginData {
163171
let pluginData: any = {};
164172
pluginData.name = cacheData.name;
165173
pluginData.version = cacheData.version;
166174
pluginData.fullPath = path.dirname(this.getPackageJsonFilePathForModule(cacheData.name));
167175
pluginData.isPlugin = !!cacheData.nativescript;
176+
pluginData.pluginPlatformsFolderPath = (platform: string) => path.join(pluginData.fullPath, "platforms", platform);
168177

169178
if(pluginData.isPlugin) {
170179
pluginData.platformsData = cacheData.nativescript.platforms;
@@ -201,16 +210,18 @@ export class PluginsService implements IPluginsService {
201210
return npmCommandResult[0][0].split("@")[0]; // returns plugin name
202211
}
203212

204-
private executeForAllInstalledPlatforms(action: (pluginDestinationPath: string, pl: string, platformData: IPlatformData) => void): void {
205-
let availablePlatforms = _.keys(this.$platformsData.availablePlatforms);
206-
_.each(availablePlatforms, platform => {
207-
let isPlatformInstalled = this.$fs.exists(path.join(this.$projectData.platformsDir, platform.toLowerCase())).wait();
208-
if(isPlatformInstalled) {
209-
let platformData = this.$platformsData.getPlatformData(platform.toLowerCase());
210-
let pluginDestinationPath = path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME, "tns_modules");
211-
action(pluginDestinationPath, platform.toLowerCase(), platformData);
212-
}
213-
});
213+
private executeForAllInstalledPlatforms(action: (pluginDestinationPath: string, pl: string, platformData: IPlatformData) => IFuture<void>): IFuture<void> {
214+
return (() => {
215+
let availablePlatforms = _.keys(this.$platformsData.availablePlatforms);
216+
_.each(availablePlatforms, platform => {
217+
let isPlatformInstalled = this.$fs.exists(path.join(this.$projectData.platformsDir, platform.toLowerCase())).wait();
218+
if(isPlatformInstalled) {
219+
let platformData = this.$platformsData.getPlatformData(platform.toLowerCase());
220+
let pluginDestinationPath = path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME, "tns_modules");
221+
action(pluginDestinationPath, platform.toLowerCase(), platformData).wait();
222+
}
223+
});
224+
}).future<void>()();
214225
}
215226

216227
private getInstalledFrameworkVersion(platform: string): IFuture<string> {

0 commit comments

Comments
 (0)