Add a standalone Android app with the same daily reading and config page
build / build (push) Successful in 4m8s
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.
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
#!/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()
|
||||
Reference in New Issue
Block a user