Skip to content

Commit 258887b

Browse files
Merge branch 'dev' into release
2 parents 3393d3b + a624285 commit 258887b

5 files changed

Lines changed: 46 additions & 78 deletions

File tree

lib/app/app.dart

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import 'package:defyx_vpn/app/router/app_router.dart';
55
import 'package:defyx_vpn/core/theme/app_theme.dart';
66
import 'package:defyx_vpn/modules/core/vpn.dart';
77
import 'package:defyx_vpn/modules/core/desktop_platform_handler.dart';
8+
import 'package:defyx_vpn/modules/main/presentation/widgets/ump_service.dart';
89
import 'package:flutter/foundation.dart';
910
import 'package:flutter/material.dart';
1011
import 'package:flutter_screenutil/flutter_screenutil.dart';
@@ -46,14 +47,21 @@ class App extends ConsumerWidget {
4647
if (shouldUseInternalAds) {
4748
debugPrint('Using internal ads');
4849
} else {
49-
_initializeMobileAds();
50+
_initializeMobileAdsWithConsent();
5051
}
5152
}
5253

53-
Future<void> _initializeMobileAds() async {
54+
Future<void> _initializeMobileAdsWithConsent() async {
5455
try {
5556
if (Platform.isAndroid || Platform.isIOS) {
56-
await MobileAds.instance.initialize();
57+
// Request UMP consent first (required for GDPR compliance)
58+
UmpService.requestConsent(
59+
onDone: () async {
60+
// Initialize Mobile Ads after consent flow completes
61+
await MobileAds.instance.initialize();
62+
debugPrint('Google AdMob initialized with UMP consent');
63+
},
64+
);
5765
}
5866
} catch (error) {
5967
debugPrint('Error initializing Google AdMob: $error');

lib/modules/main/presentation/screens/main_screen.dart

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ class _MainScreenState extends ConsumerState<MainScreen> {
3434
final AnimationService _animationService = AnimationService();
3535
bool _showHeaderShadow = false;
3636
ConnectionStatus? _previousConnectionStatus;
37-
bool? _previousShowCountdown;
3837
late MainScreenLogic _logic;
3938
late final GoogleAds _googleAds;
4039
late ScrollManager _scrollManager;
@@ -109,10 +108,6 @@ class _MainScreenState extends ConsumerState<MainScreen> {
109108
});
110109
}
111110

112-
void _handleAdsStateChange(bool showCountdown) {
113-
_scrollManager.handleAdsStateChange(showCountdown);
114-
}
115-
116111
void _checkInitialConnectionState() {
117112
Future.delayed(const Duration(milliseconds: 1000), () {
118113
if (!mounted) return;
@@ -178,15 +173,6 @@ class _MainScreenState extends ConsumerState<MainScreen> {
178173
_previousConnectionStatus = connectionState.status;
179174
_handleConnectionStateChange(connectionState.status);
180175
}
181-
182-
if (mounted &&
183-
_previousShowCountdown != null &&
184-
_previousShowCountdown != adsState.showCountdown &&
185-
_previousShowCountdown! &&
186-
!adsState.showCountdown) {
187-
_handleAdsStateChange(adsState.showCountdown);
188-
}
189-
_previousShowCountdown = adsState.showCountdown;
190176
});
191177

192178
return MainScreenBackground(
@@ -240,7 +226,13 @@ class _MainScreenState extends ConsumerState<MainScreen> {
240226
),
241227
_buildContentSection(
242228
connectionState.status, adsState),
243-
SizedBox(height: 0.15.sh),
229+
SizedBox(
230+
height: connectionState.status ==
231+
ConnectionStatus.connected &&
232+
adsState.showCountdown
233+
? 140.h
234+
: 0.15.sh,
235+
),
244236
],
245237
),
246238
],
@@ -299,8 +291,8 @@ class _MainScreenState extends ConsumerState<MainScreen> {
299291
);
300292

301293
default:
302-
final shouldShowAd =
303-
status == ConnectionStatus.connected && adsState.showCountdown;
294+
// Show ads only when connected to VPN
295+
final shouldShowAd = status == ConnectionStatus.connected && adsState.showCountdown;
304296

305297
return SizedBox(
306298
height: 280.h,

lib/modules/main/presentation/widgets/google_ads.dart

Lines changed: 19 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -19,32 +19,28 @@ Future<bool> _shouldShowGoogleAds(WidgetRef ref) async {
1919

2020
class GoogleAdsState {
2121
final bool nativeAdIsLoaded;
22+
final bool adLoadFailed;
2223
final int countdown;
2324
final bool showCountdown;
24-
final bool shouldDisposeAd;
25-
final bool adLoadFailed;
2625

2726
const GoogleAdsState({
2827
this.nativeAdIsLoaded = false,
28+
this.adLoadFailed = false,
2929
this.countdown = _countdownDuration,
3030
this.showCountdown = true,
31-
this.shouldDisposeAd = false,
32-
this.adLoadFailed = false,
3331
});
3432

3533
GoogleAdsState copyWith({
3634
bool? nativeAdIsLoaded,
35+
bool? adLoadFailed,
3736
int? countdown,
3837
bool? showCountdown,
39-
bool? shouldDisposeAd,
40-
bool? adLoadFailed,
4138
}) {
4239
return GoogleAdsState(
4340
nativeAdIsLoaded: nativeAdIsLoaded ?? this.nativeAdIsLoaded,
41+
adLoadFailed: adLoadFailed ?? this.adLoadFailed,
4442
countdown: countdown ?? this.countdown,
4543
showCountdown: showCountdown ?? this.showCountdown,
46-
shouldDisposeAd: shouldDisposeAd ?? this.shouldDisposeAd,
47-
adLoadFailed: adLoadFailed ?? this.adLoadFailed,
4844
);
4945
}
5046
}
@@ -62,35 +58,27 @@ class GoogleAdsNotifier extends StateNotifier<GoogleAdsState> {
6258
state = state.copyWith(
6359
countdown: _countdownDuration,
6460
showCountdown: true,
65-
shouldDisposeAd: false,
6661
);
6762
_countdownTimer = Timer.periodic(const Duration(seconds: 1), (timer) {
6863
if (state.countdown > 0) {
6964
state = state.copyWith(countdown: state.countdown - 1);
7065
} else {
7166
state = state.copyWith(
7267
showCountdown: false,
73-
shouldDisposeAd: true,
74-
nativeAdIsLoaded: false,
68+
// Keep nativeAdIsLoaded true - ad stays loaded, just hidden
7569
);
7670
timer.cancel();
7771
}
7872
});
7973
}
8074

81-
void resestCountDown() {
82-
state = state.copyWith(countdown: _countdownDuration);
83-
}
84-
8575
void setAdLoaded(bool isLoaded) {
8676
debugPrint('Ad loaded: $isLoaded');
8777
state = state.copyWith(
8878
nativeAdIsLoaded: isLoaded,
8979
adLoadFailed: false,
9080
);
91-
if (isLoaded &&
92-
state.showCountdown &&
93-
state.countdown == _countdownDuration) {
81+
if (isLoaded && state.showCountdown && state.countdown == _countdownDuration) {
9482
startCountdownTimer();
9583
}
9684
}
@@ -102,10 +90,6 @@ class GoogleAdsNotifier extends StateNotifier<GoogleAdsState> {
10290
);
10391
}
10492

105-
void acknowledgeDisposal() {
106-
state = state.copyWith(shouldDisposeAd: false);
107-
}
108-
10993
void resetState() {
11094
state = const GoogleAdsState();
11195
}
@@ -188,6 +172,8 @@ class _GoogleAdsState extends ConsumerState<GoogleAds> {
188172
void _initializeAds() async {
189173
if (_isDisposed || _hasInitialized) return;
190174

175+
_hasInitialized = true;
176+
191177
try {
192178
// Disable Google Ads on non-mobile platforms.
193179
if (!(Platform.isAndroid || Platform.isIOS)) {
@@ -329,6 +315,9 @@ class _GoogleAdsState extends ConsumerState<GoogleAds> {
329315
@override
330316
void dispose() {
331317
_isDisposed = true;
318+
_nativeAd?.dispose();
319+
_nativeAd = null;
320+
_globalAdInitialized = false;
332321
super.dispose();
333322
}
334323

@@ -338,24 +327,17 @@ class _GoogleAdsState extends ConsumerState<GoogleAds> {
338327
final shouldShowGoogle = ref.watch(shouldShowGoogleAdsProvider);
339328
final customAdData = ref.watch(customAdDataProvider);
340329

330+
// Restart countdown on new connection for fresh 60-second impressions
331+
// Ad stays loaded through disconnect/reconnect - no disposal
341332
ref.listen(connectionStateProvider, (previous, next) {
342-
if (next.status == ConnectionStatus.disconnected && !_isDisposed) {
343-
if (_nativeAd != null) {
344-
_nativeAd?.dispose();
345-
_nativeAd = null;
346-
}
347-
if (mounted) {
348-
setState(() {
349-
_isLoading = false;
350-
});
351-
}
352-
ref.read(googleAdsProvider.notifier).acknowledgeDisposal();
353-
_globalAdInitialized = false;
354-
}
355-
if (next.status == ConnectionStatus.connected) {
356-
ref.read(googleAdsProvider.notifier).resestCountDown();
333+
if (next.status == ConnectionStatus.connected &&
334+
previous?.status != ConnectionStatus.connected &&
335+
adsState.nativeAdIsLoaded &&
336+
!_isDisposed) {
337+
ref.read(googleAdsProvider.notifier).startCountdownTimer();
357338
}
358339
});
340+
359341
return SizedBox(
360342
height: 280.h,
361343
width: 336.w,

lib/modules/main/presentation/widgets/tips_slider_section.dart

Lines changed: 5 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -15,24 +15,10 @@ class TipsSliderSection extends StatelessWidget {
1515
Widget build(BuildContext context) {
1616
final isDisconnected = status == ConnectionStatus.disconnected;
1717

18-
return AnimatedSize(
19-
duration: const Duration(milliseconds: 400),
20-
curve: Curves.easeInOut,
21-
alignment: Alignment.topCenter,
22-
child: AnimatedSlide(
23-
duration: const Duration(milliseconds: 500),
24-
curve: Curves.easeOutBack,
25-
offset: isDisconnected ? Offset.zero : const Offset(0.0, 0.3),
26-
child: AnimatedOpacity(
27-
duration: const Duration(milliseconds: 400),
28-
opacity: isDisconnected ? 1.0 : 0.0,
29-
child: isDisconnected
30-
? Column(
31-
children: [SizedBox(height: 0.05.sh), const TipsSlider()],
32-
)
33-
: const SizedBox.shrink(),
34-
),
35-
),
36-
);
18+
return isDisconnected
19+
? Column(
20+
children: [SizedBox(height: 0.05.sh), const TipsSlider()],
21+
)
22+
: const SizedBox.shrink();
3723
}
3824
}

pubspec.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name: defyx_vpn
22
description: "A new Vpn app"
33
publish_to: "none"
4-
version: 4.4.0+242
4+
version: 4.4.1+243
55

66
environment:
77
sdk: ^3.5.3
@@ -103,7 +103,7 @@ msix_config:
103103
display_name: Defyx VPN
104104
publisher_display_name: UnboundTech UG
105105
identity_name: UnboundTechUG.6065AEC5A207
106-
msix_version: 4.4.0.0
106+
msix_version: 4.4.1.0
107107
publisher: "CN=62937938-2AE2-4C12-9ED8-4C418C0CADF2"
108108
logo_path: assets/images/logo.png
109109
capabilities: runFullTrust,internetClientServer,privateNetworkClientServer,unvirtualizedResources

0 commit comments

Comments
 (0)