"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.AndroidProjectService = void 0;
const path = require("path");
const shell = require("shelljs");
const _ = require("lodash");
const constants = require("../constants");
const semver = require("semver");
const projectServiceBaseLib = require("./platform-project-service-base");
const device_android_debug_bridge_1 = require("../common/mobile/android/device-android-debug-bridge");
const constants_1 = require("../common/constants");
const helpers_1 = require("../common/helpers");
const decorators_1 = require(".././common/decorators");
const yok_1 = require("../common/yok");
//
// we sort the native dependencies topologically to make sure they are processed in the right order
// native dependenciess need to be sorted so the deepst dependencies are built before it's parents
//
// for example, given this dep structure (assuming these are all native dependencies that need to be built)
// (note: we list all dependencies at the root level, so the leaf nodes are essentially references to the root nodes)
//
//   |- dep1
//   |- dep2
//   |- |- dep3
//   |- |- dep4
//   |- |- |- dep5
//   |- dep3
//   |- dep4
//   |- |- dep5
//   |- dep5
//
// It is sorted:
//
//   |- dep1
//   |- dep3
//   |- dep5
//   |- dep4        # depends on dep5, so dep5 must be built first, ie above ^
//   |- |- dep5
//   |- dep2        # depends on dep3, dep4 (and dep5 through dep4) so all of them must be built first before dep2 is built
//   |- |- dep3
//   |- |- dep4
//   |- |- |- dep5
//
// for more details see: https://wikiless.org/wiki/Topological_sorting?lang=en
//
function topologicalSortNativeDependencies(dependencies, start = [], depth = 0, total = 0 // do not pass in, we calculate it in the initial run!
) {
    // we set the total on the initial call - and never increment it, as it's used for esacaping the recursion
    if (total === 0) {
        total = dependencies.length;
    }
    const sortedDeps = dependencies.reduce((sortedDeps, currentDependency) => {
        const allSubDependenciesProcessed = currentDependency.dependencies.every((subDependency) => {
            return sortedDeps.some((dep) => dep.name === subDependency);
        });
        if (allSubDependenciesProcessed) {
            sortedDeps.push(currentDependency);
        }
        return sortedDeps;
    }, start);
    const remainingDeps = dependencies.filter((nativeDep) => !sortedDeps.includes(nativeDep));
    // recurse if we still have remaining deps
    // the second condition here prevents infinite recursion
    if (remainingDeps.length && sortedDeps.length < total) {
        return topologicalSortNativeDependencies(remainingDeps, sortedDeps, depth + 1, total);
    }
    return sortedDeps;
}
class AndroidProjectService extends projectServiceBaseLib.PlatformProjectServiceBase {
    constructor($androidToolsInfo, $errors, $fs, $logger, $projectDataService, $options, $injector, $devicePlatformsConstants, $androidPluginBuildService, $platformEnvironmentRequirements, $androidResourcesMigrationService, $filesHashService, $gradleCommandService, $gradleBuildService, $analyticsService) {
        super($fs, $projectDataService);
        this.$androidToolsInfo = $androidToolsInfo;
        this.$errors = $errors;
        this.$logger = $logger;
        this.$options = $options;
        this.$injector = $injector;
        this.$devicePlatformsConstants = $devicePlatformsConstants;
        this.$androidPluginBuildService = $androidPluginBuildService;
        this.$platformEnvironmentRequirements = $platformEnvironmentRequirements;
        this.$androidResourcesMigrationService = $androidResourcesMigrationService;
        this.$filesHashService = $filesHashService;
        this.$gradleCommandService = $gradleCommandService;
        this.$gradleBuildService = $gradleBuildService;
        this.$analyticsService = $analyticsService;
        this._platformData = null;
    }
    getPlatformData(projectData) {
        if (!projectData && !this._platformData) {
            throw new Error("First call of getPlatformData without providing projectData.");
        }
        if (projectData && projectData.platformsDir) {
            const projectRoot = this.$options.hostProjectPath
                ? this.$options.hostProjectPath
                : path.join(projectData.platformsDir, AndroidProjectService.ANDROID_PLATFORM_NAME);
            const appDestinationDirectoryArr = [
                projectRoot,
                this.$options.hostProjectModuleName,
                constants.SRC_DIR,
                constants.MAIN_DIR,
                constants.ASSETS_DIR,
            ];
            const configurationsDirectoryArr = [
                projectRoot,
                this.$options.hostProjectModuleName,
                constants.SRC_DIR,
                constants.MAIN_DIR,
                constants.MANIFEST_FILE_NAME,
            ];
            const deviceBuildOutputArr = [
                projectRoot,
                this.$options.hostProjectModuleName,
                constants.BUILD_DIR,
                constants.OUTPUTS_DIR,
                constants.APK_DIR,
            ];
            const packageName = this.getProjectNameFromId(projectData);
            const runtimePackage = this.$projectDataService.getRuntimePackage(projectData.projectDir, "android" /* constants.PlatformTypes.android */);
            this._platformData = {
                frameworkPackageName: runtimePackage.name,
                normalizedPlatformName: "Android",
                platformNameLowerCase: "android",
                appDestinationDirectoryPath: path.join(...appDestinationDirectoryArr),
                platformProjectService: this,
                projectRoot: projectRoot,
                getBuildOutputPath: (buildOptions) => {
                    if (buildOptions.androidBundle) {
                        return path.join(projectRoot, this.$options.hostProjectModuleName, constants.BUILD_DIR, constants.OUTPUTS_DIR, constants.BUNDLE_DIR);
                    }
                    return path.join(...deviceBuildOutputArr);
                },
                getValidBuildOutputData: (buildOptions) => {
                    const buildMode = buildOptions.release
                        ? constants_1.Configurations.Release.toLowerCase()
                        : constants_1.Configurations.Debug.toLowerCase();
                    if (buildOptions.androidBundle) {
                        return {
                            packageNames: [
                                `${this.$options.hostProjectModuleName}${constants.AAB_EXTENSION_NAME}`,
                                `${this.$options.hostProjectModuleName}-${buildMode}${constants.AAB_EXTENSION_NAME}`,
                            ],
                        };
                    }
                    return {
                        packageNames: [
                            `${packageName}-${buildMode}${constants.APK_EXTENSION_NAME}`,
                            `${projectData.projectName}-${buildMode}${constants.APK_EXTENSION_NAME}`,
                            `${projectData.projectName}${constants.APK_EXTENSION_NAME}`,
                            `${this.$options.hostProjectModuleName}-${buildMode}${constants.APK_EXTENSION_NAME}`,
                        ],
                        regexes: [
                            new RegExp(`(${packageName}|${this.$options.hostProjectModuleName})-.*-(${constants_1.Configurations.Debug}|${constants_1.Configurations.Release})(-unsigned)?${constants.APK_EXTENSION_NAME}`, "i"),
                        ],
                    };
                },
                configurationFileName: constants.MANIFEST_FILE_NAME,
                configurationFilePath: path.join(...configurationsDirectoryArr),
                relativeToFrameworkConfigurationFilePath: path.join(constants.SRC_DIR, constants.MAIN_DIR, constants.MANIFEST_FILE_NAME),
                fastLivesyncFileExtensions: [".jpg", ".gif", ".png", ".bmp", ".webp"], // http://developer.android.com/guide/appendix/media-formats.html
            };
        }
        return this._platformData;
    }
    getCurrentPlatformVersion(platformData, projectData) {
        const currentPlatformData = this.$projectDataService.getRuntimePackage(projectData.projectDir, platformData.platformNameLowerCase);
        return currentPlatformData && currentPlatformData[constants.VERSION_STRING];
    }
    validateOptions() {
        return __awaiter(this, void 0, void 0, function* () {
            return true;
        });
    }
    getAppResourcesDestinationDirectoryPath(projectData) {
        const appResourcesDirStructureHasMigrated = this.$androidResourcesMigrationService.hasMigrated(projectData.getAppResourcesDirectoryPath());
        if (appResourcesDirStructureHasMigrated) {
            return this.getUpdatedAppResourcesDestinationDirPath(projectData);
        }
        else {
            return this.getLegacyAppResourcesDestinationDirPath(projectData);
        }
    }
    validate(projectData, options, notConfiguredEnvOptions) {
        return __awaiter(this, void 0, void 0, function* () {
            this.validatePackageName(projectData.projectIdentifiers.android);
            this.validateProjectName(projectData.projectName);
            const checkEnvironmentRequirementsOutput = yield this.$platformEnvironmentRequirements.checkEnvironmentRequirements({
                platform: this.getPlatformData(projectData).normalizedPlatformName,
                projectDir: projectData.projectDir,
                options,
                notConfiguredEnvOptions,
            });
            this.$androidToolsInfo.validateInfo({
                showWarningsAsErrors: true,
                projectDir: projectData.projectDir,
                validateTargetSdk: true,
            });
            return {
                checkEnvironmentRequirementsOutput,
            };
        });
    }
    createProject(frameworkDir, frameworkVersion, projectData) {
        return __awaiter(this, void 0, void 0, function* () {
            if (semver.lt(frameworkVersion, AndroidProjectService.MIN_RUNTIME_VERSION_WITH_GRADLE)) {
                this.$errors.fail(`The NativeScript CLI requires Android runtime ${AndroidProjectService.MIN_RUNTIME_VERSION_WITH_GRADLE} or later to work properly.`);
            }
            this.$fs.ensureDirectoryExists(this.getPlatformData(projectData).projectRoot);
            const androidToolsInfo = this.$androidToolsInfo.getToolsInfo({
                projectDir: projectData.projectDir,
            });
            const targetSdkVersion = androidToolsInfo && androidToolsInfo.targetSdkVersion;
            this.$logger.trace(`Using Android SDK '${targetSdkVersion}'.`);
            this.copy(this.getPlatformData(projectData).projectRoot, frameworkDir, "*", "-R");
            // TODO: Check if we actually need this and if it should be targetSdk or compileSdk
            this.cleanResValues(targetSdkVersion, projectData);
        });
    }
    getResDestinationDir(projectData) {
        const appResourcesDirStructureHasMigrated = this.$androidResourcesMigrationService.hasMigrated(projectData.getAppResourcesDirectoryPath());
        if (appResourcesDirStructureHasMigrated) {
            const appResourcesDestinationPath = this.getUpdatedAppResourcesDestinationDirPath(projectData);
            return path.join(appResourcesDestinationPath, constants.MAIN_DIR, constants.RESOURCES_DIR);
        }
        else {
            return this.getLegacyAppResourcesDestinationDirPath(projectData);
        }
    }
    cleanResValues(targetSdkVersion, projectData) {
        const resDestinationDir = this.getResDestinationDir(projectData);
        const directoriesInResFolder = this.$fs.readDirectory(resDestinationDir);
        const directoriesToClean = directoriesInResFolder
            .map((dir) => {
            return {
                dirName: dir,
                sdkNum: parseInt(dir.substr(AndroidProjectService.VALUES_VERSION_DIRNAME_PREFIX.length)),
            };
        })
            .filter((dir) => dir.dirName.match(AndroidProjectService.VALUES_VERSION_DIRNAME_PREFIX) &&
            dir.sdkNum &&
            (!targetSdkVersion || targetSdkVersion < dir.sdkNum))
            .map((dir) => path.join(resDestinationDir, dir.dirName));
        this.$logger.trace("Directories to clean:");
        this.$logger.trace(directoriesToClean);
        _.map(directoriesToClean, (dir) => this.$fs.deleteDirectory(dir));
    }
    interpolateData(projectData) {
        return __awaiter(this, void 0, void 0, function* () {
            // Interpolate the apilevel and package
            this.interpolateConfigurationFile(projectData);
            const appResourcesDirectoryPath = projectData.getAppResourcesDirectoryPath();
            let stringsFilePath;
            const appResourcesDestinationDirectoryPath = this.getAppResourcesDestinationDirectoryPath(projectData);
            if (this.$androidResourcesMigrationService.hasMigrated(appResourcesDirectoryPath)) {
                stringsFilePath = path.join(appResourcesDestinationDirectoryPath, constants.MAIN_DIR, constants.RESOURCES_DIR, "values", "strings.xml");
            }
            else {
                stringsFilePath = path.join(appResourcesDestinationDirectoryPath, "values", "strings.xml");
            }
            shell.sed("-i", /__NAME__/, projectData.projectName, stringsFilePath);
            shell.sed("-i", /__TITLE_ACTIVITY__/, projectData.projectName, stringsFilePath);
            const gradleSettingsFilePath = path.join(this.getPlatformData(projectData).projectRoot, "settings.gradle");
            shell.sed("-i", /__PROJECT_NAME__/, this.getProjectNameFromId(projectData), gradleSettingsFilePath);
            try {
                // will replace applicationId in app/App_Resources/Android/app.gradle if it has not been edited by the user
                const appGradleContent = this.$fs.readText(projectData.appGradlePath);
                if (appGradleContent.indexOf(constants.PACKAGE_PLACEHOLDER_NAME) !== -1) {
                    //TODO: For compatibility with old templates. Once all templates are updated should delete.
                    shell.sed("-i", new RegExp(constants.PACKAGE_PLACEHOLDER_NAME), projectData.projectIdentifiers.android, projectData.appGradlePath);
                }
            }
            catch (e) {
                this.$logger.trace(`Templates updated and no need for replace in app.gradle.`);
            }
        });
    }
    interpolateConfigurationFile(projectData) {
        const manifestPath = this.getPlatformData(projectData).configurationFilePath;
        shell.sed("-i", /__PACKAGE__/, projectData.projectIdentifiers.android, manifestPath);
    }
    getProjectNameFromId(projectData) {
        let id;
        if (projectData &&
            projectData.projectIdentifiers &&
            projectData.projectIdentifiers.android) {
            const idParts = projectData.projectIdentifiers.android.split(".");
            id = idParts[idParts.length - 1];
        }
        return id;
    }
    afterCreateProject(projectRoot) {
        return null;
    }
    updatePlatform(currentVersion, newVersion, canUpdate, projectData, addPlatform, removePlatforms) {
        return __awaiter(this, void 0, void 0, function* () {
            if (semver.eq(newVersion, AndroidProjectService.MIN_RUNTIME_VERSION_WITH_GRADLE)) {
                const platformLowercase = this.getPlatformData(projectData).normalizedPlatformName.toLowerCase();
                yield removePlatforms([platformLowercase.split("@")[0]]);
                yield addPlatform(platformLowercase);
                return false;
            }
            return true;
        });
    }
    buildProject(projectRoot, projectData, buildData) {
        return __awaiter(this, void 0, void 0, function* () {
            const platformData = this.getPlatformData(projectData);
            yield this.$gradleBuildService.buildProject(platformData.projectRoot, buildData);
            const outputPath = platformData.getBuildOutputPath(buildData);
            yield this.$filesHashService.saveHashesForProject(this._platformData, outputPath);
            yield this.trackKotlinUsage(projectRoot);
        });
    }
    buildForDeploy(projectRoot, projectData, buildData) {
        return __awaiter(this, void 0, void 0, function* () {
            return this.buildProject(projectRoot, projectData, buildData);
        });
    }
    isPlatformPrepared(projectRoot, projectData) {
        return this.$fs.exists(path.join(this.getPlatformData(projectData).appDestinationDirectoryPath, this.$options.hostProjectModuleName));
    }
    getFrameworkFilesExtensions() {
        return [".jar", ".dat"];
    }
    prepareProject() {
        return __awaiter(this, void 0, void 0, function* () {
            // Intentionally left empty.
        });
    }
    ensureConfigurationFileInAppResources(projectData) {
        const appResourcesDirectoryPath = projectData.appResourcesDirectoryPath;
        const appResourcesDirStructureHasMigrated = this.$androidResourcesMigrationService.hasMigrated(appResourcesDirectoryPath);
        let originalAndroidManifestFilePath;
        if (appResourcesDirStructureHasMigrated) {
            originalAndroidManifestFilePath = path.join(appResourcesDirectoryPath, this.$devicePlatformsConstants.Android, "src", "main", this.getPlatformData(projectData).configurationFileName);
        }
        else {
            originalAndroidManifestFilePath = path.join(appResourcesDirectoryPath, this.$devicePlatformsConstants.Android, this.getPlatformData(projectData).configurationFileName);
        }
        const manifestExists = this.$fs.exists(originalAndroidManifestFilePath);
        if (!manifestExists) {
            this.$logger.warn("No manifest found in " + originalAndroidManifestFilePath);
            return;
        }
        // Overwrite the AndroidManifest from runtime.
        if (!appResourcesDirStructureHasMigrated) {
            this.$fs.copyFile(originalAndroidManifestFilePath, this.getPlatformData(projectData).configurationFilePath);
        }
    }
    prepareAppResources(projectData) {
        const platformData = this.getPlatformData(projectData);
        const projectAppResourcesPath = projectData.getAppResourcesDirectoryPath(projectData.projectDir);
        const platformsAppResourcesPath = this.getAppResourcesDestinationDirectoryPath(projectData);
        this.cleanUpPreparedResources(projectData);
        this.$fs.ensureDirectoryExists(platformsAppResourcesPath);
        const appResourcesDirStructureHasMigrated = this.$androidResourcesMigrationService.hasMigrated(projectAppResourcesPath);
        if (appResourcesDirStructureHasMigrated) {
            this.$fs.copyFile(path.join(projectAppResourcesPath, platformData.normalizedPlatformName, constants.SRC_DIR, "*"), platformsAppResourcesPath);
        }
        else {
            this.$fs.copyFile(path.join(projectAppResourcesPath, platformData.normalizedPlatformName, "*"), platformsAppResourcesPath);
            // https://github.com/NativeScript/android-runtime/issues/899
            // App_Resources/Android/libs is reserved to user's aars and jars, but they should not be copied as resources
            this.$fs.deleteDirectory(path.join(platformsAppResourcesPath, "libs"));
        }
        const androidToolsInfo = this.$androidToolsInfo.getToolsInfo({
            projectDir: projectData.projectDir,
        });
        const compileSdkVersion = androidToolsInfo && androidToolsInfo.compileSdkVersion;
        this.cleanResValues(compileSdkVersion, projectData);
    }
    preparePluginNativeCode(pluginData, projectData) {
        return __awaiter(this, void 0, void 0, function* () {
            // build Android plugins which contain AndroidManifest.xml and/or resources
            const pluginPlatformsFolderPath = this.getPluginPlatformsFolderPath(pluginData, AndroidProjectService.ANDROID_PLATFORM_NAME);
            if (this.$fs.exists(pluginPlatformsFolderPath)) {
                const options = {
                    gradlePath: this.$options.gradlePath,
                    gradleArgs: this.$options.gradleArgs,
                    projectDir: projectData.projectDir,
                    pluginName: pluginData.name,
                    platformsAndroidDirPath: pluginPlatformsFolderPath,
                    aarOutputDir: pluginPlatformsFolderPath,
                    tempPluginDirPath: path.join(projectData.platformsDir, "tempPlugin"),
                };
                if (yield this.$androidPluginBuildService.buildAar(options)) {
                    this.$logger.info(`Built aar for ${options.pluginName}`);
                }
                this.$androidPluginBuildService.migrateIncludeGradle(options);
            }
        });
    }
    processConfigurationFilesFromAppResources() {
        return __awaiter(this, void 0, void 0, function* () {
            return;
        });
    }
    removePluginNativeCode(pluginData, projectData) {
        return __awaiter(this, void 0, void 0, function* () {
            // not implemented
        });
    }
    beforePrepareAllPlugins(projectData, dependencies) {
        return __awaiter(this, void 0, void 0, function* () {
            if (dependencies) {
                dependencies = this.filterUniqueDependencies(dependencies);
                return this.provideDependenciesJson(projectData, dependencies);
            }
        });
    }
    handleNativeDependenciesChange(projectData, opts) {
        return __awaiter(this, void 0, void 0, function* () {
            return;
        });
    }
    filterUniqueDependencies(dependencies) {
        const depsDictionary = dependencies.reduce((dict, dep) => {
            const collision = dict[dep.name];
            // in case there are multiple dependencies to the same module, the one declared in the package.json takes precedence
            if (!collision || collision.depth > dep.depth) {
                dict[dep.name] = dep;
            }
            return dict;
        }, {});
        return _.values(depsDictionary);
    }
    provideDependenciesJson(projectData, dependencies) {
        const platformDir = this.$options.hostProjectPath
            ? this.$options.hostProjectPath
            : path.join(projectData.platformsDir, AndroidProjectService.ANDROID_PLATFORM_NAME);
        const dependenciesJsonPath = path.join(platformDir, constants.DEPENDENCIES_JSON_NAME);
        let nativeDependencyData = dependencies.filter(AndroidProjectService.isNativeAndroidDependency);
        let nativeDependencies = nativeDependencyData.map(({ name, directory, dependencies }) => {
            return {
                name,
                directory: path.relative(platformDir, directory),
                dependencies: dependencies.filter((dep) => {
                    // filter out transient dependencies that don't have native dependencies
                    return (nativeDependencyData.findIndex((nativeDep) => nativeDep.name === dep) !== -1);
                }),
            };
        });
        nativeDependencies = topologicalSortNativeDependencies(nativeDependencies);
        const jsonContent = JSON.stringify(nativeDependencies, null, 4);
        this.$fs.writeFile(dependenciesJsonPath, jsonContent);
        // we sort all the dependencies to respect the topological sorting of the native dependencies
        return dependencies.sort(function (a, b) {
            return (nativeDependencies.findIndex((n) => n.name === a.name) -
                nativeDependencies.findIndex((n) => n.name === b.name));
        });
    }
    static isNativeAndroidDependency({ nativescript, }) {
        return (nativescript &&
            (nativescript.android ||
                (nativescript.platforms && nativescript.platforms.android)));
    }
    stopServices(projectRoot) {
        return __awaiter(this, void 0, void 0, function* () {
            const result = yield this.$gradleCommandService.executeCommand(["--stop", "--quiet"], {
                cwd: projectRoot,
                message: "Gradle stop services...",
                stdio: "pipe",
            });
            return result;
        });
    }
    cleanProject(projectRoot) {
        return __awaiter(this, void 0, void 0, function* () {
            yield this.$gradleBuildService.cleanProject(projectRoot, {
                release: false,
            });
        });
    }
    cleanDeviceTempFolder(deviceIdentifier, projectData) {
        return __awaiter(this, void 0, void 0, function* () {
            const adb = this.$injector.resolve(device_android_debug_bridge_1.DeviceAndroidDebugBridge, {
                identifier: deviceIdentifier,
            });
            const deviceRootPath = `${constants_1.LiveSyncPaths.ANDROID_TMP_DIR_NAME}/${projectData.projectIdentifiers.android}`;
            yield adb.executeShellCommand(["rm", "-rf", deviceRootPath]);
        });
    }
    checkForChanges() {
        return __awaiter(this, void 0, void 0, function* () {
            // Nothing android specific to check yet.
        });
    }
    getDeploymentTarget(projectData) {
        return;
    }
    copy(projectRoot, frameworkDir, files, cpArg) {
        const paths = files.split(" ").map((p) => path.join(frameworkDir, p));
        shell.cp(cpArg, paths, projectRoot);
    }
    validatePackageName(packageName) {
        //Make the package conform to Java package types
        //Enforce underscore limitation
        if (!/^[a-zA-Z]+(\.[a-zA-Z0-9][a-zA-Z0-9_]*)+$/.test(packageName)) {
            this.$errors.fail(`Package name must look like: com.company.Name. Got: ${packageName}`);
        }
        //Class is a reserved word
        if (/\b[Cc]lass\b/.test(packageName)) {
            this.$errors.fail("class is a reserved word");
        }
    }
    validateProjectName(projectName) {
        if (projectName === "") {
            this.$errors.fail("Project name cannot be empty");
        }
        //Classes in Java don't begin with numbers
        if (/^[0-9]/.test(projectName)) {
            this.$errors.fail("Project name must not begin with a number");
        }
    }
    getLegacyAppResourcesDestinationDirPath(projectData) {
        const resourcePath = [
            this.$options.hostProjectModuleName,
            constants.SRC_DIR,
            constants.MAIN_DIR,
            constants.RESOURCES_DIR,
        ];
        return path.join(this.getPlatformData(projectData).projectRoot, ...resourcePath);
    }
    getUpdatedAppResourcesDestinationDirPath(projectData) {
        const resourcePath = [
            this.$options.hostProjectModuleName,
            constants.SRC_DIR,
        ];
        return path.join(this.getPlatformData(projectData).projectRoot, ...resourcePath);
    }
    /**
     * The purpose of this method is to delete the previously prepared user resources.
     * The content of the `<platforms>/android/.../res` directory is based on user's resources and gradle project template from android-runtime.
     * During preparation of the `<path to user's App_Resources>/Android` we want to clean all the users files from previous preparation,
     * but keep the ones that were introduced during `platform add` of the android-runtime.
     * Currently the Gradle project template contains resources only in values and values-v21 directories.
     * So the current logic of the method is cleaning al resources from `<platforms>/android/.../res` that are not in `values.*` directories
     * and that exist in the `<path to user's App_Resources>/Android/.../res` directory
     * This means that if user has a resource file in values-v29 for example, builds the project and then deletes this resource,
     * it will be kept in platforms directory. Reference issue: `https://github.com/NativeScript/nativescript-cli/issues/5083`
     * Same is valid for files in `drawable-<resolution>` directories - in case in user's resources there's drawable-hdpi directory,
     * which is deleted after the first build of app, it will remain in platforms directory.
     */
    cleanUpPreparedResources(projectData) {
        let resourcesDirPath = path.join(projectData.appResourcesDirectoryPath, this.getPlatformData(projectData).normalizedPlatformName);
        if (this.$androidResourcesMigrationService.hasMigrated(projectData.appResourcesDirectoryPath)) {
            resourcesDirPath = path.join(resourcesDirPath, constants.SRC_DIR, constants.MAIN_DIR, constants.RESOURCES_DIR);
        }
        const valuesDirRegExp = /^values/;
        if (this.$fs.exists(resourcesDirPath)) {
            const resourcesDirs = this.$fs
                .readDirectory(resourcesDirPath)
                .filter((resDir) => !resDir.match(valuesDirRegExp));
            const resDestinationDir = this.getResDestinationDir(projectData);
            _.each(resourcesDirs, (currentResource) => {
                this.$fs.deleteDirectory(path.join(resDestinationDir, currentResource));
            });
        }
    }
    trackKotlinUsage(projectRoot) {
        return __awaiter(this, void 0, void 0, function* () {
            const buildStatistics = this.tryGetAndroidBuildStatistics(projectRoot);
            try {
                if (buildStatistics && buildStatistics.kotlinUsage) {
                    const analyticsDelimiter = constants.AnalyticsEventLabelDelimiter;
                    const hasUseKotlinPropertyInAppData = `hasUseKotlinPropertyInApp${analyticsDelimiter}${buildStatistics.kotlinUsage.hasUseKotlinPropertyInApp}`;
                    const hasKotlinRuntimeClassesData = `hasKotlinRuntimeClasses${analyticsDelimiter}${buildStatistics.kotlinUsage.hasKotlinRuntimeClasses}`;
                    yield this.$analyticsService.trackEventActionInGoogleAnalytics({
                        action: "Using Kotlin" /* constants.TrackActionNames.UsingKotlin */,
                        additionalData: `${hasUseKotlinPropertyInAppData}${analyticsDelimiter}${hasKotlinRuntimeClassesData}`,
                    });
                }
            }
            catch (e) {
                this.$logger.trace(`Failed to track android build statistics. Error is: ${e.message}`);
            }
        });
    }
    tryGetAndroidBuildStatistics(projectRoot) {
        const staticsFilePath = path.join(projectRoot, constants.ANDROID_ANALYTICS_DATA_DIR, constants.ANDROID_ANALYTICS_DATA_FILE);
        let buildStatistics;
        if (this.$fs.exists(staticsFilePath)) {
            try {
                buildStatistics = this.$fs.readJson(staticsFilePath);
            }
            catch (e) {
                this.$logger.trace(`Unable to read android build statistics file. Error is ${e.message}`);
            }
        }
        return buildStatistics;
    }
}
AndroidProjectService.VALUES_DIRNAME = "values";
AndroidProjectService.VALUES_VERSION_DIRNAME_PREFIX = AndroidProjectService.VALUES_DIRNAME + "-v";
AndroidProjectService.ANDROID_PLATFORM_NAME = "android";
AndroidProjectService.MIN_RUNTIME_VERSION_WITH_GRADLE = "1.5.0";
__decorate([
    (0, decorators_1.performanceLog)(),
    (0, helpers_1.hook)("buildAndroid")
], AndroidProjectService.prototype, "buildProject", null);
exports.AndroidProjectService = AndroidProjectService;
yok_1.injector.register("androidProjectService", AndroidProjectService);
//# sourceMappingURL=android-project-service.js.map