Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(share_plus): Recover ShareSuccessManager state after error #2817

Merged
merged 4 commits into from
Apr 7, 2024

Conversation

miquelbeltran
Copy link
Member

@miquelbeltran miquelbeltran commented Apr 5, 2024

Description

  • Looking at issues reported in [Bug]: share_plus exception prevents further use #1936 and [Bug]: prior share-sheet did not call back, did you await it? Maybe use non-result variant #1351, I figured out that the ShareSuccessManager was in a bad state which blocked users from continuing using the plugin.
  • The ShareSuccessManager checks the state of the current callback state, and fails if a callback was never executed. This is problematic because causes this state to be never recover after an error happened.
  • The problem is that, on error, onActivityResult is never called, so the state is never restored.
  • To solve this problem, I introduced a clear() method that will reset the state, only call to this method in cases we catch an error.
  • did a refactor, and moved things a bit, simplifying the code of the MethodCallHandler.
  • to test this, I call shareXFiles with bad XFile (wrong paths), which causes an exception. Then I call the share methods normally and still works, both with result and without.
  • In the future, if we find other edge cases where ShareSuccessManager is in a bad state, we can use the clear method in those edge cases to clean up the state.
  • This PR supersedes fix(share_plus): reset isCalledBack on android correctly after closing share-sheet #2446 as it provides a more precise way to handle errors, while still maintaining the old functionality intact.

Related Issues

Checklist

  • I read the Contributor Guide and followed the process outlined there for submitting PRs.
  • I titled the PR using Conventional Commits.
  • I did not modify the CHANGELOG.md nor the plugin version in pubspec.yaml files.
  • All existing and new tests are passing.
  • The analyzer (flutter analyze) does not report any problems on my PR.

Breaking Change

Does your PR require plugin users to manually update their apps to accommodate your change?

  • Yes, this is a breaking change (please indicate that with a ! in the title as explained in Conventional Commits).
  • No, this is not a breaking change.

@miquelbeltran miquelbeltran changed the title Miquelbeltran/1936 fix(share_plus): Recover ShareSuccessManager state after error Apr 5, 2024
@miquelbeltran miquelbeltran marked this pull request as ready for review April 5, 2024 10:10
@@ -8,72 +8,68 @@ import java.io.IOException
/** Handles the method calls for the plugin. */
internal class MethodCallHandler(
private val share: Share,
private val manager: ShareSuccessManager
private val manager: ShareSuccessManager,
Copy link
Member Author

Choose a reason for hiding this comment

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

Added the tailing comma to avoid code formatting to a single line

Comment on lines +15 to +16
expectMapArguments(call)

Copy link
Member Author

Choose a reason for hiding this comment

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

Moved to the top since it is common on all methods

"share", "shareWithResult" -> {
expectMapArguments(call)
if (isWithResult && !manager.setCallback(result)) return
if (isWithResult && !manager.setCallback(result)) return
Copy link
Member Author

Choose a reason for hiding this comment

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

Moved to the top since it is common for all methods as well.

Comment on lines +62 to +71
private fun success(
isWithResult: Boolean,
isResultRequested: Boolean,
result: MethodChannel.Result
) {
if (!isWithResult) {
if (isResultRequested) {
result.success("dev.fluttercommunity.plus/share/unavailable")
} else {
result.success(null)
Copy link
Member Author

Choose a reason for hiding this comment

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

Just moved all the result.success() code to a common method to avoid code duplication in the switch

Comment on lines +56 to +59
} catch (e: Throwable) {
manager.clear()
result.error("Share failed", e.message, e)
}
Copy link
Member Author

Choose a reason for hiding this comment

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

Catch all errors from share methods, pass them up to the Flutter layer, but before also call to manager.clear() to avoid the deadlock state in ShareSuccessManager.

This can be tested simply by calling

    await Share.shareXFiles(
      [XFile('/wrong/file')],
      text: text,
    );

then the user sees something like

E/flutter (29015): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: PlatformException(Share failed, /wrong/file: The source file doesn't exist.,

however, they will be able to use the share methods again later on because manager.clear() was called.

Copy link
Collaborator

@vbuberen vbuberen left a comment

Choose a reason for hiding this comment

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

Thanks for tackling this bug

@vbuberen
Copy link
Collaborator

vbuberen commented Apr 7, 2024

I believe it also closes #2515 even though the report didn't bother to provide meaningful details requested.

@miquelbeltran miquelbeltran merged commit 2b12d8a into main Apr 7, 2024
18 of 19 checks passed
@miquelbeltran miquelbeltran deleted the miquelbeltran/1936 branch April 7, 2024 16:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment