"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.AndroidProject = void 0; const path_1 = require("path"); const utils_fs_1 = require("@ionic/utils-fs"); const gradle_file_1 = require("./gradle-file"); const xml_1 = require("../xml"); const properties_1 = require("../properties"); const platform_project_1 = require("../platform-project"); const read_src_1 = require("../read-src"); const logger_1 = require("../logger"); class AndroidProject extends platform_project_1.PlatformProject { constructor(project) { super(project); this.buildGradle = null; this.appBuildGradle = null; const manifestPath = this.getAndroidManifestPath(); this.manifest = new xml_1.XmlFile(manifestPath, project.vfs); } async load() { try { await this.manifest.load(); this.buildGradle = await this.loadGradle('build.gradle'); this.appBuildGradle = await this.loadGradle('app/build.gradle'); } catch (e) { this.setError(e); } } getBuildGradle() { return this.buildGradle; } getAppBuildGradle() { return this.appBuildGradle; } getAndroidManifest() { return this.manifest; } /** * Get a project file container for the given path in the project root. * This will return an existing file container or create a new one. */ getProjectFile(path, create) { var _a; const root = (_a = this.project.config.android) === null || _a === void 0 ? void 0 : _a.path; if (!root) { return null; } const filename = (0, path_1.join)(root, path); const existing = this.project.vfs.get(filename); if (existing) { return existing.getData(); } return create(filename); } getResourceXmlFile(resourcePath) { return this.getXmlFile((0, path_1.join)(this.getResourcesPath(), resourcePath)); } getXmlFile(path) { return this.getProjectFile(path, (filename) => new xml_1.XmlFile(filename, this.project.vfs)); } getPropertiesFile(path) { return this.getProjectFile(path, (filename) => new properties_1.PropertiesFile(filename, this.project.vfs)); } async getGradleFile(path) { if (path === 'build.gradle') { return this.buildGradle; } else if (path === 'app/build.gradle') { return this.appBuildGradle; } return this.loadGradle(path); } async setAppName(appName) { const application = this.manifest.find('manifest/application'); if (!application) { logger_1.Logger.v('android', 'setAppName', `No node found in `); return; } const label = application[0].getAttribute('android:label'); logger_1.Logger.v('android', 'setAppName', `current app label is ${label}`); if (label) { if (label.indexOf('@string') === 0) { logger_1.Logger.v('android', 'setAppName', 'android:label pointing to strings.xml resource file. Reading values/strings.xml'); const stringsFile = await this.getResourceXmlFile('values/strings.xml'); if (!stringsFile) { logger_1.Logger.v('android', 'setAppName', 'Unable to load values/strings.xml resource file'); return; } await stringsFile.load(); const attr = label.replace('@string/', ''); // TODO: use the value specified in the @strings attribute logger_1.Logger.v('android', 'setAppName', `Updated values/strings.xml to ${appName}`); stringsFile.replaceFragment(`resources/string[@name="${attr}"]`, `${appName}`); } } else { logger_1.Logger.v('android', 'setAppName', `No android:label on node, setting value directly`); application[0].setAttribute('android:label', appName); } } /** * Update the Android package name. This renames the package in `AndroidManifest.xml`, * the `applicationId` in `app/build.gradle`, and renames the java * package for the `MainActivity.java` file. * * This action will mutate the project on disk! */ async setPackageName(packageName) { var _a, _b, _c, _d, _e, _f; const sourceDir = (0, path_1.join)(this.getAppRoot(), 'src', 'main', 'java'); let hadPackageAttr = false; let oldPackageName = await ((_a = this.manifest .getDocumentElement()) === null || _a === void 0 ? void 0 : _a.getAttribute('package')); if (!oldPackageName) { oldPackageName = await ((_b = this.appBuildGradle) === null || _b === void 0 ? void 0 : _b.getApplicationId()); } else { hadPackageAttr = true; } const oldPackageParts = (_c = oldPackageName === null || oldPackageName === void 0 ? void 0 : oldPackageName.split('.')) !== null && _c !== void 0 ? _c : []; logger_1.Logger.v('android', 'setPackageName', 'setting Android package name to', packageName, 'from', oldPackageName); if (packageName === oldPackageName) { return; } const existingPackage = (0, path_1.join)(sourceDir, ...oldPackageParts); if (!(await (0, utils_fs_1.pathExists)(existingPackage))) { throw new Error('Current Java package name and directory structure do not match the package attribute. Ensure these match before modifying the project package name'); } let activityName = '.MainActivity'; if (hadPackageAttr) { (_d = this.manifest.getDocumentElement()) === null || _d === void 0 ? void 0 : _d.setAttribute('package', packageName); activityName = `${packageName}.MainActivity`; } await ((_e = this.appBuildGradle) === null || _e === void 0 ? void 0 : _e.setApplicationId(packageName)); await ((_f = this.appBuildGradle) === null || _f === void 0 ? void 0 : _f.setNamespace(packageName)); logger_1.Logger.v('android', 'setPackageName', `set manifest package attribute and applicationId to ${packageName}`); this.manifest.setAttrs('manifest/application/activity', { 'android:name': activityName }); logger_1.Logger.v('android', 'setPackageName', `set { var _a, _b; return (_b = (_a = this.buildGradle) === null || _a === void 0 ? void 0 : _a.getSource(f.node)) !== null && _b !== void 0 ? _b : ''; }); const gradleLine = sources.find(s => s.indexOf('com.android.tools.build:gradle:')); return (_d = (_c = gradleLine === null || gradleLine === void 0 ? void 0 : gradleLine.match(/:([\d.]+)/)) === null || _c === void 0 ? void 0 : _c[1]) !== null && _d !== void 0 ? _d : null; } async getPackageName() { var _a, _b; const namespace = await ((_a = this.appBuildGradle) === null || _a === void 0 ? void 0 : _a.getNamespace()); if (namespace) { return namespace; } return (_b = this.manifest.getDocumentElement()) === null || _b === void 0 ? void 0 : _b.getAttribute('package'); } setVersionCode(versionCode) { var _a; if (versionCode === '') { versionCode = 1; } return (_a = this.appBuildGradle) === null || _a === void 0 ? void 0 : _a.setVersionCode(typeof versionCode === 'number' ? versionCode : parseInt(versionCode, 10)); } async getVersionCode() { var _a, _b; return (_b = (await ((_a = this.appBuildGradle) === null || _a === void 0 ? void 0 : _a.getVersionCode()))) !== null && _b !== void 0 ? _b : null; } incrementVersionCode() { var _a, _b; return (_b = (_a = this.appBuildGradle) === null || _a === void 0 ? void 0 : _a.incrementVersionCode()) !== null && _b !== void 0 ? _b : Promise.resolve(); } setVersionName(versionName) { var _a; return (_a = this.appBuildGradle) === null || _a === void 0 ? void 0 : _a.setVersionName(versionName); } getVersionName() { var _a, _b; return (_b = (_a = this.appBuildGradle) === null || _a === void 0 ? void 0 : _a.getVersionName()) !== null && _b !== void 0 ? _b : Promise.resolve(null); } setVersionNameSuffix(versionNameSuffix) { var _a; return (_a = this.appBuildGradle) === null || _a === void 0 ? void 0 : _a.setVersionNameSuffix(versionNameSuffix); } getVersionNameSuffix() { var _a, _b; return (_b = (_a = this.appBuildGradle) === null || _a === void 0 ? void 0 : _a.getVersionNameSuffix()) !== null && _b !== void 0 ? _b : Promise.resolve(null); } /** * Add a new file to the given resources directory with the given contents and * given file name **/ getResource(resDir, file, options = { encoding: 'utf-8' }) { const root = this.getResourcesRoot(); if (!root) { return; } const dir = (0, path_1.join)(root, resDir); if (!options) { return (0, utils_fs_1.readFile)((0, path_1.join)(dir, file)); } return (0, utils_fs_1.readFile)((0, path_1.join)(dir, file), options); } /** * Add a new file to the given resources directory with the given contents and * given file name **/ async addResource(resDir, file, contents) { const root = this.getResourcesRoot(); if (!root) { return; } const dir = (0, path_1.join)(root, resDir); logger_1.Logger.v(`android`, 'addResource', `add res file ${file} to ${resDir}`); if (!(await (0, utils_fs_1.pathExists)(dir))) { await (0, utils_fs_1.mkdir)(dir); } return (0, utils_fs_1.writeFile)((0, path_1.join)(dir, file), contents); } async copyFile(src, dest) { var _a, _b, _c; if (!((_c = (_b = (_a = this.project) === null || _a === void 0 ? void 0 : _a.config) === null || _b === void 0 ? void 0 : _b.android) === null || _c === void 0 ? void 0 : _c.path)) { return Promise.reject(); } const destPath = (0, path_1.join)(this.project.config.android.path, dest); logger_1.Logger.v(`android`, `copyFile`, `copying ${src} to ${destPath}`); if (/^(https?:\/\/)/.test(src)) { const res = await fetch(src); return (0, utils_fs_1.writeFile)(destPath, Buffer.from(await res.arrayBuffer())); } const srcPath = (0, path_1.join)(this.project.config.android.path, src); return (0, utils_fs_1.copy)(srcPath, destPath); } /** * Copy the given source into the given resources directory with the * given file name **/ async copyToResources(resDir, file, source) { const root = this.getResourcesRoot(); if (!root) { return; } const dir = (0, path_1.join)(root, resDir); if (!(await (0, utils_fs_1.pathExists)(dir))) { await (0, utils_fs_1.mkdir)(dir); } logger_1.Logger.v(`android`, `copyToResources`, `copying ${file} to Android resources at ${(0, path_1.join)(dir, file)}`); const sourceData = await (0, read_src_1.readSource)(source); return (0, utils_fs_1.writeFile)((0, path_1.join)(dir, file), sourceData); } getAndroidManifestPath() { var _a, _b; if (!((_a = this.project.config.android) === null || _a === void 0 ? void 0 : _a.path)) { return null; } return (0, path_1.join)((_b = this.project.config.android) === null || _b === void 0 ? void 0 : _b.path, 'app', 'src', 'main', 'AndroidManifest.xml'); } getResourcesPath() { return (0, path_1.join)('app', 'src', 'main', 'res'); } getResourcesRoot() { var _a, _b; if (!((_a = this.project.config.android) === null || _a === void 0 ? void 0 : _a.path)) { return null; } return (0, path_1.join)((_b = this.project.config.android) === null || _b === void 0 ? void 0 : _b.path, this.getResourcesPath()); } getAppRoot() { var _a, _b; if (!((_a = this.project.config.android) === null || _a === void 0 ? void 0 : _a.path)) { return null; } // TODO: Don't hard-code app return (0, path_1.join)((_b = this.project.config.android) === null || _b === void 0 ? void 0 : _b.path, 'app'); } async loadGradle(path) { var _a, _b; if (!((_a = this.project.config.android) === null || _a === void 0 ? void 0 : _a.path)) { return null; } const filename = (0, path_1.join)((_b = this.project.config.android) === null || _b === void 0 ? void 0 : _b.path, path); return new gradle_file_1.GradleFile(filename, this.project.vfs); } } exports.AndroidProject = AndroidProject; //# sourceMappingURL=project.js.map