Open PGN files from file explorer, messages,... in the App on Android#2671
Open PGN files from file explorer, messages,... in the App on Android#2671HaonRekcef wants to merge 3 commits intolichess-org:mainfrom
Conversation
There was a problem hiding this comment.
Pull request overview
This PR adds the ability to open .pgn files from outside the Lichess mobile app on Android, allowing users to share or open PGN files from file explorers, messaging apps, and other sources directly into the app.
Changes:
- Added the
receive_sharing_intentpackage (via git dependency) to handle incoming file shares on Android - Refactored PGN handling logic in
ImportPgnScreento be reusable from the app entry point - Updated Android manifest to register intent filters for PGN files and changed launch mode to
singleTask
Reviewed changes
Copilot reviewed 4 out of 5 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| pubspec.yaml | Added receive_sharing_intent dependency from a specific GitHub commit |
| pubspec.lock | Lock file update for the new dependency |
| lib/src/view/more/import_pgn_screen.dart | Refactored PGN handling logic into a public static method for reuse |
| lib/src/app.dart | Added sharing intent initialization and file processing logic |
| android/app/src/main/AndroidManifest.xml | Added intent filters for PGN files and changed launch mode to singleTask |
Comments suppressed due to low confidence (10)
lib/src/view/more/import_pgn_screen.dart:30
- The shorthand enum syntax
.erroris inconsistent with the rest of the codebase. The codebase consistently uses the fully-qualifiedSnackBarType.errorformat. Please changetype: .errortotype: SnackBarType.error.
showSnackBar(context, context.l10n.invalidPgn, type: .error);
lib/src/view/more/import_pgn_screen.dart:43
- The shorthand enum syntax
.whiteis inconsistent with the rest of the codebase. The codebase consistently uses the fully-qualifiedSide.whiteformat. Please changeorientation: .whitetoorientation: Side.white.
orientation: .white,
android/app/src/main/AndroidManifest.xml:54
- The second intent-filter for PGN files (lines 46-54) lacks MIME type specification and only uses pathSuffix. This is problematic because:
- The pathSuffix filter alone may not work reliably across all Android versions and file providers
- It could potentially match unintended files with .pgn in their names
- It creates duplicate intent handling with the first filter
Consider either removing this filter (if the MIME type filter at lines 36-44 is sufficient) or adding a MIME type to this filter as well for better reliability.
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="content" />
<data android:pathSuffix=".pgn" />
</intent-filter>
lib/src/app.dart:200
- Error handling in
_processSharedFilessilently fails with only a debug print. If file reading fails due to permissions, encoding issues, or the file being moved/deleted, the user receives no feedback. Consider showing a user-visible error message usingshowSnackBarsimilar to how errors are handled inImportPgnScreen.handlePgnText. This would provide better user experience and consistency with the existing error handling patterns in the codebase.
} catch (e) {
debugPrint('Failed to process incoming file: $e');
}
pubspec.yaml:74
- The dependency uses a specific commit SHA (2cea396843cd3ab1b5ec4334be4233864637874e) rather than a branch or tag. While this ensures reproducibility, it's important to verify:
- That this commit exists and contains the necessary fix mentioned in the PR description
- That this is a reasonable long-term approach, as the package maintainer may force-push or delete commits
- Consider documenting in a comment why this specific commit is needed and what fix it includes
This helps future maintainers understand why a git dependency is used instead of a published version.
receive_sharing_intent:
git:
url: https://github.com/KasemJaffer/receive_sharing_intent
ref: 2cea396843cd3ab1b5ec4334be4233864637874e
android/app/src/main/AndroidManifest.xml:24
- Changing
launchModefromsingleToptosingleTaskis a significant behavioral change that affects how the app handles multiple instances and task management. WithsingleTask:
- Only one instance of the activity exists in the system
- New intents will be delivered to the existing instance via
onNewIntent() - The activity becomes the root of its own task
While this is likely necessary for the sharing intent to work properly, be aware that:
- It changes how back navigation works - users may not return to the previous app after pressing back
- It affects how the app interacts with the recent apps list
- It may impact existing deep link handling behavior
Consider testing edge cases like: opening the app from a notification while it's already open, deep links when the app is in the background, and back navigation from various entry points.
android:launchMode="singleTask"
lib/src/view/more/import_pgn_screen.dart:47
- The shorthand enum syntax
.standardis inconsistent with the rest of the codebase. The codebase consistently uses fully-qualified enum values. Please change.standardtoVariant.standard.
variant: rule != null ? Variant.fromRule(rule) : .standard,
lib/src/view/more/import_pgn_screen.dart:58
- The shorthand enum syntax
.erroris inconsistent with the rest of the codebase. The codebase consistently uses the fully-qualifiedSnackBarType.errorformat. Please changetype: .errortotype: SnackBarType.error.
showSnackBar(context, context.l10n.invalidPgn, type: .error);
lib/src/app.dart:187
- The code only processes the first file from the shared files list (
files.first.path). If a user shares multiple PGN files at once, only the first one will be processed and the rest will be silently ignored. Consider either:
- Processing all shared files sequentially
- Showing an error message if multiple files are shared
- Documenting this limitation
The current behavior could confuse users who attempt to share multiple PGN files at once.
Future<void> _processSharedFiles(List<SharedMediaFile> files) async {
if (files.isEmpty) return;
final filePath = files.first.path;
lib/src/app.dart:183
- The sharing intent functionality is initialized unconditionally but appears to be Android-only based on the PR description. While the
receive_sharing_intentpackage may handle platform checks internally, it would be more explicit and maintainable to wrap the initialization in a platform check (e.g.,if (Platform.isAndroid)) to prevent unnecessary initialization on iOS and other platforms. This also makes it clearer that this feature is currently Android-only until iOS implementation is added.
void _initSharingIntent() {
// Warm start
_intentSub = ReceiveSharingIntent.instance.getMediaStream().listen((
List<SharedMediaFile> value,
) {
_processSharedFiles(value);
});
// Cold start
ReceiveSharingIntent.instance.getInitialMedia().then((List<SharedMediaFile> value) {
_processSharedFiles(value);
ReceiveSharingIntent.instance.reset();
});
}
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| Future<void> _processSharedFiles(List<SharedMediaFile> files) async { | ||
| if (files.isEmpty) return; | ||
| final filePath = files.first.path; | ||
| try { | ||
| final context = _navigatorKey.currentContext; | ||
| if (context == null || !context.mounted) return; | ||
|
|
There was a problem hiding this comment.
There's a potential race condition in the cold start handling. The getInitialMedia() call is made in initState(), but at that point the navigator context might not be ready yet, causing the file processing to silently fail when _navigatorKey.currentContext is null. Consider adding a retry mechanism or deferring the cold start processing until after the first frame is built using WidgetsBinding.instance.addPostFrameCallback(). This would ensure the navigator is ready to handle the navigation.
| Future<void> _processSharedFiles(List<SharedMediaFile> files) async { | |
| if (files.isEmpty) return; | |
| final filePath = files.first.path; | |
| try { | |
| final context = _navigatorKey.currentContext; | |
| if (context == null || !context.mounted) return; | |
| Future<void> _processSharedFiles( | |
| List<SharedMediaFile> files, { | |
| int attemptsLeft = 3, | |
| }) async { | |
| if (files.isEmpty) return; | |
| final context = _navigatorKey.currentContext; | |
| if (context == null || !context.mounted) { | |
| if (attemptsLeft <= 0) { | |
| return; | |
| } | |
| WidgetsBinding.instance.addPostFrameCallback((_) { | |
| _processSharedFiles( | |
| files, | |
| attemptsLeft: attemptsLeft - 1, | |
| ); | |
| }); | |
| return; | |
| } | |
| final filePath = files.first.path; | |
| try { |
|
Cool stuff! 😎 Once this gets merged I'll work on the iOS support for this 🍏 |
Allows to open
.pgnfiles from outside the app on android. Preview:open_pgn_files.mp4
Successfully tested on Android 8 and Android 15
I used the package
receive_sharing_intentwhich seems to only be sporadically maintained, but I couldnt find a better option.I needed to give a direct link to github for the package because of a fix that is not released yet: KasemJaffer/receive_sharing_intent#333
Needs to be implemented on the iOS side as well, but I don't have the devices to do it.
I changed the launchmode to single task to prevent multiple instances of the app getting created.