From 5b2d7bb13c2fe3a48d5de6e451b0e5f944cad7d1 Mon Sep 17 00:00:00 2001 From: Sembauke Date: Fri, 31 Jul 2026 14:50:16 +0200 Subject: [PATCH 1/3] [quick_actions_android] Return null from getLaunchAction when no activity is attached getLaunchAction threw quick_action_getlaunchaction_no_activity whenever the plugin had no attached activity, which made QuickActionsAndroid.initialize throw, since it calls getLaunchAction unconditionally. An engine can legitimately run without an activity, for example a cached engine warmed up by a background service, so callers had no way to avoid this other than catching the error code around initialize. The throw also provided no recovery value: initialize registers the Dart handler before calling getLaunchAction, and any action that arrives later is reported through the plugin's onNewIntent listener once an activity attaches. A warning is logged so a misconfigured launcher activity stays diagnosable. Fixes flutter/flutter#190348 --- .../quick_actions_android/CHANGELOG.md | 6 ++++++ .../plugins/quickactions/QuickActions.java | 12 ++++++++---- .../plugins/quickactions/QuickActionsTest.java | 17 +++++++++++++++++ .../quick_actions_android/pubspec.yaml | 2 +- 4 files changed, 32 insertions(+), 5 deletions(-) diff --git a/packages/quick_actions/quick_actions_android/CHANGELOG.md b/packages/quick_actions/quick_actions_android/CHANGELOG.md index fb9a4c468086..6f98e33822c1 100644 --- a/packages/quick_actions/quick_actions_android/CHANGELOG.md +++ b/packages/quick_actions/quick_actions_android/CHANGELOG.md @@ -1,3 +1,9 @@ +## 1.0.33 + +* Fixes `getLaunchAction` throwing when the plugin has no attached activity, which made + `initialize` throw for engines that run without UI, such as a cached engine warmed up by a + background service. It now logs a warning and returns `null`. + ## 1.0.32 * Bumps the androidx group across 10 directories with 1 update. diff --git a/packages/quick_actions/quick_actions_android/android/src/main/java/io/flutter/plugins/quickactions/QuickActions.java b/packages/quick_actions/quick_actions_android/android/src/main/java/io/flutter/plugins/quickactions/QuickActions.java index 5d07406bdac0..896c942d939d 100644 --- a/packages/quick_actions/quick_actions_android/android/src/main/java/io/flutter/plugins/quickactions/QuickActions.java +++ b/packages/quick_actions/quick_actions_android/android/src/main/java/io/flutter/plugins/quickactions/QuickActions.java @@ -11,6 +11,7 @@ import android.content.Intent; import android.content.res.Resources; import android.os.Build; +import android.util.Log; import androidx.annotation.ChecksSdkIntAtLeast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -28,6 +29,7 @@ import org.jetbrains.annotations.NotNull; final class QuickActions implements AndroidQuickActionsApi { + private static final String TAG = "QuickActionsAndroid"; static final String EXTRA_ACTION = "some unique action key"; private final Context context; @@ -102,10 +104,12 @@ public void clearShortcutItems() { return null; } if (activity == null) { - throw new FlutterError( - "quick_action_getlaunchaction_no_activity", - "There is no activity available when launching action", - null); + // The engine can run without an attached activity, for instance when it is cached and warmed + // up by a background service. Nothing launched the app in that case, so there is no launch + // action to report. Any action that arrives later is reported through the plugin's + // onNewIntent listener once an activity attaches. + Log.w(TAG, "There is no activity available when getting the launch action."); + return null; } final Intent intent = activity.getIntent(); final String launchAction = intent.getStringExtra(EXTRA_ACTION); diff --git a/packages/quick_actions/quick_actions_android/android/src/test/java/io/flutter/plugins/quickactions/QuickActionsTest.java b/packages/quick_actions/quick_actions_android/android/src/test/java/io/flutter/plugins/quickactions/QuickActionsTest.java index 114201240d74..748227e784cd 100644 --- a/packages/quick_actions/quick_actions_android/android/src/test/java/io/flutter/plugins/quickactions/QuickActionsTest.java +++ b/packages/quick_actions/quick_actions_android/android/src/test/java/io/flutter/plugins/quickactions/QuickActionsTest.java @@ -6,8 +6,10 @@ import static io.flutter.plugins.quickactions.QuickActions.EXTRA_ACTION; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; import android.app.Activity; @@ -50,6 +52,21 @@ public void setMessageHandler(@NonNull String channel, @Nullable BinaryMessageHa static final int UNSUPPORTED_BUILD = 24; static final String SHORTCUT_TYPE = "action_one"; + @Test + public void getLaunchAction_noActivity_returnsNull() { + // Arrange + // Build.VERSION.SDK_INT is 0 in unit tests, so the version check is stubbed to make sure the + // null activity, and not the unsupported version, is what is being exercised here. + final QuickActions quickActions = spy(new QuickActions(mock(Context.class))); + when(quickActions.isVersionAllowed()).thenReturn(true); + + // Act + final String launchAction = quickActions.getLaunchAction(); + + // Assert + assertNull(launchAction); + } + @Test public void canAttachToEngine() { final TestBinaryMessenger testBinaryMessenger = new TestBinaryMessenger(); diff --git a/packages/quick_actions/quick_actions_android/pubspec.yaml b/packages/quick_actions/quick_actions_android/pubspec.yaml index 53fc1ad2c700..6f87a56e4088 100644 --- a/packages/quick_actions/quick_actions_android/pubspec.yaml +++ b/packages/quick_actions/quick_actions_android/pubspec.yaml @@ -2,7 +2,7 @@ name: quick_actions_android description: An implementation for the Android platform of the Flutter `quick_actions` plugin. repository: https://github.com/flutter/packages/tree/main/packages/quick_actions/quick_actions_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 1.0.32 +version: 1.0.33 environment: sdk: ^3.12.0 From 37d79d0d424225901e0b5f88fb0d220fdea79881 Mon Sep 17 00:00:00 2001 From: Sembauke Date: Fri, 31 Jul 2026 15:07:05 +0200 Subject: [PATCH 2/3] Address review: use debug logging and doReturn for spy stubbing Logs at debug level rather than warning, since running without an attached activity is an expected state and would otherwise be noisy on every headless engine start. Uses doReturn().when() for the spy stub so the real method is not invoked while stubbing. --- packages/quick_actions/quick_actions_android/CHANGELOG.md | 2 +- .../java/io/flutter/plugins/quickactions/QuickActions.java | 2 +- .../java/io/flutter/plugins/quickactions/QuickActionsTest.java | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/quick_actions/quick_actions_android/CHANGELOG.md b/packages/quick_actions/quick_actions_android/CHANGELOG.md index 6f98e33822c1..15d457c08ac1 100644 --- a/packages/quick_actions/quick_actions_android/CHANGELOG.md +++ b/packages/quick_actions/quick_actions_android/CHANGELOG.md @@ -2,7 +2,7 @@ * Fixes `getLaunchAction` throwing when the plugin has no attached activity, which made `initialize` throw for engines that run without UI, such as a cached engine warmed up by a - background service. It now logs a warning and returns `null`. + background service. It now returns `null`. ## 1.0.32 diff --git a/packages/quick_actions/quick_actions_android/android/src/main/java/io/flutter/plugins/quickactions/QuickActions.java b/packages/quick_actions/quick_actions_android/android/src/main/java/io/flutter/plugins/quickactions/QuickActions.java index 896c942d939d..f738d5722093 100644 --- a/packages/quick_actions/quick_actions_android/android/src/main/java/io/flutter/plugins/quickactions/QuickActions.java +++ b/packages/quick_actions/quick_actions_android/android/src/main/java/io/flutter/plugins/quickactions/QuickActions.java @@ -108,7 +108,7 @@ public void clearShortcutItems() { // up by a background service. Nothing launched the app in that case, so there is no launch // action to report. Any action that arrives later is reported through the plugin's // onNewIntent listener once an activity attaches. - Log.w(TAG, "There is no activity available when getting the launch action."); + Log.d(TAG, "There is no activity available when getting the launch action."); return null; } final Intent intent = activity.getIntent(); diff --git a/packages/quick_actions/quick_actions_android/android/src/test/java/io/flutter/plugins/quickactions/QuickActionsTest.java b/packages/quick_actions/quick_actions_android/android/src/test/java/io/flutter/plugins/quickactions/QuickActionsTest.java index 748227e784cd..100f4a7a8528 100644 --- a/packages/quick_actions/quick_actions_android/android/src/test/java/io/flutter/plugins/quickactions/QuickActionsTest.java +++ b/packages/quick_actions/quick_actions_android/android/src/test/java/io/flutter/plugins/quickactions/QuickActionsTest.java @@ -8,6 +8,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; @@ -58,7 +59,7 @@ public void getLaunchAction_noActivity_returnsNull() { // Build.VERSION.SDK_INT is 0 in unit tests, so the version check is stubbed to make sure the // null activity, and not the unsupported version, is what is being exercised here. final QuickActions quickActions = spy(new QuickActions(mock(Context.class))); - when(quickActions.isVersionAllowed()).thenReturn(true); + doReturn(true).when(quickActions).isVersionAllowed(); // Act final String launchAction = quickActions.getLaunchAction(); From dce097bfab4b5d03c5783d540b4dbc968f9ad127 Mon Sep 17 00:00:00 2001 From: Sembauke Date: Fri, 31 Jul 2026 15:12:14 +0200 Subject: [PATCH 3/3] Leave version and CHANGELOG to the maintainers Reverts the version bump and CHANGELOG entry, leaving the release decision to the package maintainers. --- packages/quick_actions/quick_actions_android/CHANGELOG.md | 6 ------ packages/quick_actions/quick_actions_android/pubspec.yaml | 2 +- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/packages/quick_actions/quick_actions_android/CHANGELOG.md b/packages/quick_actions/quick_actions_android/CHANGELOG.md index 15d457c08ac1..fb9a4c468086 100644 --- a/packages/quick_actions/quick_actions_android/CHANGELOG.md +++ b/packages/quick_actions/quick_actions_android/CHANGELOG.md @@ -1,9 +1,3 @@ -## 1.0.33 - -* Fixes `getLaunchAction` throwing when the plugin has no attached activity, which made - `initialize` throw for engines that run without UI, such as a cached engine warmed up by a - background service. It now returns `null`. - ## 1.0.32 * Bumps the androidx group across 10 directories with 1 update. diff --git a/packages/quick_actions/quick_actions_android/pubspec.yaml b/packages/quick_actions/quick_actions_android/pubspec.yaml index 6f87a56e4088..53fc1ad2c700 100644 --- a/packages/quick_actions/quick_actions_android/pubspec.yaml +++ b/packages/quick_actions/quick_actions_android/pubspec.yaml @@ -2,7 +2,7 @@ name: quick_actions_android description: An implementation for the Android platform of the Flutter `quick_actions` plugin. repository: https://github.com/flutter/packages/tree/main/packages/quick_actions/quick_actions_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 1.0.33 +version: 1.0.32 environment: sdk: ^3.12.0