diff --git a/lib/apk.js b/lib/apk.js index c2cc25a..4985d05 100644 --- a/lib/apk.js +++ b/lib/apk.js @@ -3,6 +3,7 @@ const { mapInfoResource, findApkIconPath, getBase64FromBuffer } = require('./uti const ManifestName = /^androidmanifest\.xml$/ const ResourceName = /^resources\.arsc$/ +const AdaptiveIconParser = require('./xml-parser/adaptive-icon') const ManifestXmlParser = require('./xml-parser/manifest') const ResourceFinder = require('./resource-finder') @@ -32,20 +33,32 @@ class ApkParser extends Zip { resourceMap = this._parseResourceMap(buffers[ResourceName]) // update apkInfo with resourceMap apkInfo = mapInfoResource(apkInfo, resourceMap) + apkInfo.icon = null + apkInfo.adaptiveIcons = null // find icon path and parse icon const iconPath = findApkIconPath(apkInfo) - if (iconPath) { + if (iconPath.endsWith('.xml')) { + this.getEntry(iconPath).then(adaptiveIconBuffer => { + const adaptiveIconParser = new AdaptiveIconParser(adaptiveIconBuffer, resourceMap) + const adaptiveIcons = adaptiveIconParser.parse() + return this._getAdaptiveIconBuffers(adaptiveIcons) + }).then(iconBuffers => { + apkInfo.adaptiveIcons = iconBuffers + resolve(apkInfo) + }).catch(e => { + resolve(apkInfo) + console.warn('[Warning] failed to parse adaptive icon: ', e) + }) + } else if (iconPath) { this.getEntry(iconPath).then(iconBuffer => { apkInfo.icon = iconBuffer ? getBase64FromBuffer(iconBuffer) : null resolve(apkInfo) }).catch(e => { - apkInfo.icon = null resolve(apkInfo) console.warn('[Warning] failed to parse icon: ', e) }) } else { - apkInfo.icon = null resolve(apkInfo) } } @@ -54,6 +67,18 @@ class ApkParser extends Zip { }) }) } + + _getAdaptiveIconBuffers (icons) { + const iconBuffers = {} + const pending = [] + for (let key of Object.keys(icons)) { + pending.push(this.getEntry(icons[key]).then(buffer => { + iconBuffers[key] = getBase64FromBuffer(buffer) + })) + } + return Promise.allSettled(pending).then(() => iconBuffers) + } + /** * Parse manifest * @param {Buffer} buffer // manifest file's buffer diff --git a/lib/xml-parser/adaptive-icon.js b/lib/xml-parser/adaptive-icon.js new file mode 100644 index 0000000..bb26013 --- /dev/null +++ b/lib/xml-parser/adaptive-icon.js @@ -0,0 +1,45 @@ +const {mapInfoResource, findApkIconPath} = require('../utils') +const BinaryXmlParser = require('./binary') + +const ICON_NODE_NAMES = ['foreground', 'background', 'monochrome'] + +class AdaptiveIconParser { + constructor (buffer, resourceMap, options = {}) { + this.buffer = buffer + this.resourceMap = resourceMap + this.xmlParser = new BinaryXmlParser(this.buffer, options) + } + + parse () { + const document = this.xmlParser.parse() + const adaptiveIcon = {} + + if (document.nodeName === 'adaptive-icon') { + document.childNodes.forEach(element => { + if (ICON_NODE_NAMES.includes(element.nodeName)) { + adaptiveIcon[element.nodeName] = this.parseAdaptiveIconElement(element) + } + }) + return adaptiveIcon + } + return null + } + + parseAdaptiveIconElement (element) { + const collapsed = Object.create(null) + for (let attr of Array.from(element.attributes)) { + collapsed[attr.name] = attr.typedValue.value + } + const resource = mapInfoResource(collapsed, this.resourceMap) + if (resource.drawable) { + return findApkIconPath({ + application: { + icon: resource.drawable + } + }) + } + return '' + } +} + +module.exports = AdaptiveIconParser