From b5a0c83b6028246297ee5973d200cbff044ada40 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ptit=20l=C3=A9gume?=
<67562086+PtitLegume@users.noreply.github.com>
Date: Sat, 8 Aug 2026 20:39:29 +0200
Subject: [PATCH 1/2] feat(mobile): samsung flex window widgets
---
.../android/app/src/main/AndroidManifest.xml | 46 +++++++++++++++++++
.../immich/widget/FlexWindowReceivers.kt | 9 ++++
.../alextran/immich/widget/MemoryReceiver.kt | 4 +-
.../alextran/immich/widget/RandomReceiver.kt | 4 +-
.../main/res/values/attrs_samsung_widget.xml | 9 ++++
.../app/src/main/res/values/strings.xml | 4 ++
.../main/res/xml/memory_widget_flex_2x2.xml | 19 ++++++++
.../main/res/xml/memory_widget_flex_4x2.xml | 19 ++++++++
.../main/res/xml/random_widget_flex_2x2.xml | 21 +++++++++
.../res/xml/samsung_appwidget_sub_screen.xml | 3 ++
mobile/lib/constants/constants.dart | 4 ++
11 files changed, 138 insertions(+), 4 deletions(-)
create mode 100644 mobile/android/app/src/main/kotlin/app/alextran/immich/widget/FlexWindowReceivers.kt
create mode 100644 mobile/android/app/src/main/res/values/attrs_samsung_widget.xml
create mode 100644 mobile/android/app/src/main/res/xml/memory_widget_flex_2x2.xml
create mode 100644 mobile/android/app/src/main/res/xml/memory_widget_flex_4x2.xml
create mode 100644 mobile/android/app/src/main/res/xml/random_widget_flex_2x2.xml
create mode 100644 mobile/android/app/src/main/res/xml/samsung_appwidget_sub_screen.xml
diff --git a/mobile/android/app/src/main/AndroidManifest.xml b/mobile/android/app/src/main/AndroidManifest.xml
index 5f4de8284a..99a1a6e6b6 100644
--- a/mobile/android/app/src/main/AndroidManifest.xml
+++ b/mobile/android/app/src/main/AndroidManifest.xml
@@ -178,6 +178,52 @@
android:resource="@xml/memory_widget" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/FlexWindowReceivers.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/FlexWindowReceivers.kt
new file mode 100644
index 0000000000..aae500270c
--- /dev/null
+++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/FlexWindowReceivers.kt
@@ -0,0 +1,9 @@
+package app.alextran.immich.widget
+
+// Widget providers for the Samsung Flex Window cover screen.
+
+class MemoryReceiverFlex2x2 : MemoryReceiver()
+
+class MemoryReceiverFlex4x2 : MemoryReceiver()
+
+class RandomReceiverFlex2x2 : RandomReceiver()
diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/MemoryReceiver.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/MemoryReceiver.kt
index 63b32eb6f0..aee4398e58 100644
--- a/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/MemoryReceiver.kt
+++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/MemoryReceiver.kt
@@ -11,7 +11,7 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
-class MemoryReceiver : GlanceAppWidgetReceiver() {
+open class MemoryReceiver : GlanceAppWidgetReceiver() {
override val glanceAppWidget = PhotoWidget()
override fun onUpdate(
@@ -28,7 +28,7 @@ class MemoryReceiver : GlanceAppWidgetReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val fromMainApp = intent.getBooleanExtra(HomeWidgetPlugin.TRIGGERED_FROM_HOME_WIDGET, false)
- val provider = ComponentName(context, MemoryReceiver::class.java)
+ val provider = ComponentName(context, javaClass)
val glanceIds = AppWidgetManager.getInstance(context).getAppWidgetIds(provider)
// Launch coroutine to setup a single shot if the app requested the update
diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/RandomReceiver.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/RandomReceiver.kt
index a7662181bc..3d4c581e52 100644
--- a/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/RandomReceiver.kt
+++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/RandomReceiver.kt
@@ -11,7 +11,7 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
-class RandomReceiver : GlanceAppWidgetReceiver() {
+open class RandomReceiver : GlanceAppWidgetReceiver() {
override val glanceAppWidget = PhotoWidget()
override fun onUpdate(
@@ -28,7 +28,7 @@ class RandomReceiver : GlanceAppWidgetReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val fromMainApp = intent.getBooleanExtra(HomeWidgetPlugin.TRIGGERED_FROM_HOME_WIDGET, false)
- val provider = ComponentName(context, RandomReceiver::class.java)
+ val provider = ComponentName(context, javaClass)
val glanceIds = AppWidgetManager.getInstance(context).getAppWidgetIds(provider)
// Launch coroutine to setup a single shot if the app requested the update
diff --git a/mobile/android/app/src/main/res/values/attrs_samsung_widget.xml b/mobile/android/app/src/main/res/values/attrs_samsung_widget.xml
new file mode 100644
index 0000000000..27254c3866
--- /dev/null
+++ b/mobile/android/app/src/main/res/values/attrs_samsung_widget.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
diff --git a/mobile/android/app/src/main/res/values/strings.xml b/mobile/android/app/src/main/res/values/strings.xml
index d543e33116..4897b5bd1e 100644
--- a/mobile/android/app/src/main/res/values/strings.xml
+++ b/mobile/android/app/src/main/res/values/strings.xml
@@ -2,6 +2,10 @@
Memories
Random
+
+ Memories (2x2)
+ Memories (4x2)
+ Random (2x2)
See memories from Immich.
View a random image from your library or a specific album.
diff --git a/mobile/android/app/src/main/res/xml/memory_widget_flex_2x2.xml b/mobile/android/app/src/main/res/xml/memory_widget_flex_2x2.xml
new file mode 100644
index 0000000000..8e0379e14f
--- /dev/null
+++ b/mobile/android/app/src/main/res/xml/memory_widget_flex_2x2.xml
@@ -0,0 +1,19 @@
+
+
+
diff --git a/mobile/android/app/src/main/res/xml/memory_widget_flex_4x2.xml b/mobile/android/app/src/main/res/xml/memory_widget_flex_4x2.xml
new file mode 100644
index 0000000000..40d2faff9c
--- /dev/null
+++ b/mobile/android/app/src/main/res/xml/memory_widget_flex_4x2.xml
@@ -0,0 +1,19 @@
+
+
+
diff --git a/mobile/android/app/src/main/res/xml/random_widget_flex_2x2.xml b/mobile/android/app/src/main/res/xml/random_widget_flex_2x2.xml
new file mode 100644
index 0000000000..791bc03c66
--- /dev/null
+++ b/mobile/android/app/src/main/res/xml/random_widget_flex_2x2.xml
@@ -0,0 +1,21 @@
+
+
+
diff --git a/mobile/android/app/src/main/res/xml/samsung_appwidget_sub_screen.xml b/mobile/android/app/src/main/res/xml/samsung_appwidget_sub_screen.xml
new file mode 100644
index 0000000000..3e7c6a0f27
--- /dev/null
+++ b/mobile/android/app/src/main/res/xml/samsung_appwidget_sub_screen.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/mobile/lib/constants/constants.dart b/mobile/lib/constants/constants.dart
index c2a13c3ec2..3ccab26408 100644
--- a/mobile/lib/constants/constants.dart
+++ b/mobile/lib/constants/constants.dart
@@ -37,9 +37,13 @@ const String kWidgetCustomHeaders = "widget_custom_headers";
// add widget identifiers here for new widgets
// these are used to force a widget refresh
// (iOSName, androidFQDN)
+// Android-only providers reuse the iOS name of their counterpart, updateWidget rejects a null one.
const List<(String, String)> kWidgetNames = [
('com.immich.widget.random', 'app.alextran.immich.widget.RandomReceiver'),
('com.immich.widget.memory', 'app.alextran.immich.widget.MemoryReceiver'),
+ ('com.immich.widget.memory', 'app.alextran.immich.widget.MemoryReceiverFlex2x2'),
+ ('com.immich.widget.memory', 'app.alextran.immich.widget.MemoryReceiverFlex4x2'),
+ ('com.immich.widget.random', 'app.alextran.immich.widget.RandomReceiverFlex2x2'),
];
const int kMinMonthsToEnableScrubberSnap = 12;
From e0caaf39b2e4236f2d1179cd5cf128a87913242d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ptit=20l=C3=A9gume?=
<67562086+PtitLegume@users.noreply.github.com>
Date: Sun, 9 Aug 2026 01:21:51 +0200
Subject: [PATCH 2/2] feat(mobile): add random 4x2 flex window widget
---
.../android/app/src/main/AndroidManifest.xml | 15 +++++++++++++
.../immich/widget/FlexWindowReceivers.kt | 2 ++
.../app/src/main/res/values/strings.xml | 1 +
.../main/res/xml/random_widget_flex_4x2.xml | 21 +++++++++++++++++++
mobile/lib/constants/constants.dart | 1 +
5 files changed, 40 insertions(+)
create mode 100644 mobile/android/app/src/main/res/xml/random_widget_flex_4x2.xml
diff --git a/mobile/android/app/src/main/AndroidManifest.xml b/mobile/android/app/src/main/AndroidManifest.xml
index 99a1a6e6b6..c6f0232bb6 100644
--- a/mobile/android/app/src/main/AndroidManifest.xml
+++ b/mobile/android/app/src/main/AndroidManifest.xml
@@ -224,6 +224,21 @@
android:resource="@xml/samsung_appwidget_sub_screen" />
+
+
+
+
+
+
+
+
diff --git a/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/FlexWindowReceivers.kt b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/FlexWindowReceivers.kt
index aae500270c..fd43f71fcf 100644
--- a/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/FlexWindowReceivers.kt
+++ b/mobile/android/app/src/main/kotlin/app/alextran/immich/widget/FlexWindowReceivers.kt
@@ -7,3 +7,5 @@ class MemoryReceiverFlex2x2 : MemoryReceiver()
class MemoryReceiverFlex4x2 : MemoryReceiver()
class RandomReceiverFlex2x2 : RandomReceiver()
+
+class RandomReceiverFlex4x2 : RandomReceiver()
diff --git a/mobile/android/app/src/main/res/values/strings.xml b/mobile/android/app/src/main/res/values/strings.xml
index 4897b5bd1e..89b6384ae4 100644
--- a/mobile/android/app/src/main/res/values/strings.xml
+++ b/mobile/android/app/src/main/res/values/strings.xml
@@ -6,6 +6,7 @@
Memories (2x2)
Memories (4x2)
Random (2x2)
+ Random (4x2)
See memories from Immich.
View a random image from your library or a specific album.
diff --git a/mobile/android/app/src/main/res/xml/random_widget_flex_4x2.xml b/mobile/android/app/src/main/res/xml/random_widget_flex_4x2.xml
new file mode 100644
index 0000000000..5baa4e01c0
--- /dev/null
+++ b/mobile/android/app/src/main/res/xml/random_widget_flex_4x2.xml
@@ -0,0 +1,21 @@
+
+
+
diff --git a/mobile/lib/constants/constants.dart b/mobile/lib/constants/constants.dart
index 3ccab26408..cadb896c42 100644
--- a/mobile/lib/constants/constants.dart
+++ b/mobile/lib/constants/constants.dart
@@ -44,6 +44,7 @@ const List<(String, String)> kWidgetNames = [
('com.immich.widget.memory', 'app.alextran.immich.widget.MemoryReceiverFlex2x2'),
('com.immich.widget.memory', 'app.alextran.immich.widget.MemoryReceiverFlex4x2'),
('com.immich.widget.random', 'app.alextran.immich.widget.RandomReceiverFlex2x2'),
+ ('com.immich.widget.random', 'app.alextran.immich.widget.RandomReceiverFlex4x2'),
];
const int kMinMonthsToEnableScrubberSnap = 12;