Skip to content

[quick_actions_android] Return null from getLaunchAction when no activity is attached - #12331

Open
Sembauke wants to merge 3 commits into
flutter:mainfrom
Sembauke:quick-actions-android-no-activity-returns-null
Open

[quick_actions_android] Return null from getLaunchAction when no activity is attached#12331
Sembauke wants to merge 3 commits into
flutter:mainfrom
Sembauke:quick-actions-android-no-activity-returns-null

Conversation

@Sembauke

@Sembauke Sembauke commented Jul 31, 2026

Copy link
Copy Markdown

getLaunchAction threw quick_action_getlaunchaction_no_activity when the plugin had no attached activity. QuickActionsAndroid.initialize calls getLaunchAction unconditionally, so initialize threw a PlatformException that callers could not prevent.

An engine can legitimately run without an attached activity. A cached engine warmed up by a background service is the common case: audio_service supplies a cached engine to its activity and also warms up that same engine from AudioService.onCreate, so main() runs with no activity when a media button or Android Auto starts the service.

This returns null in that case instead, and logs at debug level. The throw provided no recovery value:

  • initialize registers the Dart handler before it calls getLaunchAction, so the handler is already installed when the exception is thrown.
  • When an activity does attach, QuickActionsPlugin.onAttachedToActivity calls onNewIntent(activity.getIntent()), which reports the shortcut through AndroidQuickActionsFlutterApi.launchAction.

null is also already the return value for the other "cannot answer" case in this method, on the line directly above (if (!isVersionAllowed())).

Verified on an Android 12 emulator by pointing an app at this branch and starting its audio service with no UI: no exception, and a shortcut tap afterwards still routed correctly. A cold start from a shortcut still returns the action, so the activity-present path is unchanged. The new unit test fails with io.flutter.plugins.quickactions.FlutterError against the unmodified file.

Fixes flutter/flutter#190348

Pre-Review Checklist

Footnotes

  1. Regular contributors who have demonstrated familiarity with the repository guidelines only need to comment if the PR is not auto-exempted by repo tooling. 2

…vity 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
@google-cla

google-cla Bot commented Jul 31, 2026

Copy link
Copy Markdown

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request modifies the Android quick_actions plugin to return null and log a message instead of throwing a FlutterError when getLaunchAction is called without an attached activity. It also adds a unit test to verify this behavior. The review feedback suggests downgrading the log level from warning to debug to prevent misleading logs during normal execution, and recommends using doReturn().when() instead of when().thenReturn() for stubbing the Mockito spy in the test.

// 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.");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Since running the engine without an attached activity is a legitimate and expected use case (e.g., when warmed up by a background service), logging this as a warning (Log.w) can be misleading. It may cause developers to think there is an issue with their configuration when everything is operating normally. Consider using debug logging (Log.d) instead to keep the production logs clean while still allowing the state to be diagnosable during development.

Suggested change
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.");

@Sembauke Sembauke Jul 31, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, done in 37d79d0. A media app can hit this on every headless engine start, so warning level would be recurring noise for normal operation, and a genuinely misconfigured launcher activity is something you would be debugging anyway, where Log.d is visible.

// 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);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

When stubbing methods on a Mockito spy, it is highly recommended to use doReturn().when() instead of when().thenReturn(). Using when().thenReturn() actually invokes the real method on the spy, which can lead to unexpected side effects or NullPointerExceptions if the real method relies on uninitialized state. Although isVersionAllowed() is safe to call here, using doReturn is a safer and more idiomatic practice for spies.

Suggested change
when(quickActions.isVersionAllowed()).thenReturn(true);
org.mockito.Mockito.doReturn(true).when(quickActions).isVersionAllowed();

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 37d79d0, using a static import to match the existing import style in this file rather than the fully-qualified call.

I re-verified the test is still a real regression test after the change, since doReturn alters stubbing semantics: against the unmodified QuickActions.java it still fails with io.flutter.plugins.quickactions.FlutterError, and passes with the fix.

@Sembauke

Copy link
Copy Markdown
Author

@googlebot I signed it!

Sembauke added 2 commits July 31, 2026 15:07
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.
Reverts the version bump and CHANGELOG entry, leaving the release
decision to the package maintainers.
@Sembauke

Copy link
Copy Markdown
Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request modifies the Android QuickActions plugin to return null instead of throwing a FlutterError when the activity is null during getLaunchAction(), logging a debug message instead. A corresponding unit test was added to verify this behavior. The review feedback suggests changing the log level from debug (Log.d) to warning (Log.w) to ensure that a missing activity remains diagnosable in logcat.

// 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.d(TAG, "There is no activity available when getting the launch action.");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The PR description mentions that this change logs a warning so a genuinely misconfigured launcher activity stays diagnosable in logcat. However, the implementation uses Log.d (debug level) instead of Log.w (warning level). Using Log.w ensures that this potential misconfiguration is logged as a warning, making it easier to diagnose in production/logcat.

Suggested change
Log.d(TAG, "There is no activity available when getting the launch action.");
Log.w(TAG, "There is no activity available when getting the launch action.");

@stuartmorgan-g

Copy link
Copy Markdown
Collaborator

Thank you for your contribution! Because of the volume of PRs we receive, we require that new contributors use our checklist to guide them through critical steps in creating a Flutter PR. This PR's description is using an alternate (possibly AI-generated) checklist that does not follow our process.

Please edit the PR description to add the correct checklist, then ensure that you have completed all of the steps. Once you've done that, please mark the PR as ready for review.

If you need help, consider asking for advice on the #hackers-new channel on Discord.

@stuartmorgan-g
stuartmorgan-g marked this pull request as draft July 31, 2026 16:24
@Sembauke
Sembauke marked this pull request as ready for review July 31, 2026 19:46

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request modifies QuickActions.java to return null and log a debug message instead of throwing a FlutterError when there is no activity available during getLaunchAction(). Additionally, a unit test getLaunchAction_noActivity_returnsNull has been added to QuickActionsTest.java to verify this behavior. I have no feedback to provide.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[quick_actions_android] initialize() throws quick_action_getlaunchaction_no_activity when the engine has no attached Activity

2 participants