From bb666e54bff8363fd64ce1c4a7ed14ca889ead77 Mon Sep 17 00:00:00 2001 From: mcocdaa <2021137961@qq.com> Date: Wed, 14 Oct 2026 11:27:29 +0200 Subject: [PATCH] fix(android): wire roundIcon and keep.xml so AGP 9 R8 preserves launcher mipmaps --- .../release/configure_android_system_bars.py | 44 +++++++++++++++++++ .../test_configure_android_system_bars.py | 33 +++++++++++++- 2 files changed, 76 insertions(+), 1 deletion(-) diff --git a/scripts/release/configure_android_system_bars.py b/scripts/release/configure_android_system_bars.py index ef4cea63..be98ddb6 100644 --- a/scripts/release/configure_android_system_bars.py +++ b/scripts/release/configure_android_system_bars.py @@ -79,6 +79,49 @@ def configure_activity(source: str) -> str: ) +_MANIFEST_ICON = 'android:icon="@mipmap/ic_launcher"' +_MANIFEST_ROUND_ICON = 'android:roundIcon="@mipmap/ic_launcher_round"' +_KEEP_XML = ( + '\n' + '\n' +) + + +def configure_manifest_round_icon(manifest_source: str) -> str: + """Ensure AndroidManifest.xml references @mipmap/ic_launcher_round so AGP 9 R8 keeps it.""" + + if _MANIFEST_ROUND_ICON in manifest_source: + return manifest_source + if _MANIFEST_ICON not in manifest_source: + raise ValueError("unsupported Tauri AndroidManifest template") + return manifest_source.replace( + _MANIFEST_ICON, + f"{_MANIFEST_ICON}\n {_MANIFEST_ROUND_ICON}", + 1, + ) + + +def preserve_launcher_resources(activity_path: Path) -> None: + """Wire android:roundIcon in AndroidManifest.xml and write res/raw/keep.xml.""" + + main_dir = next((p for p in activity_path.parents if p.name == "main"), None) + if main_dir is None: + return + manifest_path = main_dir / "AndroidManifest.xml" + if manifest_path.is_file(): + original_manifest = manifest_path.read_text() + updated_manifest = configure_manifest_round_icon(original_manifest) + if updated_manifest != original_manifest: + manifest_path.write_text(updated_manifest) + res_dir = main_dir / "res" + if res_dir.is_dir() or manifest_path.is_file(): + keep_path = res_dir / "raw" / "keep.xml" + keep_path.parent.mkdir(parents=True, exist_ok=True) + keep_path.write_text(_KEEP_XML) + + def main() -> int: parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("--activity", type=Path, required=True) @@ -87,6 +130,7 @@ def main() -> int: configured = configure_activity(original) if configured != original: args.activity.write_text(configured) + preserve_launcher_resources(args.activity) print(f"configured Android system bars: {args.activity}") return 0 diff --git a/tests/release/test_configure_android_system_bars.py b/tests/release/test_configure_android_system_bars.py index 11e09aab..bb49617f 100644 --- a/tests/release/test_configure_android_system_bars.py +++ b/tests/release/test_configure_android_system_bars.py @@ -1,8 +1,14 @@ from __future__ import annotations +from pathlib import Path + import pytest -from scripts.release.configure_android_system_bars import configure_activity +from scripts.release.configure_android_system_bars import ( + configure_activity, + configure_manifest_round_icon, + preserve_launcher_resources, +) TAURI_ACTIVITY = """package io.termflow.client @@ -31,6 +37,31 @@ def test_configures_orientation_aware_transient_status_bar_once() -> None: assert configure_activity(configured) == configured +def test_preserves_round_launcher_icon_in_manifest_and_keep_xml(tmp_path: Path) -> None: + main_dir = tmp_path / "src" / "main" + activity = main_dir / "java" / "io" / "termflow" / "client" / "MainActivity.kt" + activity.parent.mkdir(parents=True, exist_ok=True) + activity.write_text(TAURI_ACTIVITY) + manifest = main_dir / "AndroidManifest.xml" + manifest.write_text( + '\n' + ' \n' + ' \n' + '\n' + ) + + preserve_launcher_resources(activity) + + updated = manifest.read_text() + assert 'android:roundIcon="@mipmap/ic_launcher_round"' in updated + assert configure_manifest_round_icon(updated) == updated + keep_xml = main_dir / "res" / "raw" / "keep.xml" + assert keep_xml.is_file() + assert "@mipmap/ic_launcher_round" in keep_xml.read_text() + + @pytest.mark.parametrize( "source", [