#!/usr/bin/env python3 """Regenerate Android launcher icons from piti-icon.png. Usage: python3 tools/gen_launcher_icons.py Requires: Pillow (pip install Pillow) Produces, under app/src/main/res/: - mipmap-/ic_launcher.png legacy square (rounded), all densities - mipmap-/ic_launcher_round.png legacy round, all densities - mipmap-/ic_launcher_foreground.png adaptive-icon foreground (padded) The adaptive XMLs (mipmap-anydpi-v26/) and the black background drawable are checked in and don't need regenerating. """ import os from PIL import Image, ImageDraw ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) SRC = os.path.join(ROOT, "piti-icon.png") RES = os.path.join(ROOT, "app", "src", "main", "res") BG = (0, 0, 0, 255) # matches the app's black theme / ic_launcher_background.xml brain = Image.open(SRC).convert("RGBA") LEGACY = {"mdpi": 48, "hdpi": 72, "xhdpi": 96, "xxhdpi": 144, "xxxhdpi": 192} ADAPTIVE = {"mdpi": 108, "hdpi": 162, "xhdpi": 216, "xxhdpi": 324, "xxxhdpi": 432} def fit(img, box): w, h = img.size s = box / max(w, h) return img.resize((max(1, int(w * s)), max(1, int(h * s))), Image.LANCZOS) def rounded_mask(size, radius): m = Image.new("L", (size, size), 0) ImageDraw.Draw(m).rounded_rectangle([0, 0, size - 1, size - 1], radius=radius, fill=255) return m def circle_mask(size): m = Image.new("L", (size, size), 0) ImageDraw.Draw(m).ellipse([0, 0, size - 1, size - 1], fill=255) return m def compose(size, mask, content_scale): canvas = Image.new("RGBA", (size, size), BG) fg = fit(brain, int(size * content_scale)) canvas.paste(fg, ((size - fg.width) // 2, (size - fg.height) // 2), fg) out = Image.new("RGBA", (size, size), (0, 0, 0, 0)) out.paste(canvas, (0, 0), mask) return out def save(img, folder, name): d = os.path.join(RES, folder) os.makedirs(d, exist_ok=True) img.save(os.path.join(d, name)) for bucket, size in LEGACY.items(): save(compose(size, rounded_mask(size, int(size * 0.18)), 0.86), f"mipmap-{bucket}", "ic_launcher.png") save(compose(size, circle_mask(size), 0.86), f"mipmap-{bucket}", "ic_launcher_round.png") for bucket, size in ADAPTIVE.items(): fg_canvas = Image.new("RGBA", (size, size), (0, 0, 0, 0)) fg = fit(brain, int(size * 0.64)) # keep within the adaptive-icon safe zone fg_canvas.paste(fg, ((size - fg.width) // 2, (size - fg.height) // 2), fg) save(fg_canvas, f"mipmap-{bucket}", "ic_launcher_foreground.png") print("launcher icons regenerated under app/src/main/res/")