diff --git a/store-assets/README.md b/store-assets/README.md new file mode 100644 index 0000000..07a9b6f --- /dev/null +++ b/store-assets/README.md @@ -0,0 +1,30 @@ +# Google Play Store assets + +## Ready to upload + +- `play-store-icon-512.png`: app icon, 512 x 512 px, PNG. +- `feature-graphic-1024x500.png`: feature graphic, 1024 x 500 px, PNG. +- `phone-01-map-1920x1080.png`: real app map view, landscape 16:9. +- `phone-02-data-drawings-1920x1080.png`: real data and drawing tools, landscape 16:9. +- `phone-03-projects-1920x1080.png`: real project archive, landscape 16:9. + +The screenshots were captured from the Windows build, whose map and responsive panels match the Android app. The Windows title bar has been removed and the real app view is presented inside a 16:9 Store graphic. + +## Optional Android replacements + +For a later production listing, the Windows captures can be replaced with Android phone captures: + +1. `phone-01-map.png`: main map with cadastral layer and one selected parcel. +2. `phone-02-project-tools.png`: project or data-and-drawings panel with useful content visible. +3. `phone-03-gps.png`: map with GPS position or a short track, without showing a private address. +4. `phone-04-export.png`: import/export tools or project archive. + +Use portrait orientation and keep every image between 320 px and 3840 px per side. Do not include personal coordinates, credentials, license codes, notifications, or system dialogs. + +## Store text + +Short description: + +`Mappe catastali, GPS, misure, progetti GIS, importazione e backup WebDAV.` + +The complete Italian description is recorded in `PLAY_STORE_PRO_CHECKLIST.md` and can be pasted into Play Console. diff --git a/store-assets/feature-graphic-1024x500.png b/store-assets/feature-graphic-1024x500.png new file mode 100644 index 0000000..f78b4e3 Binary files /dev/null and b/store-assets/feature-graphic-1024x500.png differ diff --git a/store-assets/phone-01-map-1920x1080.png b/store-assets/phone-01-map-1920x1080.png new file mode 100644 index 0000000..93469fa Binary files /dev/null and b/store-assets/phone-01-map-1920x1080.png differ diff --git a/store-assets/phone-02-data-drawings-1920x1080.png b/store-assets/phone-02-data-drawings-1920x1080.png new file mode 100644 index 0000000..63b1708 Binary files /dev/null and b/store-assets/phone-02-data-drawings-1920x1080.png differ diff --git a/store-assets/phone-03-projects-1920x1080.png b/store-assets/phone-03-projects-1920x1080.png new file mode 100644 index 0000000..6fb2818 Binary files /dev/null and b/store-assets/phone-03-projects-1920x1080.png differ diff --git a/store-assets/play-store-icon-512.png b/store-assets/play-store-icon-512.png new file mode 100644 index 0000000..8316f19 Binary files /dev/null and b/store-assets/play-store-icon-512.png differ diff --git a/tools/generate_play_store_assets.py b/tools/generate_play_store_assets.py new file mode 100644 index 0000000..ef20f2f --- /dev/null +++ b/tools/generate_play_store_assets.py @@ -0,0 +1,173 @@ +from pathlib import Path + +from PIL import Image, ImageDraw, ImageFont + + +ROOT = Path(__file__).resolve().parents[1] +OUTPUT = ROOT / "store-assets" +GREEN = "#075E54" +GREEN_LIGHT = "#83B6A5" +ORANGE = "#F97316" +INK = "#18342F" +PAPER = "#F7FAF5" +LINE = "#BCD3CB" +BLUE = "#DCEAF0" + + +def font(size: int, semibold: bool = False) -> ImageFont.FreeTypeFont: + name = "seguisb.ttf" if semibold else "segoeui.ttf" + return ImageFont.truetype(str(Path("C:/Windows/Fonts") / name), size) + + +def draw_mark(draw: ImageDraw.ImageDraw, box: tuple[int, int, int, int]) -> None: + left, top, right, bottom = box + width = right - left + height = bottom - top + + map_points = [ + (left + 0.04 * width, top + 0.30 * height), + (left + 0.32 * width, top + 0.18 * height), + (left + 0.64 * width, top + 0.30 * height), + (left + 0.94 * width, top + 0.18 * height), + (left + 0.94 * width, top + 0.76 * height), + (left + 0.64 * width, top + 0.88 * height), + (left + 0.32 * width, top + 0.76 * height), + (left + 0.04 * width, top + 0.88 * height), + ] + draw.polygon(map_points, fill=PAPER, outline="#E1ECE5", width=max(4, width // 38)) + draw.line( + [(left + 0.32 * width, top + 0.18 * height), (left + 0.32 * width, top + 0.76 * height)], + fill=GREEN_LIGHT, + width=max(4, width // 35), + ) + draw.line( + [(left + 0.64 * width, top + 0.30 * height), (left + 0.64 * width, top + 0.88 * height)], + fill=GREEN_LIGHT, + width=max(4, width // 35), + ) + + cx = left + 0.50 * width + cy = top + 0.36 * height + radius = 0.20 * width + pin = [ + (cx, cy + radius * 1.72), + (cx - radius * 0.86, cy + radius * 0.20), + (cx - radius, cy - radius * 0.18), + (cx - radius * 0.72, cy - radius * 0.72), + (cx - radius * 0.22, cy - radius), + (cx + radius * 0.22, cy - radius), + (cx + radius * 0.72, cy - radius * 0.72), + (cx + radius, cy - radius * 0.18), + (cx + radius * 0.86, cy + radius * 0.20), + ] + draw.polygon(pin, fill=ORANGE) + draw.ellipse( + (cx - radius, cy - radius, cx + radius, cy + radius), + fill=ORANGE, + outline="white", + width=max(5, width // 32), + ) + hole = radius * 0.34 + draw.ellipse((cx - hole, cy - hole, cx + hole, cy + hole), fill="white") + + +def create_icon() -> None: + image = Image.new("RGB", (512, 512), GREEN) + draw = ImageDraw.Draw(image) + draw_mark(draw, (82, 78, 430, 434)) + image.save(OUTPUT / "play-store-icon-512.png", optimize=True) + + +def create_feature_graphic() -> None: + image = Image.new("RGB", (1024, 500), "#F3F7F5") + draw = ImageDraw.Draw(image) + + draw.rectangle((0, 0, 20, 500), fill=GREEN) + draw.rectangle((20, 0, 32, 500), fill=ORANGE) + + draw.text((78, 107), "LME", font=font(34, True), fill=ORANGE) + draw.text((78, 154), "Mappe Catastali Pro", font=font(55, True), fill=INK) + draw.text((80, 234), "Mappe, GPS e progetti GIS", font=font(27), fill=GREEN) + draw.line((80, 290, 455, 290), fill=LINE, width=3) + draw.text((80, 315), "Consulta • misura • organizza", font=font(21), fill="#46635D") + + panel = (595, 48, 965, 452) + draw.rounded_rectangle(panel, radius=8, fill="white", outline=LINE, width=3) + draw.polygon([(618, 86), (760, 65), (802, 164), (664, 190)], fill=BLUE, outline=GREEN_LIGHT) + draw.polygon([(806, 75), (940, 110), (923, 236), (784, 205)], fill="#E9EFE7", outline=GREEN_LIGHT) + draw.polygon([(625, 210), (779, 184), (817, 318), (665, 351)], fill="#F4E9D9", outline=GREEN_LIGHT) + draw.polygon([(820, 232), (929, 256), (946, 394), (853, 420), (798, 336)], fill=BLUE, outline=GREEN_LIGHT) + for x1, y1, x2, y2 in [ + (630, 128, 932, 327), + (646, 310, 915, 128), + (704, 73, 857, 422), + (607, 267, 948, 181), + ]: + draw.line((x1, y1, x2, y2), fill="#A4BDB5", width=3) + draw_mark(draw, (706, 136, 883, 340)) + + image.save(OUTPUT / "feature-graphic-1024x500.png", optimize=True) + + +def create_store_screenshot(raw_name: str, output_name: str, title: str, subtitle: str) -> None: + raw = Image.open(OUTPUT / raw_name).convert("RGB") + # Remove the Windows title bar and the desktop-only watermark/status area. + app_view = raw.crop((0, 32, raw.width, min(raw.height, 500))) + max_width, max_height = 1840, 760 + scale = min(max_width / app_view.width, max_height / app_view.height) + app_view = app_view.resize( + (round(app_view.width * scale), round(app_view.height * scale)), + Image.Resampling.LANCZOS, + ) + + image = Image.new("RGB", (1920, 1080), "#F3F7F5") + draw = ImageDraw.Draw(image) + draw.rectangle((0, 0, 24, 1080), fill=GREEN) + draw.rectangle((24, 0, 38, 1080), fill=ORANGE) + draw.text((88, 54), title, font=font(52, True), fill=INK) + draw.text((90, 126), subtitle, font=font(26), fill="#46635D") + + x = (1920 - app_view.width) // 2 + y = 270 + draw.rounded_rectangle( + (x - 8, y - 8, x + app_view.width + 8, y + app_view.height + 8), + radius=8, + fill="white", + outline=LINE, + width=4, + ) + image.paste(app_view, (x, y)) + image.save(OUTPUT / output_name, optimize=True) + + +def main() -> None: + OUTPUT.mkdir(exist_ok=True) + create_icon() + create_feature_graphic() + screenshots = [ + ( + "windows-map-raw.jpg", + "phone-01-map-1920x1080.png", + "Mappe catastali e cartografia", + "Consulta il territorio, cerca localita e lavora sulla mappa.", + ), + ( + "windows-data-drawings-raw.jpg", + "phone-02-data-drawings-1920x1080.png", + "Importa dati e disegna", + "KML, KMZ e GeoJSON insieme a misure e strumenti GIS.", + ), + ( + "windows-projects-raw.jpg", + "phone-03-projects-1920x1080.png", + "Organizza i tuoi progetti", + "Salva, riapri e condividi il lavoro sul campo.", + ), + ] + for raw_name, output_name, title, subtitle in screenshots: + if (OUTPUT / raw_name).exists(): + create_store_screenshot(raw_name, output_name, title, subtitle) + + +if __name__ == "__main__": + main()