Skip to content

Add file support on opening#203

Open
JuliusCaesarCrypto wants to merge 1 commit intodevfrom
add-file-support
Open

Add file support on opening#203
JuliusCaesarCrypto wants to merge 1 commit intodevfrom
add-file-support

Conversation

@JuliusCaesarCrypto
Copy link
Copy Markdown
Contributor

Change Description

Briefly describe what this PR does and why. Keep it short and clear.


Related Platforms

Which platforms are affected by your changes? Check only the ones you actually tested.

  • Android
  • iOS
  • iPad
  • Windows
  • Linux
  • Android TV
  • OpenWrt

Verification Checklist

Make sure the things you checked actually work. It's okay if you didn't test everything.

  • Project builds successfully
  • App runs without crashes on tested platforms
  • VPN connection works correctly
  • No obvious regressions observed
  • Documentation updated (if needed)

Optional (for bigger changes)

  • Added or updated unit / E2E tests
  • Checked security and edge cases

Related Links

Closes #ID.

@JuliusCaesarCrypto JuliusCaesarCrypto self-assigned this Mar 25, 2026
Copilot AI review requested due to automatic review settings March 25, 2026 12:41
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR aims to add support for opening/importing .dfx files into the app (via OS “open with” / sharing flows), and wires the imported content into the existing flowline saving path.

Changes:

  • Add a FileListener that listens for incoming shared/opened files and saves .dfx content via flowlineServiceProvider.
  • Register .dfx as a supported document type on iOS and add an Android intent-filter for viewing .dfx files.
  • Update Flutter dependencies/lockfile (including adding receive_sharing_intent).

Reviewed changes

Copilot reviewed 10 out of 12 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
pubspec.yaml Adds dependencies needed for receiving shared/opened files and webview platform impls.
pubspec.lock Updates resolved dependency graph to include new packages and version changes.
lib/modules/core/file_listener.dart New listener to ingest .dfx files and persist flowline data.
lib/app/app.dart Initializes the new file listener during app initialization.
android/app/src/main/AndroidManifest.xml Adds an ACTION_VIEW intent-filter targeting .dfx files.
ios/Runner/Info.plist Declares .dfx UTI/document types and enables document opening/sharing.
lib/shared/layout/navbar/** Minor localization null-assertion cleanup and a small styling tweak.
lib/main.dart Whitespace-only formatting change.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

<data
android:scheme="file"
android:pathPattern=".*\\.dfx"
android:mimeType="*/*" />
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

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

The intent filter only matches android:scheme="file", but on modern Android most file managers/DocumentProvider flows deliver ACTION_VIEW URIs as content://.... With the current filter, tapping a .dfx file is unlikely to resolve to the app.

Consider adding a content scheme variant (and handling persisted URI permissions as needed), or registering by MIME/type/extension in a way that matches how files are actually opened (and ensure the Flutter side can read the content URI).

Suggested change
android:mimeType="*/*" />
android:mimeType="*/*" />
<data
android:scheme="content"
android:pathPattern=".*\\.dfx"
android:mimeType="*/*" />

Copilot uses AI. Check for mistakes.
Comment on lines 38 to 41
Future<bool> _initializeApp(WidgetRef ref) async {
FileListener().init(ProviderScope.containerOf(ref.context));
await VPN(ProviderScope.containerOf(ref.context)).getVPNStatus();
await AlertService().init();
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

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

FileListener().init(...) is called inside _initializeApp, but _initializeApp(ref) is invoked directly in the FutureBuilder (a new Future is created on every rebuild). This can register multiple ReceiveSharingIntent listeners and repeat initialization side-effects. Also, receive_sharing_intent has no desktop implementation; calling this unconditionally can trigger a MissingPluginException on Windows/Linux.

Consider initializing FileListener exactly once (e.g., move init to a StatefulWidget's initState, a Riverpod provider, or cache the Future), and guard the initialization by platform (Android/iOS only, or whichever platforms the plugin supports).

Copilot uses AI. Check for mistakes.
Comment on lines +11 to +18
ReceiveSharingIntent.instance.getMediaStream().listen(
(List<SharedMediaFile> files) async {
if (files.isEmpty) return;
final file = files.first;
if (file.path.endsWith('.dfx')) {
final content = await _readFileAsString(file.path);
onDfxFile(content);
}
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

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

The stream subscription returned by getMediaStream().listen(...) is not stored or cancelled, and there is no dispose/close method on FileListener. If init() is ever called more than once (which can happen given the current call site), this will accumulate listeners and can process the same intent multiple times.

Store the StreamSubscription(s) and provide a teardown method to cancel them (and ensure init is idempotent).

Copilot uses AI. Check for mistakes.
Comment on lines +15 to +17
if (file.path.endsWith('.dfx')) {
final content = await _readFileAsString(file.path);
onDfxFile(content);
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

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

_readFileAsString(file.path) can throw (file missing/permission/encoding), but the async callback passed to .listen/.then doesn't catch exceptions. That can terminate the stream listener or surface as unhandled async errors.

Wrap the per-file handling in a try/catch (and optionally validate file size / encoding) so a bad share/open action doesn't break future intent handling.

Copilot uses AI. Check for mistakes.
Comment on lines +46 to +48
_container ??= ProviderContainer();
await _container
?.read(flowlineServiceProvider)
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

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

_container ??= ProviderContainer(); creates a new, unmanaged Riverpod container when init hasn't been called, and it is never disposed. This can lead to duplicated state, missing overrides, and memory leaks.

Prefer making the container a required dependency (e.g., assert _container != null in _handleFile, or pass WidgetRef/ProviderContainer into the handler) and avoid creating a separate container implicitly.

Suggested change
_container ??= ProviderContainer();
await _container
?.read(flowlineServiceProvider)
final container = _container;
assert(
container != null,
'FileListener.init must be called with a ProviderContainer before handling files.',
);
if (container == null) {
debugPrint(
'FileListener used before init; ignoring received file content.',
);
return;
}
await container
.read(flowlineServiceProvider)

Copilot uses AI. Check for mistakes.
Comment on lines +10 to +17
void _startListening(void Function(String dfxPath) onDfxFile) {
ReceiveSharingIntent.instance.getMediaStream().listen(
(List<SharedMediaFile> files) async {
if (files.isEmpty) return;
final file = files.first;
if (file.path.endsWith('.dfx')) {
final content = await _readFileAsString(file.path);
onDfxFile(content);
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

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

The callback signature/name is misleading: _startListening(void Function(String dfxPath) onDfxFile) suggests the callback receives a file path, but the code passes the file content (onDfxFile(content)). This makes the API easy to misuse.

Rename the parameter/type to reflect what is passed (e.g., onDfxContent / String content), or change the call to pass the path and let the handler decide how to read it.

Copilot uses AI. Check for mistakes.
</array>
<key>LSItemContentTypes</key>
<array>
<string>public.data</string>
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

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

LSItemContentTypes is set to public.data, which makes the app claim it can open any data type rather than only the custom .dfx type. This can cause the app to appear as an option for many unrelated files and may lead to unexpected opens.

Set LSItemContentTypes to the UTI you declared in UTImportedTypeDeclarations (e.g., de.unboundtech.defyxvpn.dfx) so only .dfx files are associated.

Suggested change
<string>public.data</string>
<string>de.unboundtech.defyxvpn.dfx</string>

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants