From 4c19a18dee5c11bb0e213e3e1d92707a1e2ce264 Mon Sep 17 00:00:00 2001 From: Julius Caesar Date: Sat, 15 Aug 2026 13:15:41 -0700 Subject: [PATCH 1/3] chore: version changed to 5.8.1+288 --- pubspec.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pubspec.yaml b/pubspec.yaml index 0f9bfa7..a5a219a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: defyx_vpn description: "Defyx VPN - Secure, fast, and reliable VPN service providing global privacy protection with advanced encryption and multi-platform support" publish_to: "none" -version: 5.8.0+287 +version: 5.8.1+288 environment: sdk: ^3.8.0 @@ -105,7 +105,7 @@ msix_config: display_name: Defyx VPN publisher_display_name: UnboundTech UG identity_name: UnboundTechUG.6065AEC5A207 - msix_version: 5.8.0.287 + msix_version: 5.8.1.288 publisher: "CN=62937938-2AE2-4C12-9ED8-4C418C0CADF2" logo_path: assets/images/logo.png capabilities: runFullTrust,internetClientServer,privateNetworkClientServer,unvirtualizedResources From a2833d5219adf2325a9b6c17c3da6feaec80655c Mon Sep 17 00:00:00 2001 From: Julius Caesar Date: Mon, 17 Aug 2026 06:24:28 -0700 Subject: [PATCH 2/3] feat: flowline settings added feat: admob settings added to manage internal ads --- lib/app/ad_director_provider.dart | 19 +--------- lib/app/advertise_director.dart | 38 ++++++++++++------- lib/app/app.dart | 16 +++----- .../local/remote/api/flowline_service.dart | 10 +++++ .../api/flowline_service_interface.dart | 10 ++++- .../local/remote/api/flowline_settings.dart | 12 ++++++ .../secure_storage/secure_storage_const.dart | 1 + 7 files changed, 64 insertions(+), 42 deletions(-) create mode 100644 lib/core/data/local/remote/api/flowline_settings.dart diff --git a/lib/app/ad_director_provider.dart b/lib/app/ad_director_provider.dart index 5ed2879..e437df7 100644 --- a/lib/app/ad_director_provider.dart +++ b/lib/app/ad_director_provider.dart @@ -39,7 +39,6 @@ class AdEnvironment { /// - Desktop platforms: Don't initialize AdMob, only use internal ads /// - Mobile non-Iranian: Initialize AdMob, use both strategies final adEnvironmentProvider = FutureProvider((ref) async { - final isIranian = await AdvertiseDirector.isIranianUser(); final isMobile = Platform.isAndroid || Platform.isIOS; final shouldInitAdMob = isMobile && !isIranian; @@ -63,7 +62,6 @@ final adEnvironmentProvider = FutureProvider((ref) async { /// - Manages connection state listener lifecycle class AdStrategyManager { final Ref _ref; - final AdEnvironment _environment; final InternalAdStrategy _internalStrategy; final GoogleAdStrategy? _googleStrategy; // null for Iranian/desktop users final bool _hasGoogleStrategy; @@ -77,7 +75,6 @@ class AdStrategyManager { required InternalAdStrategy internalStrategy, required GoogleAdStrategy? googleStrategy, }) : _ref = ref, - _environment = environment, _internalStrategy = internalStrategy, _googleStrategy = googleStrategy, _hasGoogleStrategy = googleStrategy != null; @@ -89,7 +86,6 @@ class AdStrategyManager { Color backgroundColor = const Color(0xFF19312F), double cornerRadius = 10.0, }) { - // Create InternalAdStrategy (always needed) final internalStrategy = InternalAdStrategy( backgroundColor: backgroundColor, @@ -119,7 +115,6 @@ class AdStrategyManager { /// Initialize connection state listener void _initializeConnectionListener() { - // Listen to connection changes and delegate to strategies _connectionSubscription = _ref.listen(conn.connectionStateProvider, ( previous, @@ -170,8 +165,6 @@ class AdStrategyManager { required conn.ConnectionStatus previous, required conn.ConnectionStatus current, }) { - - // When connecting: notify internal strategy if (current == conn.ConnectionStatus.connected && previous != conn.ConnectionStatus.connected) { @@ -187,7 +180,6 @@ class AdStrategyManager { // This handles: connected → disconnecting, connected → disconnected else if (previous == conn.ConnectionStatus.connected && current != conn.ConnectionStatus.connected) { - _internalStrategy.onConnectionStateChanged( ref: _ref, previous: previous, @@ -202,7 +194,6 @@ class AdStrategyManager { if (current == conn.ConnectionStatus.disconnected && previous != conn.ConnectionStatus.disconnected) { if (_hasGoogleStrategy) { - _googleStrategy!.onConnectionStateChanged( ref: _ref, previous: previous, @@ -210,7 +201,7 @@ class AdStrategyManager { hasInitialized: true, onRefreshNeeded: () => _googleStrategy.loadAd(ref: _ref), ); - } + } } } @@ -225,10 +216,8 @@ class AdStrategyManager { // This handles the case where app starts in disconnected state (no state change) final initialConnectionState = _ref.read(conn.connectionStateProvider); - if (initialConnectionState.status == conn.ConnectionStatus.disconnected && _hasGoogleStrategy) { - // Simulate a state change to trigger ad load final googleStrategy = _googleStrategy!; googleStrategy.onConnectionStateChanged( @@ -240,7 +229,6 @@ class AdStrategyManager { ); } else if (initialConnectionState.status == conn.ConnectionStatus.connected) { - // Simulate a state change to trigger internal ad load _internalStrategy.onConnectionStateChanged( ref: _ref, @@ -305,15 +293,12 @@ class AdStrategyManager { /// - Session-scoped provider (NOT autoDispose) to prevent recreation churn /// - Manager holds static NativeAd which should persist across widget rebuilds /// - ref.onDispose: Ensures manager.dispose() is called on app exit -final adStrategyManagerProvider = Provider(( - ref, -) { +final adStrategyManagerProvider = Provider((ref) { // Wait for environment to be ready final environmentAsync = ref.watch(adEnvironmentProvider); return environmentAsync.when( data: (environment) { - // Create manager with environment final manager = AdStrategyManager.create( ref: ref, diff --git a/lib/app/advertise_director.dart b/lib/app/advertise_director.dart index 1b8d76d..cf73388 100644 --- a/lib/app/advertise_director.dart +++ b/lib/app/advertise_director.dart @@ -1,4 +1,5 @@ import 'dart:io'; +import 'package:defyx_vpn/core/data/local/remote/api/flowline_service.dart'; import 'package:defyx_vpn/core/data/local/secure_storage/secure_storage.dart'; import 'package:defyx_vpn/core/data/local/secure_storage/secure_storage_const.dart'; import 'package:flutter_timezone/flutter_timezone.dart'; @@ -33,12 +34,21 @@ class AdvertiseDirector { // * GoogleAdStrategy handles AdMob ads (disconnected state ONLY) // * InternalAdStrategy handles internal ads (connected state ONLY) // * AdsWidget coordinates between the two strategies - - // Check for Iranian users first (AdMob disabled for Iran) - if (await isIranianUser()) { + final flowlineSettings = await ref + .read(flowlineServiceProvider) + .getFlowlineSettings(); + + if (flowlineSettings.disabledAdmob.contains("general")) { + return true; + } + + final String currentTimeZone = (await FlutterTimezone.getLocalTimezone()) + .toLowerCase(); + + if (flowlineSettings.disabledAdmob.contains(currentTimeZone)) { return true; } - + if (Platform.isWindows || Platform.isMacOS || Platform.isLinux) { return true; } @@ -47,8 +57,6 @@ class AdvertiseDirector { // AdsWidget automatically initializes both and routes based on connection state: // - When CONNECTED: InternalAdStrategy shows internal ads (timezone-specific or General) // - When DISCONNECTED: GoogleAdStrategy shows AdMob ads - - return false; // Mobile uses dual strategy (both GoogleAdStrategy + InternalAdStrategy) } @@ -67,16 +75,19 @@ class AdvertiseDirector { final String currentTimeZone = await FlutterTimezone.getLocalTimezone(); debugPrint('📍 Ad Manager - Getting ad for timezone: $currentTimeZone'); - final adversies = - await ref.read(secureStorageProvider).readMap(apiAvertiseKey); + final adversies = await ref + .read(secureStorageProvider) + .readMap(apiAvertiseKey); if (adversies['api_advertise'] != null) { final advertiseMap = adversies['api_advertise'] as Map; - + // Try timezone-specific ads first if (advertiseMap.containsKey(currentTimeZone)) { final adsData = advertiseMap[currentTimeZone] as List; - debugPrint('📍 Ad Manager - Found ${adsData.length} timezone-specific ads'); + debugPrint( + '📍 Ad Manager - Found ${adsData.length} timezone-specific ads', + ); if (adsData.isNotEmpty) { final random = Random(); final randomIndex = random.nextInt(adsData.length); @@ -91,11 +102,13 @@ class AdvertiseDirector { } } } - + // Fallback to "General" ads if no timezone-specific ads if (advertiseMap.containsKey('General')) { final adsData = advertiseMap['General'] as List; - debugPrint('📍 Ad Manager - Using "General" fallback ads (${adsData.length} available)'); + debugPrint( + '📍 Ad Manager - Using "General" fallback ads (${adsData.length} available)', + ); if (adsData.isNotEmpty) { final random = Random(); final randomIndex = random.nextInt(adsData.length); @@ -110,7 +123,6 @@ class AdvertiseDirector { } } } - } return {'imageUrl': '', 'clickUrl': ''}; } diff --git a/lib/app/app.dart b/lib/app/app.dart index 2131e68..7d82b4a 100644 --- a/lib/app/app.dart +++ b/lib/app/app.dart @@ -1,10 +1,8 @@ -import 'dart:io'; import 'package:defyx_vpn/app/ad_director_provider.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/vpn.dart'; import 'package:defyx_vpn/modules/core/vpn_bridge.dart'; -import 'package:defyx_vpn/modules/core/desktop_platform_handler.dart'; import 'package:defyx_vpn/modules/main/presentation/widgets/ump_service.dart'; import 'package:defyx_vpn/shared/providers/language_provider.dart'; import 'package:defyx_vpn/shared/providers/ad_readiness_coordinator.dart'; @@ -40,10 +38,9 @@ class _AppState extends ConsumerState { // Single listener for ad readiness state changes ref.listen(adReadinessCoordinatorProvider, (previous, next) { if (previous == null) return; - + // When canInitializeAdMob transitions to true, start the flow if (next.canInitializeAdMob && !previous.canInitializeAdMob) { - environmentAsync.whenData((environment) { if (environment.shouldInitializeAdMob) { _initializeAdFlow(); @@ -54,7 +51,7 @@ class _AppState extends ConsumerState { } }); } - + // When consent completes and we're disconnected, retry ad load if (next.canLoadAds && !previous.canLoadAds) { final connectionState = ref.read(conn.connectionStateProvider).status; @@ -71,10 +68,10 @@ class _AppState extends ConsumerState { future: _initializeApp(), builder: (context, snapshot) { // Check ONCE if we need to initialize ads after app startup (e.g., after migration) - if (snapshot.connectionState == ConnectionState.done && + if (snapshot.connectionState == ConnectionState.done && !_hasCheckedInitialization) { _hasCheckedInitialization = true; - + WidgetsBinding.instance.addPostFrameCallback((_) { _checkAndInitializeAds(environmentAsync); }); @@ -93,7 +90,7 @@ class _AppState extends ConsumerState { } catch (e) { debugPrint('Failed to set cache directory: $e'); } - + await VPN(ProviderScope.containerOf(context, listen: false)).getVPNStatus(); await AlertService().init(); await AnimationService().init(); @@ -102,10 +99,9 @@ class _AppState extends ConsumerState { /// Check if ad initialization should happen (for migration/restart cases) void _checkAndInitializeAds(AsyncValue environmentAsync) { final adReadiness = ref.read(adReadinessCoordinatorProvider); - + // If canInitializeAdMob is already true (e.g., after migration), trigger flow if (adReadiness.canInitializeAdMob) { - environmentAsync.whenData((environment) { if (environment.shouldInitializeAdMob) { _initializeAdFlow(); diff --git a/lib/core/data/local/remote/api/flowline_service.dart b/lib/core/data/local/remote/api/flowline_service.dart index ba097fd..b1f20dc 100644 --- a/lib/core/data/local/remote/api/flowline_service.dart +++ b/lib/core/data/local/remote/api/flowline_service.dart @@ -1,5 +1,6 @@ import 'dart:convert'; import 'package:defyx_vpn/core/data/local/remote/api/flowline_service_interface.dart'; +import 'package:defyx_vpn/core/data/local/remote/api/flowline_settings.dart'; import 'package:defyx_vpn/core/data/local/secure_storage/secure_storage.dart'; import 'package:defyx_vpn/core/data/local/secure_storage/secure_storage_const.dart'; import 'package:defyx_vpn/core/data/local/secure_storage/secure_storage_interface.dart'; @@ -86,7 +87,9 @@ class FlowlineService implements IFlowlineService { final version = decoded['version']?[appBuildType]; final advertiseStorageMap = {'api_advertise': decoded['advertise']}; + final settingsStorageMap = decoded['settings']; await _secureStorage.writeMap(apiAvertiseKey, advertiseStorageMap); + await _secureStorage.writeMap(flowlineSettingsKey, settingsStorageMap); // Save tips if available if (decoded['tips'] != null) { @@ -115,4 +118,11 @@ class FlowlineService implements IFlowlineService { debugPrint('Flowline is empty, cannot save'); } } + + @override + Future getFlowlineSettings() async { + final settingsString = await _secureStorage.read(flowlineSettingsKey); + final decodedSettings = json.decode(settingsString ?? ""); + return FlowlineSettings.fromJson(decodedSettings); + } } diff --git a/lib/core/data/local/remote/api/flowline_service_interface.dart b/lib/core/data/local/remote/api/flowline_service_interface.dart index a144486..6eff89f 100644 --- a/lib/core/data/local/remote/api/flowline_service_interface.dart +++ b/lib/core/data/local/remote/api/flowline_service_interface.dart @@ -1,7 +1,13 @@ +import 'package:defyx_vpn/core/data/local/remote/api/flowline_settings.dart'; + abstract interface class IFlowlineService { Future getFlowline(); Future getCachedFlowLine(); Future decodeAndVerifyFlowline(String flowLine); - Future saveFlowline({required bool offlineMode, String? flowLine, bool forceUpdate = false}); - + Future saveFlowline({ + required bool offlineMode, + String? flowLine, + bool forceUpdate = false, + }); + Future getFlowlineSettings(); } diff --git a/lib/core/data/local/remote/api/flowline_settings.dart b/lib/core/data/local/remote/api/flowline_settings.dart new file mode 100644 index 0000000..de4375d --- /dev/null +++ b/lib/core/data/local/remote/api/flowline_settings.dart @@ -0,0 +1,12 @@ +class FlowlineSettings { + List disabledAdmob; + + FlowlineSettings({required this.disabledAdmob}); + + factory FlowlineSettings.fromJson(Map json) { + List disabledAdmob = List.from(json["disabledAdmob"]); + disabledAdmob = disabledAdmob.map((v) => v.toLowerCase()).toList(); + + return FlowlineSettings(disabledAdmob: disabledAdmob); + } +} diff --git a/lib/core/data/local/secure_storage/secure_storage_const.dart b/lib/core/data/local/secure_storage/secure_storage_const.dart index 9540889..d1624d9 100644 --- a/lib/core/data/local/secure_storage/secure_storage_const.dart +++ b/lib/core/data/local/secure_storage/secure_storage_const.dart @@ -6,3 +6,4 @@ const String apiAvertiseKey = 'api_advertise'; const String apiTipsKey = 'api_tips'; const String premiumTokenKey = 'premium_token'; const String premiumEmailKey = 'premium_email'; +const String flowlineSettingsKey = 'flowline_settings'; From c5b37bdcbb771e5a6cf38b04298dbe990546de2b Mon Sep 17 00:00:00 2001 From: Julius Caesar Date: Tue, 18 Aug 2026 12:17:07 -0700 Subject: [PATCH 3/3] fix: condition of load admob updated --- ios/Podfile.lock | 114 +++++++++++++++--------------- lib/app/ad_director_provider.dart | 12 ++-- lib/app/advertise_director.dart | 15 +--- lib/app/app.dart | 3 +- 4 files changed, 65 insertions(+), 79 deletions(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 539fd9e..11c7a60 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -42,67 +42,67 @@ PODS: - file_picker (0.0.1): - DKImagePickerController/PhotoGallery - Flutter - - Firebase/CoreOnly (12.14.0): - - FirebaseCore (~> 12.14.0) - - Firebase/Crashlytics (12.14.0): + - Firebase/CoreOnly (12.17.0): + - FirebaseCore (~> 12.17.0) + - Firebase/Crashlytics (12.17.0): - Firebase/CoreOnly - - FirebaseCrashlytics (~> 12.14.0) - - firebase_analytics (12.4.2): + - FirebaseCrashlytics (~> 12.17.0) + - firebase_analytics (12.4.6): - firebase_core - - FirebaseAnalytics (= 12.14.0) + - FirebaseAnalytics (= 12.17.0) - Flutter - - firebase_core (4.10.0): - - Firebase/CoreOnly (= 12.14.0) + - firebase_core (4.13.0): + - Firebase/CoreOnly (= 12.17.0) - Flutter - - firebase_crashlytics (5.2.3): - - Firebase/Crashlytics (= 12.14.0) + - firebase_crashlytics (5.2.7): + - Firebase/Crashlytics (= 12.17.0) - firebase_core - Flutter - - FirebaseAnalytics (12.14.0): - - FirebaseAnalytics/Default (= 12.14.0) - - FirebaseCore (~> 12.14.0) - - FirebaseInstallations (~> 12.14.0) + - FirebaseAnalytics (12.17.0): + - FirebaseAnalytics/Default (= 12.17.0) + - FirebaseCore (~> 12.17.0) + - FirebaseInstallations (~> 12.17.0) - GoogleUtilities/AppDelegateSwizzler (~> 8.1) - GoogleUtilities/MethodSwizzler (~> 8.1) - GoogleUtilities/Network (~> 8.1) - "GoogleUtilities/NSData+zlib (~> 8.1)" - nanopb (~> 3.30910.0) - - FirebaseAnalytics/Default (12.14.0): - - FirebaseCore (~> 12.14.0) - - FirebaseInstallations (~> 12.14.0) - - GoogleAppMeasurement/Default (= 12.14.0) + - FirebaseAnalytics/Default (12.17.0): + - FirebaseCore (~> 12.17.0) + - FirebaseInstallations (~> 12.17.0) + - GoogleAppMeasurement/Default (= 12.17.0) - GoogleUtilities/AppDelegateSwizzler (~> 8.1) - GoogleUtilities/MethodSwizzler (~> 8.1) - GoogleUtilities/Network (~> 8.1) - "GoogleUtilities/NSData+zlib (~> 8.1)" - nanopb (~> 3.30910.0) - - FirebaseCore (12.14.0): - - FirebaseCoreInternal (~> 12.14.0) + - FirebaseCore (12.17.0): + - FirebaseCoreInternal (~> 12.17.0) - GoogleUtilities/Environment (~> 8.1) - GoogleUtilities/Logger (~> 8.1) - - FirebaseCoreExtension (12.14.0): - - FirebaseCore (~> 12.14.0) - - FirebaseCoreInternal (12.14.0): + - FirebaseCoreExtension (12.17.0): + - FirebaseCore (~> 12.17.0) + - FirebaseCoreInternal (12.17.0): - "GoogleUtilities/NSData+zlib (~> 8.1)" - - FirebaseCrashlytics (12.14.0): - - FirebaseCore (~> 12.14.0) - - FirebaseInstallations (~> 12.14.0) - - FirebaseRemoteConfigInterop (~> 12.14.0) - - FirebaseSessions (~> 12.14.0) + - FirebaseCrashlytics (12.17.0): + - FirebaseCore (~> 12.17.0) + - FirebaseInstallations (~> 12.17.0) + - FirebaseRemoteConfigInterop (~> 12.17.0) + - FirebaseSessions (~> 12.17.0) - GoogleDataTransport (~> 10.1) - GoogleUtilities/Environment (~> 8.1) - nanopb (~> 3.30910.0) - PromisesObjC (~> 2.4) - - FirebaseInstallations (12.14.0): - - FirebaseCore (~> 12.14.0) + - FirebaseInstallations (12.17.0): + - FirebaseCore (~> 12.17.0) - GoogleUtilities/Environment (~> 8.1) - GoogleUtilities/UserDefaults (~> 8.1) - PromisesObjC (~> 2.4) - - FirebaseRemoteConfigInterop (12.14.0) - - FirebaseSessions (12.14.0): - - FirebaseCore (~> 12.14.0) - - FirebaseCoreExtension (~> 12.14.0) - - FirebaseInstallations (~> 12.14.0) + - FirebaseRemoteConfigInterop (12.17.0) + - FirebaseSessions (12.17.0): + - FirebaseCore (~> 12.17.0) + - FirebaseCoreExtension (~> 12.17.0) + - FirebaseInstallations (~> 12.17.0) - GoogleDataTransport (~> 10.1) - GoogleUtilities/Environment (~> 8.1) - GoogleUtilities/UserDefaults (~> 8.1) @@ -127,29 +127,29 @@ PODS: - GoogleUtilities/Logger (~> 8.1) - GoogleUtilities/Network (~> 8.1) - nanopb (~> 3.30910.0) - - GoogleAppMeasurement/Core (12.14.0): + - GoogleAppMeasurement/Core (12.17.0): - GoogleUtilities/AppDelegateSwizzler (~> 8.1) - GoogleUtilities/MethodSwizzler (~> 8.1) - GoogleUtilities/Network (~> 8.1) - "GoogleUtilities/NSData+zlib (~> 8.1)" - nanopb (~> 3.30910.0) - - GoogleAppMeasurement/Default (12.14.0): + - GoogleAppMeasurement/Default (12.17.0): - GoogleAdsOnDeviceConversion (~> 3.6.0) - - GoogleAppMeasurement/Core (= 12.14.0) - - GoogleAppMeasurement/IdentitySupport (= 12.14.0) + - GoogleAppMeasurement/Core (= 12.17.0) + - GoogleAppMeasurement/IdentitySupport (= 12.17.0) - GoogleUtilities/AppDelegateSwizzler (~> 8.1) - GoogleUtilities/MethodSwizzler (~> 8.1) - GoogleUtilities/Network (~> 8.1) - "GoogleUtilities/NSData+zlib (~> 8.1)" - nanopb (~> 3.30910.0) - - GoogleAppMeasurement/IdentitySupport (12.14.0): - - GoogleAppMeasurement/Core (= 12.14.0) + - GoogleAppMeasurement/IdentitySupport (12.17.0): + - GoogleAppMeasurement/Core (= 12.17.0) - GoogleUtilities/AppDelegateSwizzler (~> 8.1) - GoogleUtilities/MethodSwizzler (~> 8.1) - GoogleUtilities/Network (~> 8.1) - "GoogleUtilities/NSData+zlib (~> 8.1)" - nanopb (~> 3.30910.0) - - GoogleDataTransport (10.1.0): + - GoogleDataTransport (10.1.1): - nanopb (~> 3.30910.0) - PromisesObjC (~> 2.4) - GoogleUserMessagingPlatform (3.1.0) @@ -297,18 +297,18 @@ SPEC CHECKSUMS: DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60 file_picker: a0560bc09d61de87f12d246fc47d2119e6ef37be - Firebase: 7cc10425300768ec86292688af5cb228f0604bde - firebase_analytics: 9ea6c22e60e1d37ee76772de57b4addddb03e276 - firebase_core: 383e19b49a08df5d7a6cf5017616de6a357ed7af - firebase_crashlytics: 88273c8643a258ff74ee26203e319a782aa8b12c - FirebaseAnalytics: 99364329a3ea4d1ab4a3744b5464371b7351c481 - FirebaseCore: 4939b340b9c598dc1f965d68f8fe57e630b65407 - FirebaseCoreExtension: ee3e3697acea1062288b1900bcdcab4d59ab93b4 - FirebaseCoreInternal: 090369a5fffd7423cf88006ab4d2ccc2173a8db9 - FirebaseCrashlytics: 1fabfdca902d0706cb51bf839edb58b45db421ff - FirebaseInstallations: 7cdc919e29dc54306edeffdbdc1eed1a40d7d1e7 - FirebaseRemoteConfigInterop: 40ecdce5a4feee7643b81342f32f1cded905bb07 - FirebaseSessions: c9e7b7829265454f08cb68f7f8a6650c9b221bd4 + Firebase: e8400c058702940d53f31fc0653e4e7a06332326 + firebase_analytics: 4dc4986181e6f58d24f879716f175a53e837c914 + firebase_core: c94f9efeed0ded590b7eecaf6110a374eb865d7a + firebase_crashlytics: 36d85edffd4bfd025c13025b0009f63754983536 + FirebaseAnalytics: 652858a1424ca8c549b62261876814ff56dff650 + FirebaseCore: 52d4512296984a44a8c8870c6494870fc7aeee2f + FirebaseCoreExtension: be2d62bfcf339c7dd94646d43d910aaf37fe17c1 + FirebaseCoreInternal: 3b310521b34a51ce3a6c605e4618b9aef8f6c112 + FirebaseCrashlytics: 33f4bb8b7140f1864a4db6b819edf36e6d2b1b0d + FirebaseInstallations: bd8e80f51d4c8bdc6c872011dede0b800c5b49ea + FirebaseRemoteConfigInterop: 785e617ae4cbc778eea97dde952b88fb934e11ff + FirebaseSessions: 83cf567ddcef20303eb85e003dfdbe030343ef8d Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467 flutter_native_splash: c32d145d68aeda5502d5f543ee38c192065986cf flutter_secure_storage_darwin: acdb3f316ed05a3e68f856e0353b133eec373a23 @@ -316,8 +316,8 @@ SPEC CHECKSUMS: Google-Mobile-Ads-SDK: 1dfb0c3cb46c7e2b00b0f4de74a1e06d9ea25d67 google_mobile_ads: 535223588a6791b7a3cc3513a1bc7b89d12f3e62 GoogleAdsOnDeviceConversion: e3b71da24ff4cf01cf520756ac1c2b93c07b86ff - GoogleAppMeasurement: 1987ffa55055dfb22c52e363c31aa50c1e11d349 - GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7 + GoogleAppMeasurement: 2234668e2451f9da37b93dad7830e457600c008f + GoogleDataTransport: a24e58982ab3ba2f64d79613e027fe7f57e88539 GoogleUserMessagingPlatform: befe603da6501006420c206222acd449bba45a9c GoogleUtilities: 766ace00c6b10d8148408f329d10c4f051931850 nanopb: fad817b59e0457d11a5dfbde799381cd727c1275 @@ -333,4 +333,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 4d58577ccbfce16136516f00e34396d0d7432493 -COCOAPODS: 1.16.2 +COCOAPODS: 1.17.0 diff --git a/lib/app/ad_director_provider.dart b/lib/app/ad_director_provider.dart index e437df7..d3a006b 100644 --- a/lib/app/ad_director_provider.dart +++ b/lib/app/ad_director_provider.dart @@ -14,19 +14,19 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; /// Cached environment details to avoid repeated timezone/platform detection. /// This is computed once at app startup and used throughout the app lifecycle. class AdEnvironment { - final bool isIranian; + final bool adMobIsDisabled; final bool isMobilePlatform; final bool shouldInitializeAdMob; const AdEnvironment({ - required this.isIranian, + required this.adMobIsDisabled, required this.isMobilePlatform, required this.shouldInitializeAdMob, }); @override String toString() => - 'AdEnvironment(isIranian: $isIranian, isMobile: $isMobilePlatform, initAdMob: $shouldInitializeAdMob)'; + 'AdEnvironment(isIranian: $adMobIsDisabled, isMobile: $isMobilePlatform, initAdMob: $shouldInitializeAdMob)'; } /// Provider for ad environment configuration @@ -39,12 +39,12 @@ class AdEnvironment { /// - Desktop platforms: Don't initialize AdMob, only use internal ads /// - Mobile non-Iranian: Initialize AdMob, use both strategies final adEnvironmentProvider = FutureProvider((ref) async { - final isIranian = await AdvertiseDirector.isIranianUser(); + final adMobIsDisabled = await AdvertiseDirector.shouldUseInternalAds(ref); final isMobile = Platform.isAndroid || Platform.isIOS; - final shouldInitAdMob = isMobile && !isIranian; + final shouldInitAdMob = isMobile && !adMobIsDisabled; final environment = AdEnvironment( - isIranian: isIranian, + adMobIsDisabled: adMobIsDisabled, isMobilePlatform: isMobile, shouldInitializeAdMob: shouldInitAdMob, ); diff --git a/lib/app/advertise_director.dart b/lib/app/advertise_director.dart index cf73388..96c2efe 100644 --- a/lib/app/advertise_director.dart +++ b/lib/app/advertise_director.dart @@ -2,6 +2,7 @@ import 'dart:io'; import 'package:defyx_vpn/core/data/local/remote/api/flowline_service.dart'; import 'package:defyx_vpn/core/data/local/secure_storage/secure_storage.dart'; import 'package:defyx_vpn/core/data/local/secure_storage/secure_storage_const.dart'; +import 'package:defyx_vpn/modules/core/log.dart'; import 'package:flutter_timezone/flutter_timezone.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter/foundation.dart'; @@ -12,20 +13,6 @@ class AdvertiseDirector { AdvertiseDirector(this.ref); - /// Check if user is from Iran based on device timezone - /// Iranian users should not see AdMob ads (only internal ads) - static Future isIranianUser() async { - try { - final String currentTimeZone = await FlutterTimezone.getLocalTimezone(); - // Asia/Tehran is the timezone for Iran - final isIran = currentTimeZone == 'Asia/Tehran'; - return isIran; - } catch (e) { - debugPrint('⚠️ Error detecting timezone: $e'); - return false; - } - } - static Future shouldUseInternalAds(Ref ref) async { // STRATEGY SELECTION (for backward compatibility with desktop): // - Desktop (Windows/macOS/Linux) → InternalAdStrategy only (no AdMob support) diff --git a/lib/app/app.dart b/lib/app/app.dart index 7d82b4a..06102d3 100644 --- a/lib/app/app.dart +++ b/lib/app/app.dart @@ -8,7 +8,6 @@ import 'package:defyx_vpn/shared/providers/language_provider.dart'; import 'package:defyx_vpn/shared/providers/ad_readiness_coordinator.dart'; import 'package:defyx_vpn/shared/providers/connection_state_provider.dart' as conn; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -46,7 +45,7 @@ class _AppState extends ConsumerState { _initializeAdFlow(); } else { debugPrint( - '📱 Using internal ads only (${environment.isIranian ? "Iranian user" : "desktop platform"})', + '📱 Using internal ads only (${environment.adMobIsDisabled ? "Iranian user" : "desktop platform"})', ); } });