UNPKG

nativescript

Version:

Command-line interface for building NativeScript projects

571 lines • 30.3 kB
"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; }; 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]; } async validateOptions() { return true; } getAppResourcesDestinationDirectoryPath(projectData) { const appResourcesDirStructureHasMigrated = this.$androidResourcesMigrationService.hasMigrated(projectData.getAppResourcesDirectoryPath()); if (appResourcesDirStructureHasMigrated) { return this.getUpdatedAppResourcesDestinationDirPath(projectData); } else { return this.getLegacyAppResourcesDestinationDirPath(projectData); } } async validate(projectData, options, notConfiguredEnvOptions) { this.validatePackageName(projectData.projectIdentifiers.android); this.validateProjectName(projectData.projectName); const checkEnvironmentRequirementsOutput = await 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, }; } async createProject(frameworkDir, frameworkVersion, projectData) { 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)); } async interpolateData(projectData) { // 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; } async updatePlatform(currentVersion, newVersion, canUpdate, projectData, addPlatform, removePlatforms) { if (semver.eq(newVersion, AndroidProjectService.MIN_RUNTIME_VERSION_WITH_GRADLE)) { const platformLowercase = this.getPlatformData(projectData).normalizedPlatformName.toLowerCase(); await removePlatforms([platformLowercase.split("@")[0]]); await addPlatform(platformLowercase); return false; } return true; } async buildProject(projectRoot, projectData, buildData) { const platformData = this.getPlatformData(projectData); await this.$gradleBuildService.buildProject(platformData.projectRoot, buildData); const outputPath = platformData.getBuildOutputPath(buildData); await this.$filesHashService.saveHashesForProject(this._platformData, outputPath); await this.trackKotlinUsage(projectRoot); } async buildForDeploy(projectRoot, projectData, buildData) { 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"]; } async prepareProject() { // 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); } async preparePluginNativeCode(pluginData, projectData) { // 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 (await this.$androidPluginBuildService.buildAar(options)) { this.$logger.info(`Built aar for ${options.pluginName}`); } this.$androidPluginBuildService.migrateIncludeGradle(options); } } async processConfigurationFilesFromAppResources() { return; } async removePluginNativeCode(pluginData, projectData) { // not implemented } async beforePrepareAllPlugins(projectData, dependencies) { if (dependencies) { dependencies = this.filterUniqueDependencies(dependencies); return this.provideDependenciesJson(projectData, dependencies); } } async handleNativeDependenciesChange(projectData, opts) { 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))); } async stopServices(projectRoot) { const result = await this.$gradleCommandService.executeCommand(["--stop", "--quiet"], { cwd: projectRoot, message: "Gradle stop services...", stdio: "pipe", }); return result; } async cleanProject(projectRoot) { await this.$gradleBuildService.cleanProject(projectRoot, { release: false, }); } async cleanDeviceTempFolder(deviceIdentifier, projectData) { 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}`; await adb.executeShellCommand(["rm", "-rf", deviceRootPath]); } async checkForChanges() { // 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)); }); } } async trackKotlinUsage(projectRoot) { 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}`; await 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; } } exports.AndroidProjectService = AndroidProjectService; 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); yok_1.injector.register("androidProjectService", AndroidProjectService); //# sourceMappingURL=android-project-service.js.map