diff --git a/i18n/en.json b/i18n/en.json
index 3741722585..d699d1bf4a 100644
--- a/i18n/en.json
+++ b/i18n/en.json
@@ -566,6 +566,8 @@
"app_icon_midnight": "Midnight",
"app_icon_neon": "Neon",
"app_icon_ocean": "Ocean",
+ "app_icon_retro": "Retro",
+ "app_icon_scenic": "Scenic",
"app_icon_subtitle": "Choose the icon shown on your home screen",
"app_icon_sunset": "Sunset",
"app_icon_title": "App Icon",
diff --git a/mobile/android/app/src/main/AndroidManifest.xml b/mobile/android/app/src/main/AndroidManifest.xml
index be413a113a..4f78cedcd9 100644
--- a/mobile/android/app/src/main/AndroidManifest.xml
+++ b/mobile/android/app/src/main/AndroidManifest.xml
@@ -142,6 +142,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/appicon/AppIconApiImpl.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/appicon/AppIconApiImpl.kt
index 27d45f3136..834d13bdc0 100644
--- a/mobile/android/app/src/main/kotlin/app/alextran/immich/appicon/AppIconApiImpl.kt
+++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/appicon/AppIconApiImpl.kt
@@ -15,7 +15,7 @@ class AppIconApiImpl(context: Context) : AppIconApi {
companion object {
private const val DEFAULT_ICON = "classic"
private val ICONS =
- listOf("classic", "midnight", "ocean", "sunset", "forest", "blossom", "ink", "neon", "gold")
+ listOf("classic", "retro", "scenic", "midnight", "ocean", "sunset", "forest", "blossom", "ink", "neon", "gold")
}
private fun componentFor(iconId: String): ComponentName {
diff --git a/mobile/android/app/src/main/res/drawable/ic_launcher_bg_blossom.xml b/mobile/android/app/src/main/res/drawable/ic_launcher_bg_blossom.xml
new file mode 100644
index 0000000000..efe1ee2462
--- /dev/null
+++ b/mobile/android/app/src/main/res/drawable/ic_launcher_bg_blossom.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/mobile/android/app/src/main/res/drawable/ic_launcher_bg_gold.xml b/mobile/android/app/src/main/res/drawable/ic_launcher_bg_gold.xml
new file mode 100644
index 0000000000..0dd707ea9a
--- /dev/null
+++ b/mobile/android/app/src/main/res/drawable/ic_launcher_bg_gold.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/mobile/android/app/src/main/res/drawable/ic_launcher_bg_midnight.xml b/mobile/android/app/src/main/res/drawable/ic_launcher_bg_midnight.xml
new file mode 100644
index 0000000000..5d4171f5b4
--- /dev/null
+++ b/mobile/android/app/src/main/res/drawable/ic_launcher_bg_midnight.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/mobile/android/app/src/main/res/drawable/ic_launcher_bg_neon.xml b/mobile/android/app/src/main/res/drawable/ic_launcher_bg_neon.xml
new file mode 100644
index 0000000000..5df0d886b7
--- /dev/null
+++ b/mobile/android/app/src/main/res/drawable/ic_launcher_bg_neon.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/mobile/android/app/src/main/res/drawable/ic_launcher_bg_sunset.xml b/mobile/android/app/src/main/res/drawable/ic_launcher_bg_sunset.xml
new file mode 100644
index 0000000000..3370ce8adf
--- /dev/null
+++ b/mobile/android/app/src/main/res/drawable/ic_launcher_bg_sunset.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/mobile/android/app/src/main/res/drawable/ic_launcher_foreground_blossom.xml b/mobile/android/app/src/main/res/drawable/ic_launcher_foreground_blossom.xml
deleted file mode 100644
index 0f3c7dec53..0000000000
--- a/mobile/android/app/src/main/res/drawable/ic_launcher_foreground_blossom.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-
-
-
-
-
-
diff --git a/mobile/android/app/src/main/res/drawable/ic_launcher_foreground_forest.xml b/mobile/android/app/src/main/res/drawable/ic_launcher_foreground_forest.xml
deleted file mode 100644
index e8c8e1fc36..0000000000
--- a/mobile/android/app/src/main/res/drawable/ic_launcher_foreground_forest.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-
-
-
-
-
-
diff --git a/mobile/android/app/src/main/res/drawable/ic_launcher_foreground_gold.xml b/mobile/android/app/src/main/res/drawable/ic_launcher_foreground_gold.xml
deleted file mode 100644
index ef94097c42..0000000000
--- a/mobile/android/app/src/main/res/drawable/ic_launcher_foreground_gold.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-
-
-
-
-
-
diff --git a/mobile/android/app/src/main/res/drawable/ic_launcher_foreground_ink.xml b/mobile/android/app/src/main/res/drawable/ic_launcher_foreground_ink.xml
deleted file mode 100644
index 4f93f9822b..0000000000
--- a/mobile/android/app/src/main/res/drawable/ic_launcher_foreground_ink.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-
-
-
-
-
-
diff --git a/mobile/android/app/src/main/res/drawable/ic_launcher_foreground_midnight.xml b/mobile/android/app/src/main/res/drawable/ic_launcher_foreground_midnight.xml
deleted file mode 100644
index d14fce37ea..0000000000
--- a/mobile/android/app/src/main/res/drawable/ic_launcher_foreground_midnight.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-
-
-
-
-
-
diff --git a/mobile/android/app/src/main/res/drawable/ic_launcher_foreground_neon.xml b/mobile/android/app/src/main/res/drawable/ic_launcher_foreground_neon.xml
deleted file mode 100644
index 088605e0d7..0000000000
--- a/mobile/android/app/src/main/res/drawable/ic_launcher_foreground_neon.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-
-
-
-
-
-
diff --git a/mobile/android/app/src/main/res/drawable/ic_launcher_foreground_ocean.xml b/mobile/android/app/src/main/res/drawable/ic_launcher_foreground_ocean.xml
deleted file mode 100644
index fe02311ab8..0000000000
--- a/mobile/android/app/src/main/res/drawable/ic_launcher_foreground_ocean.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-
-
-
-
-
-
diff --git a/mobile/android/app/src/main/res/drawable/ic_launcher_foreground_sunset.xml b/mobile/android/app/src/main/res/drawable/ic_launcher_foreground_sunset.xml
deleted file mode 100644
index 8cd24718c4..0000000000
--- a/mobile/android/app/src/main/res/drawable/ic_launcher_foreground_sunset.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-
-
-
-
-
-
diff --git a/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_blossom.xml b/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_blossom.xml
index 53f5ea4ea6..5d9a411d19 100644
--- a/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_blossom.xml
+++ b/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_blossom.xml
@@ -1,6 +1,6 @@
-
-
+
+
diff --git a/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_forest.xml b/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_forest.xml
index e1bfcac7c9..a9bfee6b20 100644
--- a/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_forest.xml
+++ b/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_forest.xml
@@ -1,6 +1,6 @@
-
+
diff --git a/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_gold.xml b/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_gold.xml
index 0af836cba0..61e5c2c8d1 100644
--- a/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_gold.xml
+++ b/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_gold.xml
@@ -1,6 +1,6 @@
-
-
+
+
diff --git a/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_ink.xml b/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_ink.xml
index e93e5ab90f..7d62e091e1 100644
--- a/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_ink.xml
+++ b/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_ink.xml
@@ -1,6 +1,6 @@
-
+
diff --git a/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_midnight.xml b/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_midnight.xml
index 7093707247..0b08fc128b 100644
--- a/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_midnight.xml
+++ b/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_midnight.xml
@@ -1,6 +1,6 @@
-
-
+
+
diff --git a/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_neon.xml b/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_neon.xml
index 119a62ba97..fcb501577c 100644
--- a/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_neon.xml
+++ b/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_neon.xml
@@ -1,6 +1,6 @@
-
-
+
+
diff --git a/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_ocean.xml b/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_ocean.xml
index 8d237e8439..680cde8574 100644
--- a/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_ocean.xml
+++ b/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_ocean.xml
@@ -1,6 +1,6 @@
-
+
diff --git a/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_retro.xml b/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_retro.xml
new file mode 100644
index 0000000000..8da663641b
--- /dev/null
+++ b/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_retro.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_scenic.xml b/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_scenic.xml
new file mode 100644
index 0000000000..3a7239791b
--- /dev/null
+++ b/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_scenic.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_sunset.xml b/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_sunset.xml
index 834347457b..d6a8d61b74 100644
--- a/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_sunset.xml
+++ b/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_sunset.xml
@@ -1,6 +1,6 @@
-
-
+
+
diff --git a/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground_blossom.png b/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground_blossom.png
new file mode 100644
index 0000000000..5f065a43f5
Binary files /dev/null and b/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground_blossom.png differ
diff --git a/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground_forest.png b/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground_forest.png
new file mode 100644
index 0000000000..e2168cdd79
Binary files /dev/null and b/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground_forest.png differ
diff --git a/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground_gold.png b/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground_gold.png
new file mode 100644
index 0000000000..7c7e530e49
Binary files /dev/null and b/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground_gold.png differ
diff --git a/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground_ink.png b/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground_ink.png
new file mode 100644
index 0000000000..799f8f7f0b
Binary files /dev/null and b/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground_ink.png differ
diff --git a/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground_midnight.png b/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground_midnight.png
new file mode 100644
index 0000000000..10f4913a4c
Binary files /dev/null and b/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground_midnight.png differ
diff --git a/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground_neon.png b/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground_neon.png
new file mode 100644
index 0000000000..39eb0c0d76
Binary files /dev/null and b/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground_neon.png differ
diff --git a/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground_ocean.png b/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground_ocean.png
new file mode 100644
index 0000000000..24826a2587
Binary files /dev/null and b/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground_ocean.png differ
diff --git a/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground_retro.png b/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground_retro.png
new file mode 100644
index 0000000000..8026106488
Binary files /dev/null and b/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground_retro.png differ
diff --git a/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground_scenic.png b/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground_scenic.png
new file mode 100644
index 0000000000..b4ac8c645d
Binary files /dev/null and b/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground_scenic.png differ
diff --git a/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground_sunset.png b/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground_sunset.png
new file mode 100644
index 0000000000..b30c52f66c
Binary files /dev/null and b/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground_sunset.png differ
diff --git a/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground_blossom.png b/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground_blossom.png
new file mode 100644
index 0000000000..477775c4a7
Binary files /dev/null and b/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground_blossom.png differ
diff --git a/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground_forest.png b/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground_forest.png
new file mode 100644
index 0000000000..fd68c828e4
Binary files /dev/null and b/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground_forest.png differ
diff --git a/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground_gold.png b/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground_gold.png
new file mode 100644
index 0000000000..40b46e7a80
Binary files /dev/null and b/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground_gold.png differ
diff --git a/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground_ink.png b/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground_ink.png
new file mode 100644
index 0000000000..8b5a01d3b2
Binary files /dev/null and b/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground_ink.png differ
diff --git a/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground_midnight.png b/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground_midnight.png
new file mode 100644
index 0000000000..ecb5d20042
Binary files /dev/null and b/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground_midnight.png differ
diff --git a/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground_neon.png b/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground_neon.png
new file mode 100644
index 0000000000..9d59c1f18a
Binary files /dev/null and b/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground_neon.png differ
diff --git a/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground_ocean.png b/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground_ocean.png
new file mode 100644
index 0000000000..9e01273147
Binary files /dev/null and b/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground_ocean.png differ
diff --git a/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground_retro.png b/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground_retro.png
new file mode 100644
index 0000000000..1937510289
Binary files /dev/null and b/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground_retro.png differ
diff --git a/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground_scenic.png b/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground_scenic.png
new file mode 100644
index 0000000000..e7125b7841
Binary files /dev/null and b/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground_scenic.png differ
diff --git a/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground_sunset.png b/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground_sunset.png
new file mode 100644
index 0000000000..2dcc7b4087
Binary files /dev/null and b/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground_sunset.png differ
diff --git a/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground_blossom.png b/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground_blossom.png
new file mode 100644
index 0000000000..2e8ff3cd2b
Binary files /dev/null and b/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground_blossom.png differ
diff --git a/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground_forest.png b/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground_forest.png
new file mode 100644
index 0000000000..0589ed1074
Binary files /dev/null and b/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground_forest.png differ
diff --git a/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground_gold.png b/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground_gold.png
new file mode 100644
index 0000000000..9b19d62dae
Binary files /dev/null and b/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground_gold.png differ
diff --git a/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground_ink.png b/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground_ink.png
new file mode 100644
index 0000000000..eaeb520f23
Binary files /dev/null and b/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground_ink.png differ
diff --git a/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground_midnight.png b/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground_midnight.png
new file mode 100644
index 0000000000..2306dedbae
Binary files /dev/null and b/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground_midnight.png differ
diff --git a/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground_neon.png b/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground_neon.png
new file mode 100644
index 0000000000..4df11eebc6
Binary files /dev/null and b/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground_neon.png differ
diff --git a/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground_ocean.png b/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground_ocean.png
new file mode 100644
index 0000000000..c8dbfc2268
Binary files /dev/null and b/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground_ocean.png differ
diff --git a/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground_retro.png b/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground_retro.png
new file mode 100644
index 0000000000..5d5df05002
Binary files /dev/null and b/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground_retro.png differ
diff --git a/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground_scenic.png b/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground_scenic.png
new file mode 100644
index 0000000000..f4d5a29fd6
Binary files /dev/null and b/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground_scenic.png differ
diff --git a/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground_sunset.png b/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground_sunset.png
new file mode 100644
index 0000000000..59da4da3d8
Binary files /dev/null and b/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground_sunset.png differ
diff --git a/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground_blossom.png b/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground_blossom.png
new file mode 100644
index 0000000000..a51e3a1633
Binary files /dev/null and b/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground_blossom.png differ
diff --git a/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground_forest.png b/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground_forest.png
new file mode 100644
index 0000000000..3b07fd267d
Binary files /dev/null and b/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground_forest.png differ
diff --git a/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground_gold.png b/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground_gold.png
new file mode 100644
index 0000000000..e1fccb25dc
Binary files /dev/null and b/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground_gold.png differ
diff --git a/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground_ink.png b/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground_ink.png
new file mode 100644
index 0000000000..854f9da310
Binary files /dev/null and b/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground_ink.png differ
diff --git a/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground_midnight.png b/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground_midnight.png
new file mode 100644
index 0000000000..eae6ece5fa
Binary files /dev/null and b/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground_midnight.png differ
diff --git a/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground_neon.png b/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground_neon.png
new file mode 100644
index 0000000000..3e71232a9d
Binary files /dev/null and b/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground_neon.png differ
diff --git a/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground_ocean.png b/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground_ocean.png
new file mode 100644
index 0000000000..d86e2ad5d9
Binary files /dev/null and b/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground_ocean.png differ
diff --git a/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground_retro.png b/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground_retro.png
new file mode 100644
index 0000000000..5e5e9b3d2b
Binary files /dev/null and b/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground_retro.png differ
diff --git a/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground_scenic.png b/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground_scenic.png
new file mode 100644
index 0000000000..fb3d05f895
Binary files /dev/null and b/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground_scenic.png differ
diff --git a/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground_sunset.png b/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground_sunset.png
new file mode 100644
index 0000000000..30a691a81d
Binary files /dev/null and b/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground_sunset.png differ
diff --git a/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground_blossom.png b/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground_blossom.png
new file mode 100644
index 0000000000..4b97104fad
Binary files /dev/null and b/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground_blossom.png differ
diff --git a/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground_forest.png b/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground_forest.png
new file mode 100644
index 0000000000..953ac078f3
Binary files /dev/null and b/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground_forest.png differ
diff --git a/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground_gold.png b/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground_gold.png
new file mode 100644
index 0000000000..4cbc320038
Binary files /dev/null and b/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground_gold.png differ
diff --git a/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground_ink.png b/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground_ink.png
new file mode 100644
index 0000000000..75b2e90983
Binary files /dev/null and b/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground_ink.png differ
diff --git a/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground_midnight.png b/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground_midnight.png
new file mode 100644
index 0000000000..be19d6313b
Binary files /dev/null and b/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground_midnight.png differ
diff --git a/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground_neon.png b/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground_neon.png
new file mode 100644
index 0000000000..f5a0c34557
Binary files /dev/null and b/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground_neon.png differ
diff --git a/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground_ocean.png b/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground_ocean.png
new file mode 100644
index 0000000000..ba18f83d01
Binary files /dev/null and b/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground_ocean.png differ
diff --git a/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground_retro.png b/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground_retro.png
new file mode 100644
index 0000000000..ce2ba91940
Binary files /dev/null and b/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground_retro.png differ
diff --git a/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground_scenic.png b/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground_scenic.png
new file mode 100644
index 0000000000..5c83fd0135
Binary files /dev/null and b/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground_scenic.png differ
diff --git a/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground_sunset.png b/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground_sunset.png
new file mode 100644
index 0000000000..088fd93f1f
Binary files /dev/null and b/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground_sunset.png differ
diff --git a/mobile/android/app/src/main/res/values/app_icon_colors.xml b/mobile/android/app/src/main/res/values/app_icon_colors.xml
index 5de795bb4a..6b6f6fdf6b 100644
--- a/mobile/android/app/src/main/res/values/app_icon_colors.xml
+++ b/mobile/android/app/src/main/res/values/app_icon_colors.xml
@@ -1,11 +1,8 @@
- #0F172A
+ #FFFFFF
+ #FFFFFF
#FFFFFF
- #1E1B4B
#FFFFFF
- #FFF1F2
#FFFFFF
- #0A0A12
- #1C1917
diff --git a/mobile/assets/app-icons/app-icon-blossom.png b/mobile/assets/app-icons/app-icon-blossom.png
new file mode 100644
index 0000000000..78c9cb7015
Binary files /dev/null and b/mobile/assets/app-icons/app-icon-blossom.png differ
diff --git a/mobile/assets/app-icons/app-icon-blossom.svg b/mobile/assets/app-icons/app-icon-blossom.svg
deleted file mode 100644
index 430bf7ae40..0000000000
--- a/mobile/assets/app-icons/app-icon-blossom.svg
+++ /dev/null
@@ -1,10 +0,0 @@
-
diff --git a/mobile/assets/app-icons/app-icon-classic.png b/mobile/assets/app-icons/app-icon-classic.png
new file mode 100644
index 0000000000..adc4d0e40c
Binary files /dev/null and b/mobile/assets/app-icons/app-icon-classic.png differ
diff --git a/mobile/assets/app-icons/app-icon-classic.svg b/mobile/assets/app-icons/app-icon-classic.svg
deleted file mode 100644
index 1522e1f348..0000000000
--- a/mobile/assets/app-icons/app-icon-classic.svg
+++ /dev/null
@@ -1,10 +0,0 @@
-
diff --git a/mobile/assets/app-icons/app-icon-forest.png b/mobile/assets/app-icons/app-icon-forest.png
new file mode 100644
index 0000000000..4114f7d8ef
Binary files /dev/null and b/mobile/assets/app-icons/app-icon-forest.png differ
diff --git a/mobile/assets/app-icons/app-icon-forest.svg b/mobile/assets/app-icons/app-icon-forest.svg
deleted file mode 100644
index 9ba3659d1c..0000000000
--- a/mobile/assets/app-icons/app-icon-forest.svg
+++ /dev/null
@@ -1,10 +0,0 @@
-
diff --git a/mobile/assets/app-icons/app-icon-gold.png b/mobile/assets/app-icons/app-icon-gold.png
new file mode 100644
index 0000000000..1c34cb361f
Binary files /dev/null and b/mobile/assets/app-icons/app-icon-gold.png differ
diff --git a/mobile/assets/app-icons/app-icon-gold.svg b/mobile/assets/app-icons/app-icon-gold.svg
deleted file mode 100644
index 58b297d0d6..0000000000
--- a/mobile/assets/app-icons/app-icon-gold.svg
+++ /dev/null
@@ -1,10 +0,0 @@
-
diff --git a/mobile/assets/app-icons/app-icon-ink.png b/mobile/assets/app-icons/app-icon-ink.png
new file mode 100644
index 0000000000..1b0af961e7
Binary files /dev/null and b/mobile/assets/app-icons/app-icon-ink.png differ
diff --git a/mobile/assets/app-icons/app-icon-ink.svg b/mobile/assets/app-icons/app-icon-ink.svg
deleted file mode 100644
index e25b71fed6..0000000000
--- a/mobile/assets/app-icons/app-icon-ink.svg
+++ /dev/null
@@ -1,10 +0,0 @@
-
diff --git a/mobile/assets/app-icons/app-icon-midnight.png b/mobile/assets/app-icons/app-icon-midnight.png
new file mode 100644
index 0000000000..da7f1fc4b9
Binary files /dev/null and b/mobile/assets/app-icons/app-icon-midnight.png differ
diff --git a/mobile/assets/app-icons/app-icon-midnight.svg b/mobile/assets/app-icons/app-icon-midnight.svg
deleted file mode 100644
index 32477fabc9..0000000000
--- a/mobile/assets/app-icons/app-icon-midnight.svg
+++ /dev/null
@@ -1,10 +0,0 @@
-
diff --git a/mobile/assets/app-icons/app-icon-neon.png b/mobile/assets/app-icons/app-icon-neon.png
new file mode 100644
index 0000000000..56cff75181
Binary files /dev/null and b/mobile/assets/app-icons/app-icon-neon.png differ
diff --git a/mobile/assets/app-icons/app-icon-neon.svg b/mobile/assets/app-icons/app-icon-neon.svg
deleted file mode 100644
index 5515def8b4..0000000000
--- a/mobile/assets/app-icons/app-icon-neon.svg
+++ /dev/null
@@ -1,10 +0,0 @@
-
diff --git a/mobile/assets/app-icons/app-icon-ocean.png b/mobile/assets/app-icons/app-icon-ocean.png
new file mode 100644
index 0000000000..f76ddb92d4
Binary files /dev/null and b/mobile/assets/app-icons/app-icon-ocean.png differ
diff --git a/mobile/assets/app-icons/app-icon-ocean.svg b/mobile/assets/app-icons/app-icon-ocean.svg
deleted file mode 100644
index 53967c027e..0000000000
--- a/mobile/assets/app-icons/app-icon-ocean.svg
+++ /dev/null
@@ -1,10 +0,0 @@
-
diff --git a/mobile/assets/app-icons/app-icon-retro.png b/mobile/assets/app-icons/app-icon-retro.png
new file mode 100644
index 0000000000..836ec5396d
Binary files /dev/null and b/mobile/assets/app-icons/app-icon-retro.png differ
diff --git a/mobile/assets/app-icons/app-icon-scenic.png b/mobile/assets/app-icons/app-icon-scenic.png
new file mode 100644
index 0000000000..e37f190eee
Binary files /dev/null and b/mobile/assets/app-icons/app-icon-scenic.png differ
diff --git a/mobile/assets/app-icons/app-icon-sunset.png b/mobile/assets/app-icons/app-icon-sunset.png
new file mode 100644
index 0000000000..a2d7ea111a
Binary files /dev/null and b/mobile/assets/app-icons/app-icon-sunset.png differ
diff --git a/mobile/assets/app-icons/app-icon-sunset.svg b/mobile/assets/app-icons/app-icon-sunset.svg
deleted file mode 100644
index 08af89d78d..0000000000
--- a/mobile/assets/app-icons/app-icon-sunset.svg
+++ /dev/null
@@ -1,10 +0,0 @@
-
diff --git a/mobile/ios/Runner/Assets.xcassets/AppIconBlossom.appiconset/1024.png b/mobile/ios/Runner/Assets.xcassets/AppIconBlossom.appiconset/1024.png
index 3284a462ed..f714530ffd 100644
Binary files a/mobile/ios/Runner/Assets.xcassets/AppIconBlossom.appiconset/1024.png and b/mobile/ios/Runner/Assets.xcassets/AppIconBlossom.appiconset/1024.png differ
diff --git a/mobile/ios/Runner/Assets.xcassets/AppIconForest.appiconset/1024.png b/mobile/ios/Runner/Assets.xcassets/AppIconForest.appiconset/1024.png
index 14a58f905a..0458043638 100644
Binary files a/mobile/ios/Runner/Assets.xcassets/AppIconForest.appiconset/1024.png and b/mobile/ios/Runner/Assets.xcassets/AppIconForest.appiconset/1024.png differ
diff --git a/mobile/ios/Runner/Assets.xcassets/AppIconGold.appiconset/1024.png b/mobile/ios/Runner/Assets.xcassets/AppIconGold.appiconset/1024.png
index 77f3d92f01..e281e0dfd1 100644
Binary files a/mobile/ios/Runner/Assets.xcassets/AppIconGold.appiconset/1024.png and b/mobile/ios/Runner/Assets.xcassets/AppIconGold.appiconset/1024.png differ
diff --git a/mobile/ios/Runner/Assets.xcassets/AppIconInk.appiconset/1024.png b/mobile/ios/Runner/Assets.xcassets/AppIconInk.appiconset/1024.png
index fb5326fc00..940288ae4d 100644
Binary files a/mobile/ios/Runner/Assets.xcassets/AppIconInk.appiconset/1024.png and b/mobile/ios/Runner/Assets.xcassets/AppIconInk.appiconset/1024.png differ
diff --git a/mobile/ios/Runner/Assets.xcassets/AppIconMidnight.appiconset/1024.png b/mobile/ios/Runner/Assets.xcassets/AppIconMidnight.appiconset/1024.png
index 39898a6751..b7a738edaf 100644
Binary files a/mobile/ios/Runner/Assets.xcassets/AppIconMidnight.appiconset/1024.png and b/mobile/ios/Runner/Assets.xcassets/AppIconMidnight.appiconset/1024.png differ
diff --git a/mobile/ios/Runner/Assets.xcassets/AppIconNeon.appiconset/1024.png b/mobile/ios/Runner/Assets.xcassets/AppIconNeon.appiconset/1024.png
index e1468ee621..7e8a79463a 100644
Binary files a/mobile/ios/Runner/Assets.xcassets/AppIconNeon.appiconset/1024.png and b/mobile/ios/Runner/Assets.xcassets/AppIconNeon.appiconset/1024.png differ
diff --git a/mobile/ios/Runner/Assets.xcassets/AppIconOcean.appiconset/1024.png b/mobile/ios/Runner/Assets.xcassets/AppIconOcean.appiconset/1024.png
index 6f46bafb40..56662fad60 100644
Binary files a/mobile/ios/Runner/Assets.xcassets/AppIconOcean.appiconset/1024.png and b/mobile/ios/Runner/Assets.xcassets/AppIconOcean.appiconset/1024.png differ
diff --git a/mobile/ios/Runner/Assets.xcassets/AppIconRetro.appiconset/1024.png b/mobile/ios/Runner/Assets.xcassets/AppIconRetro.appiconset/1024.png
new file mode 100644
index 0000000000..15be678ffc
Binary files /dev/null and b/mobile/ios/Runner/Assets.xcassets/AppIconRetro.appiconset/1024.png differ
diff --git a/mobile/ios/Runner/Assets.xcassets/AppIconRetro.appiconset/Contents.json b/mobile/ios/Runner/Assets.xcassets/AppIconRetro.appiconset/Contents.json
new file mode 100644
index 0000000000..7afd6df765
--- /dev/null
+++ b/mobile/ios/Runner/Assets.xcassets/AppIconRetro.appiconset/Contents.json
@@ -0,0 +1,14 @@
+{
+ "images": [
+ {
+ "filename": "1024.png",
+ "idiom": "universal",
+ "platform": "ios",
+ "size": "1024x1024"
+ }
+ ],
+ "info": {
+ "author": "xcode",
+ "version": 1
+ }
+}
diff --git a/mobile/ios/Runner/Assets.xcassets/AppIconScenic.appiconset/1024.png b/mobile/ios/Runner/Assets.xcassets/AppIconScenic.appiconset/1024.png
new file mode 100644
index 0000000000..c0e068fac7
Binary files /dev/null and b/mobile/ios/Runner/Assets.xcassets/AppIconScenic.appiconset/1024.png differ
diff --git a/mobile/ios/Runner/Assets.xcassets/AppIconScenic.appiconset/Contents.json b/mobile/ios/Runner/Assets.xcassets/AppIconScenic.appiconset/Contents.json
new file mode 100644
index 0000000000..7afd6df765
--- /dev/null
+++ b/mobile/ios/Runner/Assets.xcassets/AppIconScenic.appiconset/Contents.json
@@ -0,0 +1,14 @@
+{
+ "images": [
+ {
+ "filename": "1024.png",
+ "idiom": "universal",
+ "platform": "ios",
+ "size": "1024x1024"
+ }
+ ],
+ "info": {
+ "author": "xcode",
+ "version": 1
+ }
+}
diff --git a/mobile/ios/Runner/Assets.xcassets/AppIconSunset.appiconset/1024.png b/mobile/ios/Runner/Assets.xcassets/AppIconSunset.appiconset/1024.png
index 07cc6dc417..80c74391dc 100644
Binary files a/mobile/ios/Runner/Assets.xcassets/AppIconSunset.appiconset/1024.png and b/mobile/ios/Runner/Assets.xcassets/AppIconSunset.appiconset/1024.png differ
diff --git a/mobile/lib/constants/app_icons.dart b/mobile/lib/constants/app_icons.dart
index 8d10664167..4df1e555ff 100644
--- a/mobile/lib/constants/app_icons.dart
+++ b/mobile/lib/constants/app_icons.dart
@@ -3,6 +3,8 @@
/// alternate icon asset names), so renaming a value is a breaking change.
enum AppIconVariant {
classic,
+ retro,
+ scenic,
midnight,
ocean,
sunset,
@@ -12,7 +14,7 @@ enum AppIconVariant {
neon,
gold;
- String get assetPath => 'assets/app-icons/app-icon-$name.svg';
+ String get assetPath => 'assets/app-icons/app-icon-$name.png';
String get translationKey => 'app_icon_$name';
diff --git a/mobile/lib/widgets/settings/preference_settings/app_icon_setting.dart b/mobile/lib/widgets/settings/preference_settings/app_icon_setting.dart
index 55f6e3b62a..01f9c84f9f 100644
--- a/mobile/lib/widgets/settings/preference_settings/app_icon_setting.dart
+++ b/mobile/lib/widgets/settings/preference_settings/app_icon_setting.dart
@@ -1,6 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
-import 'package:flutter_svg/flutter_svg.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/constants/app_icons.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
@@ -108,7 +107,7 @@ class _AppIconTile extends StatelessWidget {
),
child: ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(13)),
- child: SvgPicture.asset(variant.assetPath, width: 64, height: 64),
+ child: Image.asset(variant.assetPath, width: 64, height: 64),
),
),
if (isSelected)
diff --git a/mobile/scripts/generate_app_icons.py b/mobile/scripts/generate_app_icons.py
index f8cd8f46d7..b24e391280 100644
--- a/mobile/scripts/generate_app_icons.py
+++ b/mobile/scripts/generate_app_icons.py
@@ -1,14 +1,28 @@
#!/usr/bin/env python3
"""Generates the alternate app icon assets for Android and iOS.
-Recolors the five petals of the Immich logo for each icon variant and emits:
- - assets/app-icons/app-icon-.svg (in-app previews)
- - android res: drawable/ic_launcher_foreground_.xml,
- mipmap-anydpi-v26/ic_launcher_.xml,
- values/app_icon_colors.xml
+Variant families:
+ - gradient: the five petals of the Immich logo dressed in a center-out
+ radial gradient (bright cores fading to deep tips on light grounds, dark
+ bases flaring to glowing tips on dark grounds) with a soft drop shadow
+ under every petal, over a flat or gradient background.
+ - retro: a pixel-art take on the flower, after the "Retro Magnet" from
+ immich.store (jagged pixel petals with darker pixel outlines).
+ - scenic: a tiny landscape painted inside each petal, after the
+ "Landscape Logo Magnet" from immich.store (sunset mountains, dusk
+ skyline, desert dunes, moonlit ocean, misty pines).
+
+Because neither Android vector drawables nor flutter_svg can render blur
+filters, everything the shadows touch ships as rendered PNGs. Emits:
+
+ - assets/app-icons/app-icon-.png (in-app previews)
+ - android res: mipmap-/ic_launcher_foreground_.png,
+ drawable/ic_launcher_bg_.xml (gradient grounds),
+ values/app_icon_colors.xml (flat grounds),
+ mipmap-anydpi-v26/ic_launcher_.xml
- ios: Assets.xcassets/AppIcon.appiconset/{Contents.json,1024.png}
-The iOS PNGs are rendered with macOS `qlmanage`, so this script must run on
+The PNGs are rendered with macOS `qlmanage`, so this script must run on
macOS. Run it from anywhere: `python3 mobile/scripts/generate_app_icons.py`.
"""
@@ -28,90 +42,158 @@ MOBILE = Path(__file__).resolve().parent.parent
# top (red), upper-left (pink), upper-right (yellow),
# lower-left (blue), lower-right (green)
BASE_PETALS = ["#FA2921", "#ED79B5", "#FFB400", "#1E83F7", "#18C249"]
-BASE_BACKGROUND = "#FFFFFF"
-# id -> (asset suffix, background, petal colors in BASE_PETALS order).
-# "classic" is the default icon and only gets a preview SVG; its native
-# assets are the stock ic_launcher / AppIcon.
+# The flower fills the logo's 192-unit viewBox; icons inset it to 78% (the
+# padding of the original iOS icon) and the Android 108dp adaptive
+# foreground to 27.55% (the geometry of the stock ic_launcher_foreground).
+LOGO = 192
+ICON_SCALE = 0.78
+DROID_CANVAS = 108
+DROID_SCALE = 0.2755
+
+# Petal pixels only exist beyond the flower's hollow core, so the first
+# radial stop sits partway out to keep the visible ramp full-range.
+INNER_STOP = 0.18
+SHADOW = ''
+
+# Adaptive icon foreground densities (108dp layer).
+DENSITIES = {"mdpi": 108, "hdpi": 162, "xhdpi": 216, "xxhdpi": 324, "xxxhdpi": 432}
+
+# Backgrounds: ("solid", color) | ("linear", start, end) diagonal top-left ->
+# bottom-right | ("vertical", top, bottom) | ("radial", center, edge).
+# Petals: five (inner, outer) gradient stop pairs in BASE_PETALS order.
VARIANTS = {
- "classic": (BASE_BACKGROUND, BASE_PETALS),
- "midnight": ("#0F172A", BASE_PETALS),
- "ocean": ("#FFFFFF", ["#0284C7", "#38BDF8", "#1D4ED8", "#0891B2", "#14B8A6"]),
- "sunset": ("#1E1B4B", ["#F97316", "#7C3AED", "#FACC15", "#C026D3", "#F43F5E"]),
- "forest": ("#FFFFFF", ["#16A34A", "#84CC16", "#4ADE80", "#15803D", "#65A30D"]),
- "blossom": ("#FFF1F2", ["#E11D48", "#FB7185", "#F472B6", "#BE185D", "#F9A8D4"]),
- "ink": ("#FFFFFF", ["#111827", "#6B7280", "#374151", "#9CA3AF", "#4B5563"]),
- "neon": ("#0A0A12", ["#FF2D95", "#00E5FF", "#FFD600", "#7C4DFF", "#00FF9D"]),
- "gold": ("#1C1917", ["#FBBF24", "#FDE68A", "#F59E0B", "#D97706", "#FCD34D"]),
+ "classic": {
+ "bg": ("solid", "#FFFFFF"),
+ "petals": [(c, c) for c in BASE_PETALS], # the untouched default
+ "shadow": 0,
+ },
+ "retro": {"bg": ("solid", "#FFFFFF"), "kind": "retro", "shadow": 0},
+ "scenic": {"bg": ("solid", "#FFFFFF"), "kind": "scenic", "shadow": 0.22},
+ "midnight": {
+ "bg": ("linear", "#1B2A4A", "#0A0F1E"),
+ "petals": [
+ ("#B3170A", "#FF6A4D"),
+ ("#C2408D", "#FF9DD6"),
+ ("#C77F00", "#FFD54D"),
+ ("#0F5BD1", "#5CB2FF"),
+ ("#0E8A33", "#4DE87A"),
+ ],
+ },
+ "ocean": {
+ "bg": ("solid", "#FFFFFF"),
+ "petals": [
+ ("#7DD3FC", "#0369A1"),
+ ("#A5F3FC", "#0891B2"),
+ ("#93C5FD", "#1D4ED8"),
+ ("#67E8F9", "#155E75"),
+ ("#5EEAD4", "#0F766E"),
+ ],
+ },
+ "sunset": {
+ "bg": ("vertical", "#3B1D6E", "#150A38"),
+ "petals": [
+ ("#C2410C", "#FDBA74"),
+ ("#6D28D9", "#C4B5FD"),
+ ("#A16207", "#FDE047"),
+ ("#A21CAF", "#F0ABFC"),
+ ("#BE123C", "#FDA4AF"),
+ ],
+ },
+ "forest": {
+ "bg": ("solid", "#FFFFFF"),
+ "petals": [
+ ("#86EFAC", "#15803D"),
+ ("#D9F99D", "#4D7C0F"),
+ ("#6EE7B7", "#047857"),
+ ("#BBF7D0", "#166534"),
+ ("#BEF264", "#3F6212"),
+ ],
+ },
+ "blossom": {
+ "bg": ("vertical", "#FFF1F2", "#FECDD3"),
+ "petals": [
+ ("#FDA4AF", "#BE123C"),
+ ("#FBCFE8", "#DB2777"),
+ ("#F9A8D4", "#9D174D"),
+ ("#FECDD3", "#E11D48"),
+ ("#F5D0FE", "#A21CAF"),
+ ],
+ },
+ "ink": {
+ "bg": ("solid", "#FFFFFF"),
+ "petals": [
+ ("#94A3B8", "#0F172A"),
+ ("#CBD5E1", "#475569"),
+ ("#B0BCCC", "#1E293B"),
+ ("#E2E8F0", "#64748B"),
+ ("#9AA6B8", "#334155"),
+ ],
+ },
+ "neon": {
+ "bg": ("radial", "#1A1033", "#050208"),
+ "petals": [
+ ("#B3005E", "#FF4DA6"),
+ ("#0090B3", "#33F0FF"),
+ ("#B39B00", "#FFF04D"),
+ ("#6600CC", "#B266FF"),
+ ("#00B36B", "#4DFFB8"),
+ ],
+ },
+ "gold": {
+ "bg": ("linear", "#2B2118", "#141009"),
+ "petals": [
+ ("#92600A", "#FFD966"),
+ ("#B8860B", "#FFF0B3"),
+ ("#A5680A", "#FFC933"),
+ ("#7A4E06", "#E6A817"),
+ ("#9C6F0C", "#FFE080"),
+ ],
+ },
}
-def recolor(text: str, petals: list[str]) -> str:
- for base, new in zip(BASE_PETALS, petals):
- pattern = re.compile(re.escape(base), re.IGNORECASE)
- text = pattern.sub(new, text)
- return text
+def is_dark(color: str) -> bool:
+ r, g, b = (int(color[i : i + 2], 16) for i in (1, 3, 5))
+ return 0.299 * r + 0.587 * g + 0.114 * b < 128
-def petal_paths() -> str:
+def darken(color: str, factor: float) -> str:
+ r, g, b = (int(color[i : i + 2], 16) for i in (1, 3, 5))
+ return f"#{int(r * factor):02X}{int(g * factor):02X}{int(b * factor):02X}"
+
+
+def petal_path_data() -> list[str]:
svg = (MOBILE / "assets" / "immich-logo.svg").read_text()
- return "\n".join(re.findall(r"]*/>", svg))
+ return re.findall(r' str:
- # The flower spans the full 192px viewBox; scale it down so the icon
- # keeps the same padding as the original iOS app icon.
- scale = 0.78
- offset = 192 * (1 - scale) / 2
- paths = recolor(petal_paths(), petals)
- return f"""
-"""
+# ---------------------------------------------------------------------------
+# PNG helpers (qlmanage rendering + minimal decode/encode)
+# ---------------------------------------------------------------------------
-def write_previews() -> None:
- out = MOBILE / "assets" / "app-icons"
- out.mkdir(exist_ok=True)
- for variant, (background, petals) in VARIANTS.items():
- (out / f"app-icon-{variant}.svg").write_text(icon_svg(background, petals, 192))
- print(f"previews: {out}")
-
-
-def write_android() -> None:
- res = MOBILE / "android" / "app" / "src" / "main" / "res"
- foreground = (res / "drawable" / "ic_launcher_foreground.xml").read_text()
-
- colors = ['', ""]
- for variant, (background, petals) in VARIANTS.items():
- if variant == "classic":
- continue
- (res / "drawable" / f"ic_launcher_foreground_{variant}.xml").write_text(
- recolor(foreground, petals)
+def render_svg(svg_text: str, size: int) -> bytes:
+ """Renders an SVG (whose root width/height should equal size) to PNG."""
+ with tempfile.TemporaryDirectory() as tmp_dir:
+ svg = Path(tmp_dir) / "render.svg"
+ svg.write_text(svg_text)
+ subprocess.run(
+ ["qlmanage", "-t", "-s", str(size), "-o", tmp_dir, str(svg)],
+ check=True,
+ capture_output=True,
)
- colors.append(f' {background}')
- (res / "mipmap-anydpi-v26" / f"ic_launcher_{variant}.xml").write_text(
- f"""
-
-
-
-
-
-"""
- )
- colors.append("")
- (res / "values" / "app_icon_colors.xml").write_text("\n".join(colors) + "\n")
- print(f"android res: {res}")
+ rendered = Path(tmp_dir) / "render.svg.png"
+ if not rendered.exists():
+ sys.exit("qlmanage failed to render SVG")
+ return rendered.read_bytes()
-def strip_alpha(png: bytes) -> bytes:
- """Re-encodes an RGBA PNG as opaque RGB (required for app icons)."""
+def decode_png(png: bytes) -> tuple[int, int, list[bytearray]]:
+ """Decodes an 8-bit RGB(A) PNG into rows of RGBA bytes."""
assert png[:8] == b"\x89PNG\r\n\x1a\n"
- pos, chunks, idat = 8, [], b""
- width = height = bit_depth = color_type = None
+ pos, idat = 8, b""
+ width = height = color_type = None
while pos < len(png):
(length,) = struct.unpack(">I", png[pos : pos + 4])
kind = png[pos + 4 : pos + 8]
@@ -119,25 +201,22 @@ def strip_alpha(png: bytes) -> bytes:
pos += 12 + length
if kind == b"IHDR":
width, height, bit_depth, color_type = struct.unpack(">IIBB", data[:10])
+ assert bit_depth == 8 and color_type in (2, 6), "expected 8-bit RGB(A)"
if kind == b"IDAT":
idat += data
- else:
- chunks.append((kind, data))
- if color_type != 6 or bit_depth != 8:
- return png # already opaque
-
+ channels = 4 if color_type == 6 else 3
raw = zlib.decompress(idat)
- stride = width * 4
- rgb = bytearray()
- previous = bytearray(width * 4)
+ stride = width * channels
+ rows = []
+ previous = bytearray(stride)
for y in range(height):
row_start = y * (stride + 1)
filter_type = raw[row_start]
row = bytearray(raw[row_start + 1 : row_start + 1 + stride])
for x in range(stride):
- a = row[x - 4] if x >= 4 else 0
+ a = row[x - channels] if x >= channels else 0
b = previous[x]
- c = previous[x - 4] if x >= 4 else 0
+ c = previous[x - channels] if x >= channels else 0
if filter_type == 1:
row[x] = (row[x] + a) & 0xFF
elif filter_type == 2:
@@ -150,10 +229,20 @@ def strip_alpha(png: bytes) -> bytes:
pred = a if pa <= pb and pa <= pc else b if pb <= pc else c
row[x] = (row[x] + pred) & 0xFF
previous = row
+ if channels == 3:
+ rows.append(bytearray(b"".join(row[x * 3 : x * 3 + 3] + b"\xff" for x in range(width))))
+ else:
+ rows.append(row)
+ return width, height, rows
+
+
+def encode_png_rgb(width: int, height: int, rows: list[bytearray]) -> bytes:
+ """Encodes RGBA rows as an opaque RGB PNG (app icons must have no alpha)."""
+ rgb = bytearray()
+ for row in rows:
rgb.append(0)
for x in range(width):
rgb += row[x * 4 : x * 4 + 3]
-
out = bytearray(b"\x89PNG\r\n\x1a\n")
def chunk(kind: bytes, data: bytes) -> None:
@@ -168,45 +257,336 @@ def strip_alpha(png: bytes) -> bytes:
return bytes(out)
+def strip_alpha(png: bytes) -> bytes:
+ width, height, rows = decode_png(png)
+ return encode_png_rgb(width, height, rows)
+
+
+# ---------------------------------------------------------------------------
+# Flower bodies (drawn in the logo's 192-unit space)
+# ---------------------------------------------------------------------------
+
+
+def gradient_body(variant: dict) -> tuple[str, str]:
+ """Returns (defs, body) for a gradient/flat petal flower."""
+ defs, paths = [], []
+ shadow = variant.get("shadow", 0.28 if not is_dark(variant["bg"][1]) else 0.5)
+ if shadow:
+ defs.append(SHADOW.format(opacity=shadow))
+ shadow_attr = ' filter="url(#ps)"' if shadow else ""
+ for index, (data, (inner, outer)) in enumerate(zip(petal_path_data(), variant["petals"])):
+ if inner == outer:
+ fill = inner
+ else:
+ fill = f"url(#p{index})"
+ defs.append(
+ f''
+ f''
+ f''
+ f""
+ )
+ paths.append(f'')
+ return "".join(defs), "\n".join(paths)
+
+
+def retro_body(grid: int = 30) -> tuple[str, str]:
+ """Pixel-art flower: sample the logo on a coarse grid, outline the edges."""
+ scale = 10
+ # Render over a magenta chroma key: qlmanage does not reliably preserve
+ # alpha, so background cells are detected by color instead.
+ key = "#FF00FF"
+ paths = "\n".join(f'' for d, c in zip(petal_path_data(), BASE_PETALS))
+ svg = (
+ f''
+ )
+ width, height, rows = decode_png(render_svg(svg, grid * scale))
+ assert width == height == grid * scale
+
+ targets = [tuple(int(c[i : i + 2], 16) for i in (1, 3, 5)) for c in BASE_PETALS + [key]]
+
+ def classify(gx: int, gy: int) -> int | None:
+ px = (gx * scale + scale // 2) * 4
+ row = rows[gy * scale + scale // 2]
+ r, g, b, a = row[px : px + 4]
+ if a < 128:
+ return None
+ distances = [(r - t[0]) ** 2 + (g - t[1]) ** 2 + (b - t[2]) ** 2 for t in targets]
+ nearest = distances.index(min(distances))
+ # Anti-aliased blends between petals match nothing well; drop them so
+ # the petals stay separated like the magnet art.
+ if nearest == 5 or distances[nearest] > 4000:
+ return None
+ return nearest
+
+ cells = [[classify(x, y) for x in range(grid)] for y in range(grid)]
+ cell_size = LOGO / grid
+
+ def cell_color(x: int, y: int) -> str | None:
+ petal = cells[y][x]
+ if petal is None:
+ return None
+ edge = any(
+ nx < 0 or ny < 0 or nx >= grid or ny >= grid or cells[ny][nx] != petal
+ for nx, ny in ((x - 1, y), (x + 1, y), (x, y - 1), (x, y + 1))
+ )
+ return darken(BASE_PETALS[petal], 0.6) if edge else BASE_PETALS[petal]
+
+ # Merge horizontal runs of the same color into one rect (with a slight
+ # vertical overlap) so no hairline seams show between cells.
+ rects = []
+ for y in range(grid):
+ x = 0
+ while x < grid:
+ color = cell_color(x, y)
+ if color is None:
+ x += 1
+ continue
+ run = x
+ while run < grid and cell_color(run, y) == color:
+ run += 1
+ rects.append(
+ f''
+ )
+ x = run
+ return "", "\n".join(rects)
+
+
+def scenic_body(shadow: float) -> tuple[str, str]:
+ """A miniature landscape clipped inside each petal, after the store magnet."""
+
+ def sky(name: str, top: str, bottom: str, y1: float, y2: float) -> str:
+ return (
+ f''
+ f''
+ )
+
+ defs = [SHADOW.format(opacity=shadow) if shadow else ""]
+ defs += [
+ sky("sky-peak", "#FFB35C", "#E8402B", 3, 93),
+ sky("sky-city", "#F7C9DE", "#D9679F", 34, 120),
+ sky("sky-dune", "#FFE99A", "#F0AE3C", 33, 123),
+ sky("sky-sea", "#4A6DB5", "#1D3160", 77, 190),
+ sky("sky-pine", "#A8D8B4", "#1F5C3D", 119, 190),
+ ]
+ for index, data in enumerate(petal_path_data()):
+ defs.append(f'')
+
+ scenes = [
+ # top petal: sunset over mountains
+ """
+
+
+""",
+ # upper-left petal: city skyline at dusk
+ """
+
+
+""",
+ # upper-right petal: desert dunes
+ """
+
+
+""",
+ # lower-left petal: moonlit sea
+ """
+
+
+
+""",
+ # lower-right petal: misty pines
+ """
+
+
+""",
+ ]
+
+ shadow_attr = ' filter="url(#ps)"' if shadow else ""
+ body = []
+ for index, scene in enumerate(scenes):
+ body.append(f'\n{scene}\n')
+ return "".join(defs), "\n".join(body)
+
+
+def flower_body(variant: dict) -> tuple[str, str]:
+ kind = variant.get("kind", "gradient")
+ if kind == "retro":
+ return retro_body()
+ if kind == "scenic":
+ return scenic_body(variant.get("shadow", 0.22))
+ return gradient_body(variant)
+
+
+# ---------------------------------------------------------------------------
+# Canvas assembly
+# ---------------------------------------------------------------------------
+
+
+def bg_svg(bg: tuple) -> tuple[str, str]:
+ """Returns (defs snippet, fill ref) for the icon background."""
+ kind = bg[0]
+ if kind == "solid":
+ return "", bg[1]
+ if kind == "radial":
+ defs = (
+ f''
+ f''
+ f""
+ )
+ else:
+ x2, y2 = ("192", "192") if kind == "linear" else ("0", "192")
+ defs = (
+ f''
+ f''
+ f""
+ )
+ return defs, "url(#bg)"
+
+
+def icon_svg(variant: dict, size: int) -> str:
+ """Full square icon: background + inset flower."""
+ offset = LOGO * (1 - ICON_SCALE) / 2
+ bg_defs, bg_fill = bg_svg(variant["bg"])
+ defs, body = flower_body(variant)
+ return f"""
+"""
+
+
+def foreground_svg(variant: dict, size: int) -> str:
+ """Android adaptive-icon foreground layer (transparent, 108dp geometry)."""
+ offset = (DROID_CANVAS - LOGO * DROID_SCALE) / 2
+ defs, body = flower_body(variant)
+ return f"""
+"""
+
+
+# ---------------------------------------------------------------------------
+# Outputs
+# ---------------------------------------------------------------------------
+
+
+def write_previews() -> None:
+ out = MOBILE / "assets" / "app-icons"
+ out.mkdir(exist_ok=True)
+ for stale in out.glob("app-icon-*.svg"):
+ stale.unlink()
+ for variant_id, variant in VARIANTS.items():
+ (out / f"app-icon-{variant_id}.png").write_bytes(render_svg(icon_svg(variant, 384), 384))
+ print(f"previews: {out}")
+
+
+def android_bg_gradient(bg: tuple) -> str:
+ if bg[0] == "radial":
+ geometry = 'android:type="radial" android:centerX="54" android:centerY="54" android:gradientRadius="77"'
+ elif bg[0] == "vertical":
+ geometry = 'android:type="linear" android:startX="0" android:startY="0" android:endX="0" android:endY="108"'
+ else:
+ geometry = 'android:type="linear" android:startX="0" android:startY="0" android:endX="108" android:endY="108"'
+ return f"""
+
+
+
+
+
+
+
+
+
+
+"""
+
+
+def write_android() -> None:
+ res = MOBILE / "android" / "app" / "src" / "main" / "res"
+ for stale in (res / "drawable").glob("ic_launcher_foreground_*.xml"):
+ stale.unlink()
+ colors = ['', ""]
+ for variant_id, variant in VARIANTS.items():
+ if variant_id == "classic":
+ continue
+
+ base = render_svg(foreground_svg(variant, DENSITIES["xxxhdpi"]), DENSITIES["xxxhdpi"])
+ with tempfile.TemporaryDirectory() as tmp_dir:
+ base_png = Path(tmp_dir) / "fg.png"
+ base_png.write_bytes(base)
+ for density, pixels in DENSITIES.items():
+ target = res / f"mipmap-{density}" / f"ic_launcher_foreground_{variant_id}.png"
+ if pixels == DENSITIES["xxxhdpi"]:
+ target.write_bytes(base)
+ else:
+ subprocess.run(
+ ["sips", "-z", str(pixels), str(pixels), str(base_png), "--out", str(target)],
+ check=True,
+ capture_output=True,
+ )
+
+ bg = variant["bg"]
+ if bg[0] == "solid":
+ colors.append(f' {bg[1]}')
+ background_ref = f"@color/ic_launcher_background_{variant_id}"
+ (res / "drawable" / f"ic_launcher_bg_{variant_id}.xml").unlink(missing_ok=True)
+ else:
+ (res / "drawable" / f"ic_launcher_bg_{variant_id}.xml").write_text(android_bg_gradient(bg))
+ background_ref = f"@drawable/ic_launcher_bg_{variant_id}"
+
+ (res / "mipmap-anydpi-v26" / f"ic_launcher_{variant_id}.xml").write_text(
+ f"""
+
+
+
+
+
+"""
+ )
+ colors.append("")
+ (res / "values" / "app_icon_colors.xml").write_text("\n".join(colors) + "\n")
+ print(f"android res: {res}")
+
+
def write_ios() -> None:
xcassets = MOBILE / "ios" / "Runner" / "Assets.xcassets"
- with tempfile.TemporaryDirectory() as tmp_dir:
- tmp = Path(tmp_dir)
- for variant, (background, petals) in VARIANTS.items():
- if variant == "classic":
- continue
- svg = tmp / f"{variant}.svg"
- svg.write_text(icon_svg(background, petals, 1024))
- subprocess.run(
- ["qlmanage", "-t", "-s", "1024", "-o", tmp_dir, str(svg)],
- check=True,
- capture_output=True,
- )
- rendered = tmp / f"{variant}.svg.png"
- if not rendered.exists():
- sys.exit(f"qlmanage failed to render {svg}")
-
- iconset = xcassets / f"AppIcon{variant.capitalize()}.appiconset"
- shutil.rmtree(iconset, ignore_errors=True)
- iconset.mkdir()
- (iconset / "1024.png").write_bytes(strip_alpha(rendered.read_bytes()))
- (iconset / "Contents.json").write_text(
- json.dumps(
- {
- "images": [
- {
- "filename": "1024.png",
- "idiom": "universal",
- "platform": "ios",
- "size": "1024x1024",
- }
- ],
- "info": {"author": "xcode", "version": 1},
- },
- indent=2,
- )
- + "\n"
+ for variant_id, variant in VARIANTS.items():
+ if variant_id == "classic":
+ continue
+ iconset = xcassets / f"AppIcon{variant_id.capitalize()}.appiconset"
+ shutil.rmtree(iconset, ignore_errors=True)
+ iconset.mkdir()
+ (iconset / "1024.png").write_bytes(strip_alpha(render_svg(icon_svg(variant, 1024), 1024)))
+ (iconset / "Contents.json").write_text(
+ json.dumps(
+ {
+ "images": [
+ {
+ "filename": "1024.png",
+ "idiom": "universal",
+ "platform": "ios",
+ "size": "1024x1024",
+ }
+ ],
+ "info": {"author": "xcode", "version": 1},
+ },
+ indent=2,
)
+ + "\n"
+ )
print(f"ios appiconsets: {xcassets}")