From 22a57a5c5d972a61114b323e100e1f9ca74a3dc4 Mon Sep 17 00:00:00 2001 From: Mark Riedesel Date: Thu, 11 May 2023 10:09:11 -0500 Subject: [PATCH 1/2] feat(apk): add support for adaptive icon parsing --- lib/apk.js | 28 +++++++++++++++++++- lib/xml-parser/adaptive-icon.js | 45 +++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 lib/xml-parser/adaptive-icon.js diff --git a/lib/apk.js b/lib/apk.js index c2cc25a..93bbc62 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') @@ -35,7 +36,20 @@ class ApkParser extends Zip { // 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.icon = iconBuffers + resolve(apkInfo) + }).catch(e => { + apkInfo.icon = null + 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) @@ -54,6 +68,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 From 0708454f3bddc48cca637e91b3a21ea4cafda3c2 Mon Sep 17 00:00:00 2001 From: Mark Riedesel Date: Wed, 17 May 2023 15:39:36 -0400 Subject: [PATCH 2/2] expose Android adaptive icons via adaptiveIcons property rather than reuse icon property --- lib/apk.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/apk.js b/lib/apk.js index 93bbc62..4985d05 100644 --- a/lib/apk.js +++ b/lib/apk.js @@ -33,6 +33,8 @@ 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) @@ -42,10 +44,9 @@ class ApkParser extends Zip { const adaptiveIcons = adaptiveIconParser.parse() return this._getAdaptiveIconBuffers(adaptiveIcons) }).then(iconBuffers => { - apkInfo.icon = iconBuffers + apkInfo.adaptiveIcons = iconBuffers resolve(apkInfo) }).catch(e => { - apkInfo.icon = null resolve(apkInfo) console.warn('[Warning] failed to parse adaptive icon: ', e) }) @@ -54,12 +55,10 @@ class ApkParser extends Zip { 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) } }