#!/usr/bin/env python3 """Generates the Android launcher icon from ../../res/logo.png. Produces: - mipmap-/ic_launcher_foreground.png - the logo centered, scaled to fit the adaptive-icon safe zone (66% of a 108dp canvas), transparent background - paired at runtime with @color/ic_launcher_background (see mipmap-anydpi-v26/ic_launcher.xml, committed - a static reference, not generated) for API 26+. - mipmap-/ic_launcher.png / ic_launcher_round.png - the same logo flattened onto ic_launcher_background, for API <26 devices that can't use the adaptive-icon XML at all. Generated output, not committed (see the repo's root .gitignore's /android/app/src/main/res/mipmap-* entries) - regenerate by running this script directly or via Gradle's generateLauncherIcon task (wired into app's preBuild, see ../app/build.gradle.kts). Requires Pillow, already part of build-image's toolchain. """ import os import sys from PIL import Image SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) LOGO_PATH = os.path.normpath(os.path.join(SCRIPT_DIR, "..", "..", "res", "logo.png")) RES_DIR = os.path.normpath(os.path.join(SCRIPT_DIR, "..", "app", "src", "main", "res")) BACKGROUND_RGBA = (27, 27, 47, 255) # matches @color/ic_launcher_background # (density qualifier, adaptive-icon canvas px, legacy launcher px) DENSITIES = [ ("mdpi", 108, 48), ("hdpi", 162, 72), ("xhdpi", 216, 96), ("xxhdpi", 324, 144), ("xxxhdpi", 432, 192), ] # Adaptive icons render only the inner ~66% "safe zone" reliably across # launchers that apply their own mask shape. SAFE_ZONE_RATIO = 0.66 def resized_logo(logo, target_px): ratio = target_px / max(logo.width, logo.height) size = (round(logo.width * ratio), round(logo.height * ratio)) return logo.resize(size, Image.LANCZOS) def main(): logo = Image.open(LOGO_PATH).convert("RGBA") for density, canvas_px, legacy_px in DENSITIES: mipmap_dir = os.path.join(RES_DIR, f"mipmap-{density}") os.makedirs(mipmap_dir, exist_ok=True) # Adaptive-icon foreground: transparent canvas, logo scaled to the # safe zone and centered. foreground = Image.new("RGBA", (canvas_px, canvas_px), (0, 0, 0, 0)) fg_logo = resized_logo(logo, round(canvas_px * SAFE_ZONE_RATIO)) offset = ((canvas_px - fg_logo.width) // 2, (canvas_px - fg_logo.height) // 2) foreground.paste(fg_logo, offset, fg_logo) foreground.save(os.path.join(mipmap_dir, "ic_launcher_foreground.png")) # Legacy (