3bc1263294
build / build (push) Successful in 4m8s
android/ links engine/src/ and interpreter/src/ via JNI/CMake (real astronomy.c, not the watch's size-constrained substitute), rendering through Jetpack Compose with a burger-menu config page mirroring WatchConfig and a WorkManager-driven daily notification. Independent of the watch app - neither requires the other. build-image now bundles the Android SDK/NDK alongside the Pebble SDK (VERSION bumped to 2), and .gitea/workflows/build.yml/Makefile/ run-image.sh build and publish the APK the same way as the CLI tarball and .pbw.
80 lines
3.2 KiB
Python
80 lines
3.2 KiB
Python
#!/usr/bin/env python3
|
|
"""Generates the Android launcher icon from ../../res/logo.png.
|
|
|
|
Produces:
|
|
- mipmap-<density>/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-<density>/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 (<API26) flattened fallback, same for round and square -
|
|
# launchers on those versions apply their own circular crop.
|
|
legacy = Image.new("RGBA", (legacy_px, legacy_px), BACKGROUND_RGBA)
|
|
legacy_logo = resized_logo(logo, round(legacy_px * SAFE_ZONE_RATIO))
|
|
legacy_offset = ((legacy_px - legacy_logo.width) // 2, (legacy_px - legacy_logo.height) // 2)
|
|
legacy.paste(legacy_logo, legacy_offset, legacy_logo)
|
|
legacy.convert("RGB").save(os.path.join(mipmap_dir, "ic_launcher.png"))
|
|
legacy.convert("RGB").save(os.path.join(mipmap_dir, "ic_launcher_round.png"))
|
|
|
|
print(f"wrote launcher icons for {len(DENSITIES)} densities to {RES_DIR}", file=sys.stderr)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|