UNPKG

pwabuilder-android

Version:
203 lines (180 loc) 8.38 kB
'use strict'; var path = require('path'), util = require('util'), Q = require('q'), url = require('url'), colorNames = require('colornames'), DOMParser = require('xmldom').DOMParser; var pwabuilderLib = require('pwabuilder-lib'); var CustomError = pwabuilderLib.CustomError, PlatformBase = pwabuilderLib.PlatformBase, manifestTools = pwabuilderLib.manifestTools, fileTools = pwabuilderLib.fileTools; var constants = require('./constants'); var iconSizeMap = { '48x48': { density: 'mdpi', type: 'launcher' }, '72x72': { density: 'hdpi', type: 'launcher' }, '96x96': { density: 'xhdpi', type: 'launcher' }, '144x144': { density: 'xxhdpi', type: 'launcher' }, '192x192': { density: 'xxxhdpi', type: 'launcher' }, '320x480': { density: 'mdpi', type: 'splash' }, '480x800': { density: 'hdpi', type: 'splash' }, '720x1280': { density: 'xhdpi', type: 'splash' }, '768x1280': { density: 'xhdpi', type: 'splash' }, '1080x1920': { density: 'xxhdpi', type: 'splash' }, '1440x2560': { density: 'xxxhdpi', type: 'splash' } }; function Platform (packageName, platforms) { var self = this; PlatformBase.call(this, constants.platform.id, constants.platform.name, packageName, __dirname); // save platform list self.platforms = platforms; // override create function self.create = function (w3cManifestInfo, rootDir, options, href, callback) {Q if (w3cManifestInfo.format !== pwabuilderLib.constants.BASE_MANIFEST_FORMAT) { return Q.reject(new CustomError('The \'' + w3cManifestInfo.format + '\' manifest format is not valid for this platform.')); } self.info('Generating the ' + constants.platform.name + ' app...'); var platformDir = self.getOutputFolder(rootDir); var sourceDir = path.join(platformDir, 'source'); var imagesDir = path.join(platformDir, 'images'); var appResDir = path.join(sourceDir, 'app', 'src', 'main', 'res'); var imagesDir = path.join(appResDir, 'mipmap-xxxhdpi'); var stringsFile = path.join(appResDir, 'values', 'strings.xml'); var colorsFile = path.join(appResDir, 'values', 'colors.xml'); // if the platform dir doesn't exist, create it self.debug('Creating the ' + constants.platform.name + ' app folder...'); return fileTools.mkdirp(platformDir) // download manifest icons to the app's folder .then(function () { return self.downloadIcons(w3cManifestInfo.content, w3cManifestInfo.content.start_url, imagesDir); }) // copy the documentation .then(function () { return self.copyDocumentation(platformDir); }) // write generation info (telemetry) .then(function () { return self.writeGenerationInfo(w3cManifestInfo, platformDir); }) // copy project template to the source folder .then(function () { self.debug('Copying project assets...'); var projectTemplateDir = path.join(self.baseDir, 'template'); return fileTools.syncFiles(projectTemplateDir, sourceDir, {}) .catch(function (err) { return Q.reject(new CustomError('Failed to copy the project template to the source folder.', err)); }); }) // copy downloaded images to the location of the launch/splash icons .then(function () { self.debug('Copying downloaded images...'); return fileTools.readFolder(imagesDir) .then(function (files) { var tasks = files.map(function(file) { var size = path.basename(file, path.extname(file)); var icon = iconSizeMap[size]; if (icon) { var sourceFile = path.join(imagesDir, file); var targetFile = path.join(appResDir, 'mipmap-' + icon.density, 'ic_' + icon.type + '.png'); return fileTools.copyFile(sourceFile, targetFile); } }); return tasks.reduce(Q.when, new Q()); }) .catch(function (err) { if (err.code !== 'ENOENT') { return Q.reject(err); } }); }) // update app's name in strings.xml file .then(function () { self.debug('Updating app\'s name element...'); return fileTools.readFile(stringsFile, 'utf-8').then(function (xml) { var doc = new DOMParser().parseFromString(xml); var elements = doc.getElementsByTagName('string'); for (var i = 0; i < elements.length; i++) { if (elements[i].getAttribute('name') === 'app_name' && w3cManifestInfo.content.short_name || w3cManifestInfo.content.name) { elements[i].textContent = w3cManifestInfo.content.short_name || w3cManifestInfo.content.name; } } return doc.toString(); }).then(function (updatedXml) { return fileTools.writeFile(stringsFile, updatedXml, 'utf-8'); }).fail(function (err) { return Q.reject(new CustomError('Could not update the strings.xml file', err)); }); }) // update colorPrimary in colors.xml file .then(function () { self.debug('Updating colorPrimary element...'); return fileTools.readFile(colorsFile, 'utf-8').then(function (xml) { var doc = new DOMParser().parseFromString(xml); var elements = doc.getElementsByTagName('color'); for (var i = 0; i < elements.length; i++) { if (elements[i].getAttribute('name') === 'colorPrimary' && w3cManifestInfo.content.theme_color || w3cManifestInfo.content.background_color) { var colorCode = w3cManifestInfo.content.theme_color || w3cManifestInfo.content.background_color; var colorName = colorNames(colorCode); elements[i].textContent = colorName || w3cManifestInfo.content.theme_color || w3cManifestInfo.content.background_color; } } return doc.toString(); }).then(function (updatedXml) { return fileTools.writeFile(colorsFile, updatedXml, 'utf-8'); }).fail(function (err) { return Q.reject(new CustomError('Could not update the strings.xml file', err)); }); }) // persist the platform-specific manifest .then(function () { self.debug('Copying the ' + constants.platform.name + ' manifest to the app folder...'); var manifestDir = path.join(platformDir, 'source', 'app', 'src', 'main', 'assets'); return fileTools.mkdirp(manifestDir) .then(function () { var manifestFilePath = path.join(manifestDir, 'manifest.json'); return manifestTools.writeToFile(w3cManifestInfo, manifestFilePath); }); }) .nodeify(callback); }; self.getManifestIcons = function (manifest) { var manifestIcons = []; Object.keys(iconSizeMap).forEach(function (size) { var icon = self.getManifestIcon(manifest, size); if (!!icon) { manifestIcons.push({ fileName: size + '.png', url: icon.src }); } }); return manifestIcons; }; /** * Receives the size of an icon (e.g. '48x48') and returns the corresponding icon element * from the manifest or undefined if not found. The method looks for an icon that: * - Has the specified size * - Is a PNG icon */ self.getManifestIcon = function (manifest, size) { size = size.trim().toLowerCase(); return (manifest.icons || []).find(function (icon) { var extension = path.extname(url.parse(icon.src).pathname); return ((extension && (extension.toLowerCase() === '.png') || (icon.type && icon.type.toLowerCase()) === 'image/png')) && !!icon.sizes.split(/\s+/).find(function (iconSize) { return iconSize === size; }); }); }; self.addManifestIcon = function (manifest, fileName, size) { if (!manifest.icons) { manifest.icons = []; } manifest.icons.push({ 'src': fileName, 'sizes': size.toLowerCase().trim()}); }; } util.inherits(Platform, PlatformBase); module.exports = Platform;