@@ -3,6 +3,7 @@ import 'package:flutter_news_app_mobile_client_full_source_code/ads/ad_provider.
3
3
import 'package:flutter_news_app_mobile_client_full_source_code/ads/models/ad_theme_style.dart' ;
4
4
import 'package:flutter_news_app_mobile_client_full_source_code/ads/models/inline_ad.dart' ;
5
5
import 'package:flutter_news_app_mobile_client_full_source_code/ads/models/interstitial_ad.dart' ;
6
+ import 'package:flutter_news_app_mobile_client_full_source_code/app/config/app_environment.dart' ;
6
7
import 'package:logging/logging.dart' ;
7
8
8
9
/// {@template ad_service}
@@ -20,13 +21,21 @@ class AdService {
20
21
/// These providers will be used to load ads from specific ad networks.
21
22
AdService ({
22
23
required Map <AdPlatformType , AdProvider > adProviders,
24
+ required AppEnvironment environment,
23
25
Logger ? logger,
24
26
}) : _adProviders = adProviders,
27
+ _environment = environment,
25
28
_logger = logger ?? Logger ('AdService' );
26
29
27
30
final Map <AdPlatformType , AdProvider > _adProviders;
31
+ final AppEnvironment _environment;
28
32
final Logger _logger;
29
33
34
+ // Configurable retry parameters for ad loading.
35
+ // TODO(fulleni): Make this configurable through the remote config.
36
+ static const int _maxAdLoadRetries = 2 ;
37
+ static const Duration _adLoadRetryDelay = Duration (seconds: 1 );
38
+
30
39
/// Initializes all underlying ad providers.
31
40
///
32
41
/// This should be called once at application startup to ensure all
@@ -39,6 +48,41 @@ class AdService {
39
48
_logger.info ('AdService: AdService initialized.' );
40
49
}
41
50
51
+ /// Disposes of an ad object by delegating to the appropriate [AdProvider] .
52
+ ///
53
+ /// This method is called by the [InlineAdCacheService] to ensure that
54
+ /// inline ad resources are released when an ad is removed from the cache
55
+ /// or replaced. It also handles disposal of interstitial ads.
56
+ Future <void > disposeAd (dynamic adModel) async {
57
+ // Determine the AdPlatformType from the adModel if it's an InlineAd or InterstitialAd.
58
+ AdPlatformType ? providerType;
59
+ Object ? adObject; // To hold the actual ad object
60
+
61
+ if (adModel is InlineAd ) {
62
+ providerType = adModel.provider;
63
+ adObject = adModel.adObject;
64
+ } else if (adModel is InterstitialAd ) {
65
+ providerType = adModel.provider;
66
+ adObject = adModel.adObject;
67
+ }
68
+
69
+ if (providerType != null && adObject != null ) {
70
+ final adProvider = _adProviders[providerType];
71
+ if (adProvider != null ) {
72
+ await adProvider.disposeAd (adObject); // Pass the actual ad object
73
+ } else {
74
+ _logger.warning (
75
+ 'AdService: No AdProvider found for type $providerType to dispose ad.' ,
76
+ );
77
+ }
78
+ } else {
79
+ _logger.warning (
80
+ 'AdService: Cannot determine AdPlatformType or ad object for ad model of type '
81
+ '${adModel .runtimeType }. Cannot dispose ad.' ,
82
+ );
83
+ }
84
+ }
85
+
42
86
/// Retrieves a loaded inline ad (native or banner) for display in a feed.
43
87
///
44
88
/// This method delegates the ad loading to the appropriate [AdProvider]
@@ -95,6 +139,18 @@ class AdService {
95
139
}
96
140
97
141
final primaryAdPlatform = adConfig.primaryAdPlatform;
142
+
143
+ // If RemoteConfig specifies AdPlatformType.demo but the app is not in demo environment,
144
+ // log a warning and skip ad load.
145
+ if (primaryAdPlatform == AdPlatformType .demo &&
146
+ _environment != AppEnvironment .demo) {
147
+ _logger.warning (
148
+ 'AdService: RemoteConfig specifies AdPlatformType.demo as primary '
149
+ 'ad platform, but app is not in demo environment. Skipping interstitial ad load.' ,
150
+ );
151
+ return null ;
152
+ }
153
+
98
154
final adProvider = _adProviders[primaryAdPlatform];
99
155
100
156
if (adProvider == null ) {
@@ -224,6 +280,18 @@ class AdService {
224
280
}
225
281
226
282
final primaryAdPlatform = adConfig.primaryAdPlatform;
283
+
284
+ // If RemoteConfig specifies AdPlatformType.demo but the app is not in demo environment,
285
+ // log a warning and skip ad load.
286
+ if (primaryAdPlatform == AdPlatformType .demo &&
287
+ _environment != AppEnvironment .demo) {
288
+ _logger.warning (
289
+ 'AdService: RemoteConfig specifies AdPlatformType.demo as primary '
290
+ 'ad platform, but app is not in demo environment. Skipping inline ad load.' ,
291
+ );
292
+ return null ;
293
+ }
294
+
227
295
final adProvider = _adProviders[primaryAdPlatform];
228
296
229
297
if (adProvider == null ) {
@@ -258,53 +326,69 @@ class AdService {
258
326
return null ;
259
327
}
260
328
261
- _logger.info (
262
- 'AdService: Requesting $adType ad from $primaryAdPlatform AdProvider with ID: $adId '
263
- 'for ${feedAd ? 'feed' : 'in-article' } placement.' ,
264
- );
265
- try {
266
- InlineAd ? loadedAd;
267
- // For in-article banner ads, bannerAdShape dictates the visual style.
268
- // For feed ads, headlineImageStyle is still relevant.
269
- final effectiveHeadlineImageStyle = feedAd ? headlineImageStyle : null ;
270
-
271
- switch (adType) {
272
- case AdType .native :
273
- loadedAd = await adProvider.loadNativeAd (
274
- adPlatformIdentifiers: platformAdIdentifiers,
275
- adId: adId,
276
- adThemeStyle: adThemeStyle,
277
- headlineImageStyle: effectiveHeadlineImageStyle,
278
- );
279
- case AdType .banner:
280
- loadedAd = await adProvider.loadBannerAd (
281
- adPlatformIdentifiers: platformAdIdentifiers,
282
- adId: adId,
283
- adThemeStyle: adThemeStyle,
284
- headlineImageStyle: effectiveHeadlineImageStyle,
285
- );
286
- case AdType .interstitial:
287
- case AdType .video:
288
- _logger.warning (
289
- 'AdService: Attempted to load $adType ad using _loadInlineAd. This is not supported.' ,
290
- );
291
- return null ;
329
+ for (var attempt = 0 ; attempt <= _maxAdLoadRetries; attempt++ ) {
330
+ if (attempt > 0 ) {
331
+ _logger.info (
332
+ 'AdService: Retrying $adType ad load (attempt $attempt ) for ID: $adId '
333
+ 'after $_adLoadRetryDelay delay.' ,
334
+ );
335
+ await Future <void >.delayed (_adLoadRetryDelay);
292
336
}
293
337
294
- if (loadedAd != null ) {
295
- _logger.info ('AdService: $adType ad successfully loaded.' );
296
- return loadedAd;
297
- } else {
298
- _logger.info ('AdService: No $adType ad loaded by AdProvider.' );
299
- return null ;
338
+ try {
339
+ _logger.info (
340
+ 'AdService: Requesting $adType ad from $primaryAdPlatform AdProvider with ID: $adId '
341
+ 'for ${feedAd ? 'feed' : 'in-article' } placement.' ,
342
+ );
343
+ InlineAd ? loadedAd;
344
+ // For in-article banner ads, bannerAdShape dictates the visual style.
345
+ // For feed ads, headlineImageStyle is still relevant.
346
+ final effectiveHeadlineImageStyle = feedAd ? headlineImageStyle : null ;
347
+
348
+ switch (adType) {
349
+ case AdType .native :
350
+ loadedAd = await adProvider.loadNativeAd (
351
+ adPlatformIdentifiers: platformAdIdentifiers,
352
+ adId: adId,
353
+ adThemeStyle: adThemeStyle,
354
+ headlineImageStyle: effectiveHeadlineImageStyle,
355
+ );
356
+ case AdType .banner:
357
+ loadedAd = await adProvider.loadBannerAd (
358
+ adPlatformIdentifiers: platformAdIdentifiers,
359
+ adId: adId,
360
+ adThemeStyle: adThemeStyle,
361
+ headlineImageStyle: effectiveHeadlineImageStyle,
362
+ );
363
+ case AdType .interstitial:
364
+ case AdType .video:
365
+ _logger.warning (
366
+ 'AdService: Attempted to load $adType ad using _loadInlineAd. This is not supported.' ,
367
+ );
368
+ return null ;
369
+ }
370
+
371
+ if (loadedAd != null ) {
372
+ _logger.info ('AdService: $adType ad successfully loaded.' );
373
+ return loadedAd;
374
+ } else {
375
+ _logger.info ('AdService: No $adType ad loaded by AdProvider.' );
376
+ // If no ad is returned, it might be a "no fill" scenario.
377
+ // Continue to the next retry attempt.
378
+ }
379
+ } catch (e, s) {
380
+ _logger.severe (
381
+ 'AdService: Error getting $adType ad from AdProvider on attempt $attempt : $e ' ,
382
+ e,
383
+ s,
384
+ );
385
+ // If an exception occurs, log it and continue to the next retry attempt.
300
386
}
301
- } catch (e, s) {
302
- _logger.severe (
303
- 'AdService: Error getting $adType ad from AdProvider: $e ' ,
304
- e,
305
- s,
306
- );
307
- return null ;
308
387
}
388
+
389
+ _logger.warning (
390
+ 'AdService: All retry attempts failed for $adType ad with ID: $adId .' ,
391
+ );
392
+ return null ;
309
393
}
310
394
}
0 commit comments