Skip to content

Commit dc65d52

Browse files
Merge pull request #5121 from NativeScript/vladimirov/fix-values-filtering
fix: values-v<sdk> directories are not prepared correctly
2 parents a0434f3 + 5ccfba6 commit dc65d52

File tree

2 files changed

+219
-11
lines changed

2 files changed

+219
-11
lines changed

lib/services/android-project-service.ts

+37-8
Original file line numberDiff line numberDiff line change
@@ -142,11 +142,23 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject
142142

143143
this.copy(this.getPlatformData(projectData).projectRoot, frameworkDir, "*", "-R");
144144

145+
// TODO: Check if we actually need this and if it should be targetSdk or compileSdk
145146
this.cleanResValues(targetSdkVersion, projectData);
146147
}
147148

149+
private getResDestinationDir(projectData: IProjectData): string {
150+
const appResourcesDirStructureHasMigrated = this.$androidResourcesMigrationService.hasMigrated(projectData.getAppResourcesDirectoryPath());
151+
152+
if (appResourcesDirStructureHasMigrated) {
153+
const appResourcesDestinationPath = this.getUpdatedAppResourcesDestinationDirPath(projectData);
154+
return path.join(appResourcesDestinationPath, constants.MAIN_DIR, constants.RESOURCES_DIR);
155+
} else {
156+
return this.getLegacyAppResourcesDestinationDirPath(projectData);
157+
}
158+
}
159+
148160
private cleanResValues(targetSdkVersion: number, projectData: IProjectData): void {
149-
const resDestinationDir = this.getAppResourcesDestinationDirectoryPath(projectData);
161+
const resDestinationDir = this.getResDestinationDir(projectData);
150162
const directoriesInResFolder = this.$fs.readDirectory(resDestinationDir);
151163
const directoriesToClean = directoriesInResFolder
152164
.map(dir => {
@@ -283,7 +295,7 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject
283295
const projectAppResourcesPath = projectData.getAppResourcesDirectoryPath(projectData.projectDir);
284296
const platformsAppResourcesPath = this.getAppResourcesDestinationDirectoryPath(projectData);
285297

286-
this.cleanUpPreparedResources(projectAppResourcesPath, projectData);
298+
this.cleanUpPreparedResources(projectData);
287299

288300
this.$fs.ensureDirectoryExists(platformsAppResourcesPath);
289301

@@ -296,6 +308,10 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject
296308
// App_Resources/Android/libs is reserved to user's aars and jars, but they should not be copied as resources
297309
this.$fs.deleteDirectory(path.join(platformsAppResourcesPath, "libs"));
298310
}
311+
312+
const androidToolsInfo = this.$androidToolsInfo.getToolsInfo({ projectDir: projectData.projectDir });
313+
const compileSdkVersion = androidToolsInfo && androidToolsInfo.compileSdkVersion;
314+
this.cleanResValues(compileSdkVersion, projectData);
299315
}
300316

301317
public async preparePluginNativeCode(pluginData: IPluginData, projectData: IProjectData): Promise<void> {
@@ -431,18 +447,31 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject
431447
return path.join(this.getPlatformData(projectData).projectRoot, ...resourcePath);
432448
}
433449

434-
private cleanUpPreparedResources(appResourcesDirectoryPath: string, projectData: IProjectData): void {
435-
let resourcesDirPath = path.join(appResourcesDirectoryPath, this.getPlatformData(projectData).normalizedPlatformName);
450+
/**
451+
* The purpose of this method is to delete the previously prepared user resources.
452+
* The content of the `<platforms>/android/.../res` directory is based on user's resources and gradle project template from android-runtime.
453+
* During preparation of the `<path to user's App_Resources>/Android` we want to clean all the users files from previous preparation,
454+
* but keep the ones that were introduced during `platform add` of the android-runtime.
455+
* Currently the Gradle project template contains resources only in values and values-v21 directories.
456+
* So the current logic of the method is cleaning al resources from `<platforms>/android/.../res` that are not in `values.*` directories
457+
* and that exist in the `<path to user's App_Resources>/Android/.../res` directory
458+
* This means that if user has a resource file in values-v29 for example, builds the project and then deletes this resource,
459+
* it will be kept in platforms directory. Reference issue: `https://github.com/NativeScript/nativescript-cli/issues/5083`
460+
* Same is valid for files in `drawable-<resolution>` directories - in case in user's resources there's drawable-hdpi directory,
461+
* which is deleted after the first build of app, it will remain in platforms directory.
462+
*/
463+
private cleanUpPreparedResources(projectData: IProjectData): void {
464+
let resourcesDirPath = path.join(projectData.appResourcesDirectoryPath, this.getPlatformData(projectData).normalizedPlatformName);
436465
if (this.$androidResourcesMigrationService.hasMigrated(projectData.appResourcesDirectoryPath)) {
437-
resourcesDirPath = path.join(resourcesDirPath, constants.MAIN_DIR, constants.RESOURCES_DIR);
466+
resourcesDirPath = path.join(resourcesDirPath, constants.SRC_DIR, constants.MAIN_DIR, constants.RESOURCES_DIR);
438467
}
439468

440469
const valuesDirRegExp = /^values/;
441470
if (this.$fs.exists(resourcesDirPath)) {
442471
const resourcesDirs = this.$fs.readDirectory(resourcesDirPath).filter(resDir => !resDir.match(valuesDirRegExp));
443-
const appResourcesDestinationDirectoryPath = this.getAppResourcesDestinationDirectoryPath(projectData);
444-
_.each(resourcesDirs, resourceDir => {
445-
this.$fs.deleteDirectory(path.join(appResourcesDestinationDirectoryPath, resourceDir));
472+
const resDestinationDir = this.getResDestinationDir(projectData);
473+
_.each(resourcesDirs, currentResource => {
474+
this.$fs.deleteDirectory(path.join(resDestinationDir, currentResource));
446475
});
447476
}
448477
}

test/services/android-project-service.ts

+182-3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { Yok } from "../../lib/common/yok";
33
import * as stubs from "../stubs";
44
import { assert } from "chai";
55
import * as sinon from "sinon";
6+
import * as path from "path";
67
import { GradleCommandService } from "../../lib/services/android/gradle-command-service";
78
import { GradleBuildService } from "../../lib/services/android/gradle-build-service";
89
import { GradleBuildArgsService } from "../../lib/services/android/gradle-build-args-service";
@@ -42,7 +43,7 @@ const createTestInjector = (): IInjector => {
4243
testInjector.register("gradleBuildService", GradleBuildService);
4344
testInjector.register("gradleBuildArgsService", GradleBuildArgsService);
4445
testInjector.register("analyticsService", stubs.AnalyticsService);
45-
testInjector.register("staticConfig", {TRACK_FEATURE_USAGE_SETTING_NAME: "TrackFeatureUsage"});
46+
testInjector.register("staticConfig", { TRACK_FEATURE_USAGE_SETTING_NAME: "TrackFeatureUsage" });
4647
return testInjector;
4748
};
4849

@@ -59,7 +60,7 @@ const getDefautlBuildConfig = (): IBuildConfig => {
5960
};
6061
};
6162

62-
describe("androidDeviceDebugService", () => {
63+
describe("androidProjectService", () => {
6364
let injector: IInjector;
6465
let androidProjectService: IPlatformProjectService;
6566
let sandbox: sinon.SinonSandbox = null;
@@ -74,7 +75,7 @@ describe("androidDeviceDebugService", () => {
7475
sandbox.restore();
7576
});
7677

77-
describe("buildPlatform", () => {
78+
describe("buildProject", () => {
7879
let projectData: IProjectData;
7980
let childProcess: stubs.ChildProcessStub;
8081

@@ -138,4 +139,182 @@ describe("androidDeviceDebugService", () => {
138139
assert.include(childProcess.lastCommandArgs, "bundleDebug");
139140
});
140141
});
142+
143+
describe("prepareAppResources", () => {
144+
const projectDir = "testDir";
145+
const pathToAppResourcesDir = path.join(projectDir, "app", "App_Resources");
146+
const pathToAppResourcesAndroid = path.join(pathToAppResourcesDir, "Android");
147+
const pathToPlatformsAndroid = path.join(projectDir, "platforms", "android");
148+
const pathToResDirInPlatforms = path.join(pathToPlatformsAndroid, "app", "src", "main", "res");
149+
const valuesV27Path = path.join(pathToResDirInPlatforms, "values-v27");
150+
const valuesV28Path = path.join(pathToResDirInPlatforms, "values-v28");
151+
const libsPath = path.join(pathToResDirInPlatforms, "libs");
152+
const drawableHdpiPath = path.join(pathToResDirInPlatforms, "drawable-hdpi");
153+
const drawableLdpiPath = path.join(pathToResDirInPlatforms, "drawable-ldpi");
154+
let deletedDirs: string[] = [];
155+
let copiedFiles: { sourceFileName: string, destinationFileName: string }[] = [];
156+
let readDirectoryResults: IDictionary<string[]> = {};
157+
let fs: IFileSystem = null;
158+
let projectData: IProjectData = null;
159+
let compileSdkVersion = 29;
160+
161+
beforeEach(() => {
162+
projectData = injector.resolve("projectData");
163+
projectData.projectDir = projectDir;
164+
projectData.appResourcesDirectoryPath = pathToAppResourcesDir;
165+
166+
deletedDirs = [];
167+
copiedFiles = [];
168+
readDirectoryResults = {};
169+
170+
fs = injector.resolve<IFileSystem>("fs");
171+
fs.deleteDirectory = (directory: string): void => {
172+
deletedDirs.push(directory);
173+
};
174+
fs.copyFile = (sourceFileName: string, destinationFileName: string): void => {
175+
copiedFiles.push({ sourceFileName, destinationFileName });
176+
};
177+
fs.readDirectory = (dir: string): string[] => {
178+
return readDirectoryResults[dir] || [];
179+
};
180+
181+
compileSdkVersion = 29;
182+
183+
const androidToolsInfo = injector.resolve<IAndroidToolsInfo>("androidToolsInfo");
184+
androidToolsInfo.getToolsInfo = (config?: IProjectDir): IAndroidToolsInfoData => {
185+
return <any>{
186+
compileSdkVersion
187+
};
188+
};
189+
190+
});
191+
192+
describe("when new Android App_Resources structure is detected (post {N} 4.0 structure)", () => {
193+
const pathToSrcDirInAppResources = path.join(pathToAppResourcesAndroid, "src");
194+
beforeEach(() => {
195+
const androidResourcesMigrationService = injector.resolve<IAndroidResourcesMigrationService>("androidResourcesMigrationService");
196+
androidResourcesMigrationService.hasMigrated = () => true;
197+
});
198+
199+
it("copies everything from App_Resources/Android/src to correct location in platforms", async () => {
200+
await androidProjectService.prepareAppResources(projectData);
201+
202+
assert.deepEqual(copiedFiles, [{ sourceFileName: path.join(pathToSrcDirInAppResources, "*"), destinationFileName: path.join(projectData.projectDir, "platforms", "android", "app", "src") }]);
203+
});
204+
205+
it("deletes correct values-<sdk> directories based on the compileSdk", async () => {
206+
readDirectoryResults = {
207+
[`${pathToResDirInPlatforms}`]: [
208+
"values",
209+
"values-v21",
210+
"values-v26",
211+
"values-v27",
212+
"values-v28"
213+
]
214+
};
215+
216+
compileSdkVersion = 26;
217+
await androidProjectService.prepareAppResources(projectData);
218+
assert.deepEqual(deletedDirs, [
219+
valuesV27Path,
220+
valuesV28Path
221+
]);
222+
});
223+
224+
it("deletes drawable directories when they've been previously prepared", async () => {
225+
readDirectoryResults = {
226+
[path.join(pathToSrcDirInAppResources, "main", "res")]: [
227+
"drawable-hdpi",
228+
"drawable-ldpi",
229+
"values",
230+
"values-v21",
231+
"values-v29"
232+
],
233+
[`${pathToResDirInPlatforms}`]: [
234+
"drawable-hdpi",
235+
"drawable-ldpi",
236+
"drawable-mdpi",
237+
"values",
238+
"values-v21",
239+
"values-v29"
240+
]
241+
};
242+
243+
await androidProjectService.prepareAppResources(projectData);
244+
245+
// NOTE: Currently the drawable-mdpi directory is not deleted from prepared App_Resources as it does not exist in the currently prepared App_Resources
246+
// This is not correct behavior and it should be fixed in a later point.
247+
assert.deepEqual(deletedDirs, [
248+
drawableHdpiPath,
249+
drawableLdpiPath
250+
]);
251+
});
252+
});
253+
254+
describe("when old Android App_Resources structure is detected (post {N} 4.0 structure)", () => {
255+
beforeEach(() => {
256+
const androidResourcesMigrationService = injector.resolve<IAndroidResourcesMigrationService>("androidResourcesMigrationService");
257+
androidResourcesMigrationService.hasMigrated = () => false;
258+
});
259+
260+
it("copies everything from App_Resources/Android to correct location in platforms", async () => {
261+
await androidProjectService.prepareAppResources(projectData);
262+
263+
assert.deepEqual(copiedFiles, [{ sourceFileName: path.join(pathToAppResourcesAndroid, "*"), destinationFileName: pathToResDirInPlatforms }]);
264+
});
265+
266+
it("deletes correct values-<sdk> directories based on the compileSdk", async () => {
267+
readDirectoryResults = {
268+
[`${pathToResDirInPlatforms}`]: [
269+
"values",
270+
"values-v21",
271+
"values-v26",
272+
"values-v27",
273+
"values-v28"
274+
]
275+
};
276+
277+
compileSdkVersion = 26;
278+
279+
await androidProjectService.prepareAppResources(projectData);
280+
281+
// During preparation of old App_Resources, CLI copies all of them in platforms and after that deletes the libs directory.
282+
assert.deepEqual(deletedDirs, [
283+
libsPath,
284+
valuesV27Path,
285+
valuesV28Path
286+
]);
287+
});
288+
289+
it("deletes drawable directories when they've been previously prepared", async () => {
290+
readDirectoryResults = {
291+
[`${pathToAppResourcesAndroid}`]: [
292+
"drawable-hdpi",
293+
"drawable-ldpi",
294+
"values",
295+
"values-v21",
296+
"values-v29"
297+
],
298+
[`${pathToResDirInPlatforms}`]: [
299+
"drawable-hdpi",
300+
"drawable-ldpi",
301+
"drawable-mdpi",
302+
"values",
303+
"values-v21",
304+
"values-v29"
305+
]
306+
};
307+
308+
await androidProjectService.prepareAppResources(projectData);
309+
// NOTE: Currently the drawable-mdpi directory is not deleted from prepared App_Resources as it does not exist in the currently prepared App_Resources
310+
// This is not correct behavior and it should be fixed in a later point.
311+
// During preparation of old App_Resources, CLI copies all of them in platforms and after that deletes the libs directory.
312+
assert.deepEqual(deletedDirs, [
313+
drawableHdpiPath,
314+
drawableLdpiPath,
315+
libsPath
316+
]);
317+
});
318+
});
319+
});
141320
});

0 commit comments

Comments
 (0)