#!/usr/bin/env python3 """Draw the app's launcher icon: a white padlock with three "code" dots on a teal disc, on a transparent background. The icon is drawn at 8x the target size and scaled down, which gives smooth edges at the small sizes watches use (35 to 70 px). Usage: make_icon.py --size 60 --out resources/drawables/launcher_icon.png """ from __future__ import annotations import argparse from pathlib import Path from PIL import Image, ImageDraw TEAL = (14, 124, 134, 255) WHITE = (255, 255, 255, 255) SUPERSAMPLE = 8 def draw_icon(size: int) -> Image.Image: """Return the icon as an RGBA image of size x size pixels.""" s = size * SUPERSAMPLE img = Image.new("RGBA", (s, s), (0, 0, 0, 0)) d = ImageDraw.Draw(img) margin = s * 0.02 d.ellipse((margin, margin, s - margin, s - margin), fill=TEAL) # Lock body: a rounded rectangle in the lower middle. body_w, body_h = s * 0.50, s * 0.36 body_x0, body_y0 = (s - body_w) / 2, s * 0.44 d.rounded_rectangle((body_x0, body_y0, body_x0 + body_w, body_y0 + body_h), radius=s * 0.06, fill=WHITE) # Shackle: the top half of an ellipse plus two short legs into the body. stroke = int(s * 0.07) sx0, sx1, sy0, sy1 = s * 0.34, s * 0.66, s * 0.20, s * 0.56 d.arc((sx0, sy0, sx1, sy1), start=180, end=360, fill=WHITE, width=stroke) leg_top, leg_bottom = (sy0 + sy1) / 2, body_y0 + s * 0.02 d.rectangle((sx0, leg_top, sx0 + stroke, leg_bottom), fill=WHITE) d.rectangle((sx1 - stroke, leg_top, sx1, leg_bottom), fill=WHITE) # Three dots on the body stand for a one-time code. radius = s * 0.035 center_y = body_y0 + body_h * 0.5 for i in range(3): center_x = s * (0.38 + 0.12 * i) d.ellipse((center_x - radius, center_y - radius, center_x + radius, center_y + radius), fill=TEAL) return img.resize((size, size), Image.Resampling.LANCZOS) def main() -> None: parser = argparse.ArgumentParser(description=__doc__.splitlines()[0]) parser.add_argument("--size", type=int, default=60, help="icon width and height in pixels") parser.add_argument("--out", type=Path, required=True, help="PNG file to write") args = parser.parse_args() if not 16 <= args.size <= 256: parser.error("--size must be between 16 and 256") args.out.parent.mkdir(parents=True, exist_ok=True) draw_icon(args.size).save(args.out, format="PNG", optimize=True) print(f"wrote {args.out} ({args.size}x{args.size})") if __name__ == "__main__": main()