#!/usr/bin/env python3 """ Created: 2026-06-02 Updated: 2026-06-02 Name: replace_platform_icons.py Desc: Replace app icons for iOS, macOS, Android, OHOS platforms using processed icons from assets/templates/resized folder. Original icons are backed up before replacement. """ import os import shutil from PIL import Image BASE = '/Users/wushu/Documents/trae_projects/project/xianyan' SOURCE_DIR = os.path.join(BASE, 'assets/templates/resized') BACKUP_ROOT = os.path.join(BASE, 'assets/templates/backup_platform_icons') def get_source_icon(size): """Get source icon path for a given pixel size.""" filename = f'icon_{size}x{size}.png' path = os.path.join(SOURCE_DIR, filename) if os.path.exists(path): return path print(f" WARNING: Source icon {filename} not found!") return None def backup_and_replace(target_path, source_path): """Backup original icon and replace with new one.""" if not os.path.exists(target_path): print(f" SKIP: Target not found: {target_path}") return False # Create backup path maintaining directory structure rel_path = os.path.relpath(target_path, BASE) backup_path = os.path.join(BACKUP_ROOT, rel_path) backup_dir = os.path.dirname(backup_path) os.makedirs(backup_dir, exist_ok=True) # Backup if not already backed up if not os.path.exists(backup_path): shutil.copy2(target_path, backup_path) # Copy source to target shutil.copy2(source_path, target_path) # Verify new_img = Image.open(target_path) src_img = Image.open(source_path) if new_img.size == src_img.size: return True else: print(f" ERROR: Size mismatch after copy! {new_img.size} vs {src_img.size}") return False def replace_ios_runner(): """Replace iOS Runner app icons.""" print("\n=== iOS Runner AppIcon ===") ios_dir = os.path.join(BASE, 'ios/Runner/Assets.xcassets/AppIcon.appiconset') # Mapping: target_filename -> source_pixel_size mapping = { 'Icon-App-1024x1024@1x.png': 1024, 'Icon-App-20x20@1x.png': 20, 'Icon-App-20x20@2x.png': 40, 'Icon-App-20x20@3x.png': 60, 'Icon-App-29x29@1x.png': 29, 'Icon-App-29x29@2x.png': 58, 'Icon-App-29x29@3x.png': 87, 'Icon-App-40x40@1x.png': 40, 'Icon-App-40x40@2x.png': 80, 'Icon-App-40x40@3x.png': 120, 'Icon-App-60x60@2x.png': 120, 'Icon-App-60x60@3x.png': 180, 'Icon-App-76x76@1x.png': 76, 'Icon-App-76x76@2x.png': 152, 'Icon-App-83.5x83.5@2x.png': 167, } count = 0 for target_name, size in mapping.items(): source = get_source_icon(size) if source: target = os.path.join(ios_dir, target_name) if backup_and_replace(target, source): print(f" OK: {target_name} <- icon_{size}x{size}.png") count += 1 print(f" Replaced: {count}/{len(mapping)}") return count def replace_ios_widget(): """Replace iOS Widget app icon.""" print("\n=== iOS Widget AppIcon ===") widget_dir = os.path.join(BASE, 'ios/XianyanWidget/Assets.xcassets/AppIcon.appiconset') mapping = { 'Icon-App-1024x1024.png': 1024, } count = 0 for target_name, size in mapping.items(): source = get_source_icon(size) if source: target = os.path.join(widget_dir, target_name) if backup_and_replace(target, source): print(f" OK: {target_name} <- icon_{size}x{size}.png") count += 1 print(f" Replaced: {count}/{len(mapping)}") return count def replace_macos(): """Replace macOS app icons.""" print("\n=== macOS AppIcon ===") macos_dir = os.path.join(BASE, 'macos/Runner/Assets.xcassets/AppIcon.appiconset') mapping = { 'app_icon_16.png': 16, 'app_icon_32.png': 32, 'app_icon_64.png': 64, 'app_icon_128.png': 128, 'app_icon_256.png': 256, 'app_icon_512.png': 512, 'app_icon_1024.png': 1024, } count = 0 for target_name, size in mapping.items(): source = get_source_icon(size) if source: target = os.path.join(macos_dir, target_name) if backup_and_replace(target, source): print(f" OK: {target_name} <- icon_{size}x{size}.png") count += 1 print(f" Replaced: {count}/{len(mapping)}") return count def replace_android(): """Replace Android launcher icons.""" print("\n=== Android mipmap ===") res_dir = os.path.join(BASE, 'android/app/src/main/res') # Android density -> pixel size mapping = { 'mipmap-mdpi': 48, 'mipmap-hdpi': 72, 'mipmap-xhdpi': 96, 'mipmap-xxhdpi': 144, 'mipmap-xxxhdpi': 192, } count = 0 for density, size in mapping.items(): source = get_source_icon(size) if source: target = os.path.join(res_dir, density, 'ic_launcher.png') if backup_and_replace(target, source): print(f" OK: {density}/ic_launcher.png <- icon_{size}x{size}.png") count += 1 print(f" Replaced: {count}/{len(mapping)}") return count def replace_ohos(): """Replace OHOS (HarmonyOS) app icons.""" print("\n=== OHOS icons ===") # All OHOS icons are 1024x1024 paths = [ 'ohos/AppScope/resources/base/media/app_icon.png', 'ohos/AppScope/resources/base/media/foreground_icon.png', 'ohos/entry/src/main/resources/base/media/icon.png', 'ohos/entry/src/main/resources/base/media/foreground_icon.png', ] source = get_source_icon(1024) count = 0 if source: for rel_path in paths: target = os.path.join(BASE, rel_path) if backup_and_replace(target, source): print(f" OK: {rel_path} <- icon_1024x1024.png") count += 1 print(f" Replaced: {count}/{len(paths)}") return count def main(): os.makedirs(BACKUP_ROOT, exist_ok=True) print(f"Source: {SOURCE_DIR}") print(f"Backup: {BACKUP_ROOT}") total = 0 total += replace_ios_runner() total += replace_ios_widget() total += replace_macos() total += replace_android() total += replace_ohos() print(f"\n{'='*60}") print(f"Total icons replaced: {total}") print(f"Backup location: {BACKUP_ROOT}") print("Done!") if __name__ == '__main__': main()