diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index d557330..b054314 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -17,7 +17,7 @@
android:name="android.hardware.touchscreen"
android:required="false" />
-
+
@@ -38,7 +38,7 @@
android:name="com.dexterous.flutterlocalnotifications.ForegroundService"
android:exported="false"
android:stopWithTask="false"
- android:foregroundServiceType="systemExempted"/>
+ android:foregroundServiceType="systemExempted" />
+ android:name="io.flutter.embedding.android.NormalTheme"
+ android:resource="@style/NormalTheme"
+ />
+
+
+
+
+
-
-
-
+
+
+
+
@@ -86,4 +95,4 @@
-
+
\ No newline at end of file
diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist
index 6f41758..11c2b9a 100644
--- a/ios/Runner/Info.plist
+++ b/ios/Runner/Info.plist
@@ -3,7 +3,7 @@
CADisableMinimumFrameDurationOnPhone
-
+
CFBundleDevelopmentRegion
$(DEVELOPMENT_LANGUAGE)
CFBundleDisplayName
@@ -27,17 +27,19 @@
GADApplicationIdentifier
ca-app-pub-6029472941300558~5225768298
LSRequiresIPhoneOS
-
+
NSDocumentsFolderUsageDescription
WARP needs access to store its configuration files
NSVPNConfigurationUsageDescription
Defyx needs access to VPN configurations to secure your connection.
NSUserTrackingUsageDescription
- We need permission to show you personalized ads. This helps support our free VPN service. Declining means you'll see generic ads instead.
+ We need permission to show you personalized ads. This helps support our free VPN
+ service. Declining means you'll see generic ads instead.
NSPhotoLibraryUsageDescription
- Defyx needs access to your photo library to let you select and share images, such as profile pictures or VPN connection QR codes.
+ Defyx needs access to your photo library to let you select and share images, such as
+ profile pictures or VPN connection QR codes.
UIApplicationSupportsIndirectInputEvents
-
+
UILaunchStoryboardName
LaunchScreen
UIMainStoryboardFile
@@ -64,36 +66,53 @@
NSAppTransportSecurity
NSAllowsArbitraryLoads
-
+
UIStatusBarHidden
-
- LSSupportsOpeningDocumentsInPlace
-
- UIFileSharingEnabled
-
- UISupportsDocumentBrowser
-
- UTImportedTypeDeclarations
-
-
- UTTypeConformsTo
-
- public.data
- public.content
-
- UTTypeDescription
- DefyX Configuration File
- UTTypeIdentifier
- de.unboundtech.defyxvpn.dfx
- UTTypeTagSpecification
+
+ LSSupportsOpeningDocumentsInPlace
+
+ UIFileSharingEnabled
+
+ UISupportsDocumentBrowser
+
+ UTImportedTypeDeclarations
+
+
+ UTTypeConformsTo
+
+ public.data
+ public.content
+
+ UTTypeDescription
+ DefyX Configuration File
+ UTTypeIdentifier
+ de.unboundtech.defyxvpn.dfx
+ UTTypeTagSpecification
+
+ public.filename-extension
+
+ dfx
+
+
+
+
+ CFBundleDocumentTypes
+
- public.filename-extension
+ CFBundleTypeName
+ DFX File
+ LSHandlerRank
+ Alternate
+ CFBundleTypeExtensions
dfx
+ LSItemContentTypes
+
+ public.data
+
-
-
+
\ No newline at end of file
diff --git a/lib/app/app.dart b/lib/app/app.dart
index c7f940a..c3951d4 100644
--- a/lib/app/app.dart
+++ b/lib/app/app.dart
@@ -4,6 +4,7 @@ import 'package:app_tracking_transparency/app_tracking_transparency.dart';
import 'package:defyx_vpn/app/advertise_director.dart';
import 'package:defyx_vpn/app/router/app_router.dart';
import 'package:defyx_vpn/core/theme/app_theme.dart';
+import 'package:defyx_vpn/modules/core/file_listener.dart';
import 'package:defyx_vpn/modules/core/vpn.dart';
import 'package:defyx_vpn/modules/core/desktop_platform_handler.dart';
import 'package:defyx_vpn/modules/main/presentation/widgets/ump_service.dart';
@@ -35,6 +36,7 @@ class App extends ConsumerWidget {
}
Future _initializeApp(WidgetRef ref) async {
+ FileListener().init(ProviderScope.containerOf(ref.context));
await VPN(ProviderScope.containerOf(ref.context)).getVPNStatus();
await AlertService().init();
await AnimationService().init();
@@ -57,20 +59,22 @@ class App extends ConsumerWidget {
if (Platform.isAndroid || Platform.isIOS) {
// Request App Tracking Transparency (iOS only)
if (Platform.isIOS) {
- final status = await AppTrackingTransparency.trackingAuthorizationStatus;
+ final status =
+ await AppTrackingTransparency.trackingAuthorizationStatus;
if (status == TrackingStatus.notDetermined) {
// Small delay to ensure UI is ready
await Future.delayed(const Duration(milliseconds: 500));
- final result = await AppTrackingTransparency.requestTrackingAuthorization();
+ final result =
+ await AppTrackingTransparency.requestTrackingAuthorization();
debugPrint('📱 ATT Authorization: $result');
} else {
debugPrint('📱 ATT Status: $status');
}
}
-
+
// Get UMP service with cache integration
final umpService = ref.read(umpServiceProvider);
-
+
// Request UMP consent (checks cache first)
await umpService.requestConsent(
onDone: () async {
@@ -91,41 +95,42 @@ class App extends ConsumerWidget {
final designSize = _getDesignSize(context);
return ToastificationWrapper(
- config: ToastificationConfig(
- maxToastLimit: 1,
- blockBackgroundInteraction: false,
- applyMediaQueryViewInsets: true,
- ),
- child: ScreenUtilInit(
- designSize: designSize,
- minTextAdapt: true,
- splitScreenMode: true,
- builder: (_, __) {
- return MaterialApp.router(
- title: 'Defyx',
- theme: AppTheme.lightTheme,
- darkTheme: AppTheme.darkTheme,
- themeMode: ThemeMode.light,
- routerConfig: router,
- builder: _appBuilder,
- debugShowCheckedModeBanner: false,
- // Force English locale (comment out to enable device language detection)
- locale: const Locale('en'),
- localizationsDelegates: const [
- AppLocalizations.delegate,
- GlobalMaterialLocalizations.delegate,
- GlobalWidgetsLocalizations.delegate,
- GlobalCupertinoLocalizations.delegate,
- ],
- supportedLocales: const [
- Locale('en'),
- Locale('fa'),
- Locale('zh'),
- Locale('ru'),
- ],
- );
- },
- ));
+ config: ToastificationConfig(
+ maxToastLimit: 1,
+ blockBackgroundInteraction: false,
+ applyMediaQueryViewInsets: true,
+ ),
+ child: ScreenUtilInit(
+ designSize: designSize,
+ minTextAdapt: true,
+ splitScreenMode: true,
+ builder: (_, __) {
+ return MaterialApp.router(
+ title: 'Defyx',
+ theme: AppTheme.lightTheme,
+ darkTheme: AppTheme.darkTheme,
+ themeMode: ThemeMode.light,
+ routerConfig: router,
+ builder: _appBuilder,
+ debugShowCheckedModeBanner: false,
+ // Force English locale (comment out to enable device language detection)
+ locale: const Locale('en'),
+ localizationsDelegates: const [
+ AppLocalizations.delegate,
+ GlobalMaterialLocalizations.delegate,
+ GlobalWidgetsLocalizations.delegate,
+ GlobalCupertinoLocalizations.delegate,
+ ],
+ supportedLocales: const [
+ Locale('en'),
+ Locale('fa'),
+ Locale('zh'),
+ Locale('ru'),
+ ],
+ );
+ },
+ ),
+ );
}
Size _getDesignSize(BuildContext context) {
diff --git a/lib/main.dart b/lib/main.dart
index 11cda03..6d5bd26 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -11,7 +11,7 @@ import 'app/app.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await dotenv.load();
-
+
// Initialize cache directory for VPN core
try {
final String vpnCacheDir = await VpnBridge().getSharedDirectory();
@@ -20,7 +20,7 @@ void main() async {
} catch (e) {
debugPrint('Failed to set cache directory: $e');
}
-
+
// Initialize Firebase only on supported platforms (not Windows)
if (!Platform.isWindows && !Platform.isLinux) {
await Firebase.initializeApp(
diff --git a/lib/modules/core/file_listener.dart b/lib/modules/core/file_listener.dart
new file mode 100644
index 0000000..9aa76e8
--- /dev/null
+++ b/lib/modules/core/file_listener.dart
@@ -0,0 +1,56 @@
+import 'dart:io';
+import 'package:defyx_vpn/core/data/local/remote/api/flowline_service.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:receive_sharing_intent/receive_sharing_intent.dart';
+
+class FileListener {
+ ProviderContainer? _container;
+
+ void _startListening(void Function(String dfxPath) onDfxFile) {
+ ReceiveSharingIntent.instance.getMediaStream().listen(
+ (List files) async {
+ if (files.isEmpty) return;
+ final file = files.first;
+ if (file.path.endsWith('.dfx')) {
+ final content = await _readFileAsString(file.path);
+ onDfxFile(content);
+ }
+ },
+ onError: (err) {
+ debugPrint('Error receiving shared media: $err');
+ },
+ );
+
+ ReceiveSharingIntent.instance
+ .getInitialMedia()
+ .then((List files) async {
+ if (files.isEmpty) return;
+ final file = files.first;
+ if (file.path.endsWith('.dfx')) {
+ final content = await _readFileAsString(file.path);
+ onDfxFile(content);
+ }
+ })
+ .catchError((err) {
+ debugPrint('Error getting initial shared media: $err');
+ });
+ }
+
+ Future _readFileAsString(String path) async {
+ final file = File(path);
+ return await file.readAsString();
+ }
+
+ Future _handleFile(String content) async {
+ _container ??= ProviderContainer();
+ await _container
+ ?.read(flowlineServiceProvider)
+ .saveFlowline(offlineMode: true, flowLine: content);
+ }
+
+ void init(ProviderContainer container) {
+ _container = container;
+ _startListening(_handleFile);
+ }
+}
diff --git a/lib/shared/layout/navbar/defyx_navbar.dart b/lib/shared/layout/navbar/defyx_navbar.dart
index 2dc4334..afa76fc 100644
--- a/lib/shared/layout/navbar/defyx_navbar.dart
+++ b/lib/shared/layout/navbar/defyx_navbar.dart
@@ -116,7 +116,7 @@ class DefyxNavBar extends ConsumerWidget {
}
void _showShareDialog(BuildContext context, WidgetRef ref) {
- final l10n = AppLocalizations.of(context)!;
+ final l10n = AppLocalizations.of(context);
ref.read(currentScreenProvider.notifier).state = AppScreen.share;
showGeneralDialog(
context: context,
diff --git a/lib/shared/layout/navbar/widgets/introduction_dialog.dart b/lib/shared/layout/navbar/widgets/introduction_dialog.dart
index b86bc1b..6552454 100644
--- a/lib/shared/layout/navbar/widgets/introduction_dialog.dart
+++ b/lib/shared/layout/navbar/widgets/introduction_dialog.dart
@@ -9,7 +9,7 @@ class IntroductionDialog extends StatelessWidget {
@override
Widget build(BuildContext context) {
- final l10n = AppLocalizations.of(context)!;
+ final l10n = AppLocalizations.of(context);
return Dialog(
insetPadding: EdgeInsets.symmetric(horizontal: 24.w),
diff --git a/lib/shared/layout/navbar/widgets/offline_flowline_widget.dart b/lib/shared/layout/navbar/widgets/offline_flowline_widget.dart
index beb70e5..4f2c323 100644
--- a/lib/shared/layout/navbar/widgets/offline_flowline_widget.dart
+++ b/lib/shared/layout/navbar/widgets/offline_flowline_widget.dart
@@ -53,7 +53,10 @@ class _OfflineFlowlineWidgetState extends ConsumerState {
backgroundColor: const Color(0xFFF2F2F2),
padding: EdgeInsets.all(10.h),
),
- child: Text(AppLocalizations.of(context).offlineFlowlineUndo),
+ child: Text(
+ AppLocalizations.of(context).offlineFlowlineUndo,
+ style: TextStyle(fontSize: 13.sp, fontWeight: FontWeight.w500),
+ ),
),
],
),
diff --git a/lib/shared/layout/navbar/widgets/quick_menu_dialog.dart b/lib/shared/layout/navbar/widgets/quick_menu_dialog.dart
index 4a57ac7..41fe957 100644
--- a/lib/shared/layout/navbar/widgets/quick_menu_dialog.dart
+++ b/lib/shared/layout/navbar/widgets/quick_menu_dialog.dart
@@ -34,7 +34,7 @@ class _QuickMenuDialogState extends State {
@override
Widget build(BuildContext context) {
- final l10n = AppLocalizations.of(context)!;
+ final l10n = AppLocalizations.of(context);
return SafeArea(
child: Padding(
diff --git a/lib/shared/layout/navbar/widgets/social_icon_button.dart b/lib/shared/layout/navbar/widgets/social_icon_button.dart
index f7fff29..fff7c80 100644
--- a/lib/shared/layout/navbar/widgets/social_icon_button.dart
+++ b/lib/shared/layout/navbar/widgets/social_icon_button.dart
@@ -1,4 +1,3 @@
-import 'package:defyx_vpn/shared/layout/navbar/widgets/custom_webview_screen.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
diff --git a/pubspec.lock b/pubspec.lock
index 7b15131..6095f93 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -221,10 +221,10 @@ packages:
dependency: transitive
description:
name: characters
- sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b
+ sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803
url: "https://pub.dev"
source: hosted
- version: "1.4.1"
+ version: "1.4.0"
checked_yaml:
dependency: transitive
description:
@@ -849,18 +849,18 @@ packages:
dependency: transitive
description:
name: matcher
- sha256: "12956d0ad8390bbcc63ca2e1469c0619946ccb52809807067a7020d57e647aa6"
+ sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2
url: "https://pub.dev"
source: hosted
- version: "0.12.18"
+ version: "0.12.17"
material_color_utilities:
dependency: transitive
description:
name: material_color_utilities
- sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b"
+ sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
url: "https://pub.dev"
source: hosted
- version: "0.13.0"
+ version: "0.11.1"
meta:
dependency: transitive
description:
@@ -1069,6 +1069,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.5.0"
+ receive_sharing_intent:
+ dependency: "direct main"
+ description:
+ name: receive_sharing_intent
+ sha256: ec76056e4d258ad708e76d85591d933678625318e411564dcb9059048ca3a593
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.8.1"
retrofit:
dependency: "direct main"
description:
@@ -1310,10 +1318,10 @@ packages:
dependency: transitive
description:
name: test_api
- sha256: "93167629bfc610f71560ab9312acdda4959de4df6fac7492c89ff0d3886f6636"
+ sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55
url: "https://pub.dev"
source: hosted
- version: "0.7.9"
+ version: "0.7.7"
timing:
dependency: transitive
description:
@@ -1531,13 +1539,13 @@ packages:
source: hosted
version: "4.13.0"
webview_flutter_android:
- dependency: transitive
+ dependency: "direct main"
description:
name: webview_flutter_android
- sha256: "3fcca88ee2ae568807ebd42deed235bb8dd8e62b3e4d5caff67daa6bce062cca"
+ sha256: "2a03df01df2fd30b075d1e7f24c28aee593f2e5d5ac4c3c4283c5eda63717b24"
url: "https://pub.dev"
source: hosted
- version: "4.10.9"
+ version: "4.10.13"
webview_flutter_platform_interface:
dependency: transitive
description:
@@ -1547,13 +1555,13 @@ packages:
source: hosted
version: "2.14.0"
webview_flutter_wkwebview:
- dependency: transitive
+ dependency: "direct main"
description:
name: webview_flutter_wkwebview
- sha256: a57b76a081bed3bf3a71a486bdf83642b00f1a7342043d50367cea68f338b1af
+ sha256: "0d85e8bc5db9a7c49f6ff57cbeafc6cd8216ad9c9ebc70b2c4579d955698933a"
url: "https://pub.dev"
source: hosted
- version: "3.23.4"
+ version: "3.24.1"
win32:
dependency: transitive
description:
diff --git a/pubspec.yaml b/pubspec.yaml
index ed420fa..0ab97a8 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -53,7 +53,9 @@ dependencies:
msix: ^3.16.12
flutter_native_splash: ^2.4.7
file_picker: ^10.3.10
-
+ webview_flutter_android: ^4.10.13
+ receive_sharing_intent: ^1.8.1
+ webview_flutter_wkwebview: ^3.24.1
dev_dependencies:
flutter_test:
sdk: flutter