Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 28 additions & 3 deletions lib/apk.js
Original file line number Diff line number Diff line change
Expand Up @@ -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')

Expand Down Expand Up @@ -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)
}
}
Expand All @@ -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
Expand Down
45 changes: 45 additions & 0 deletions lib/xml-parser/adaptive-icon.js
Original file line number Diff line number Diff line change
@@ -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