Extracting Icons
Learn how to extract launcher icons with different densities.
Basic Icon Extraction
typescript
import { Apk } from "node-apk";
import { promises as fs } from "node:fs";
const apk = new Apk("app.apk");
// Get the best available launcher icon
const icon = await apk.getLauncherIcon();
await fs.writeFile("icon.png", icon);Density-Specific Icons
typescript
import { Apk, type IconDensity } from "node-apk";
const apk = new Apk("app.apk");
// Available densities
const densities: IconDensity[] = [
"mdpi", // ~160 dpi
"hdpi", // ~240 dpi
"xhdpi", // ~320 dpi
"xxhdpi", // ~480 dpi
"xxxhdpi", // ~640 dpi
"nodpi", // No scaling
"anydpi", // Vector/adaptive
];
for (const density of densities) {
try {
const icon = await apk.getLauncherIcon({ density });
await fs.writeFile(`icon-${density}.png`, icon);
console.log(`Extracted ${density}: ${icon.length} bytes`);
} catch (error) {
console.log(`No ${density} icon available`);
}
}Fallback Behavior
When the preferred density isn't available, the method automatically selects the best alternative:
typescript
// Request xxxhdpi - will fall back if not available
const icon = await apk.getLauncherIcon({ density: "xxxhdpi" });
// Always returns an icon (best available)Saving Icons with Metadata
typescript
import { Apk } from "node-apk";
import { promises as fs } from "node:fs";
import * as path from "node:path";
async function extractAllIcons(apkPath: string, outputDir: string) {
const apk = new Apk(apkPath);
await fs.mkdir(outputDir, { recursive: true });
const densities = ["mdpi", "hdpi", "xhdpi", "xxhdpi", "xxxhdpi"] as const;
for (const density of densities) {
const icon = await apk.getLauncherIcon({ density });
const filename = path.join(outputDir, `icon-${density}.png`);
await fs.writeFile(filename, icon);
console.log(`Saved ${filename}`);
}
}
extractAllIcons("app.apk", "./icons");Detecting Icon Format
Icons can be either PNG or WebP format:
typescript
function detectImageFormat(buffer: Buffer): "png" | "webp" | "unknown" {
// PNG: starts with 89 50 4E 47
if (buffer[0] === 0x89 && buffer[1] === 0x50) {
return "png";
}
// WebP: starts with 52 49 46 46 (RIFF)
if (buffer[0] === 0x52 && buffer[1] === 0x49) {
return "webp";
}
return "unknown";
}
const apk = new Apk("app.apk");
const icon = await apk.getLauncherIcon();
const format = detectImageFormat(icon);
console.log(`Icon format: ${format}`);
// Save with correct extension
const ext = format === "webp" ? "webp" : "png";
await fs.writeFile(`icon.${ext}`, icon);Using with Image Processing
typescript
import { Apk } from "node-apk";
import sharp from "sharp"; // npm install sharp
async function processIcon(apkPath: string) {
const apk = new Apk(apkPath);
const icon = await apk.getLauncherIcon();
// Resize to specific dimensions
await sharp(icon)
.resize(512, 512)
.png()
.toFile("icon-512.png");
// Create multiple sizes for app stores
const sizes = [192, 144, 96, 72, 48, 36];
for (const size of sizes) {
await sharp(icon)
.resize(size, size)
.png()
.toFile(`icon-${size}.png`);
}
}Responsive Icon Generation
typescript
import { Apk } from "node-apk";
import sharp from "sharp";
async function generateResponsiveIcons(apkPath: string) {
const apk = new Apk(apkPath);
// Get highest quality icon
const xxxhdpi = await apk.getLauncherIcon({ density: "xxxhdpi" });
// Generate responsive set
const breakpoints = [
{ name: "sm", size: 32 },
{ name: "md", size: 64 },
{ name: "lg", size: 128 },
{ name: "xl", size: 256 },
{ name: "2xl", size: 512 },
];
for (const bp of breakpoints) {
await sharp(xxxhdpi)
.resize(bp.size, bp.size)
.png()
.toFile(`icon-${bp.name}.png`);
}
}Related
- Basic Usage - Getting started guide
- API: getLauncherIcon - API reference