727 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			Dart
		
	
	
			
		
		
	
	
			727 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			Dart
		
	
	
| import 'dart:async';
 | |
| import 'dart:convert';
 | |
| import 'dart:ui';
 | |
| 
 | |
| import 'package:connectivity_plus/connectivity_plus.dart';
 | |
| import 'package:guru_app/account/model/user.dart';
 | |
| import 'package:guru_app/ads/applovin/banner/applovin_banner_ads.dart';
 | |
| import 'package:guru_app/ads/core/ads.dart';
 | |
| import 'package:guru_app/ads/core/ads_config.dart';
 | |
| import 'package:guru_app/ads/core/ads_impression.dart';
 | |
| import 'package:guru_app/ads/core/strategy/interstitial/max_strategy_interstitial_ads.dart';
 | |
| import 'package:guru_app/financial/asset/assets_model.dart';
 | |
| import 'package:guru_app/financial/asset/assets_store.dart';
 | |
| import 'package:guru_app/financial/iap/iap_manager.dart';
 | |
| import 'package:guru_app/financial/iap/iap_model.dart';
 | |
| import 'package:guru_app/firebase/remoteconfig/remote_config_manager.dart';
 | |
| import 'package:guru_app/guru_app.dart';
 | |
| import 'package:guru_utils/lifecycle/lifecycle_manager.dart';
 | |
| import 'package:guru_app/property/app_property.dart';
 | |
| import 'package:guru_app/property/property_keys.dart';
 | |
| import 'package:guru_app/property/settings/guru_settings.dart';
 | |
| import 'package:guru_applovin_flutter/guru_applovin_flutter.dart';
 | |
| import 'package:guru_utils/datetime/datetime_utils.dart';
 | |
| import 'package:guru_utils/extensions/extensions.dart';
 | |
| import 'package:guru_utils/network/network_utils.dart';
 | |
| import 'package:guru_utils/tuple/tuple.dart';
 | |
| import 'package:guru_utils/ads/ads.dart';
 | |
| 
 | |
| import 'applovin/interstitial/applovin_interstitial_ads.dart';
 | |
| import 'applovin/rewarded/applovin_rewarded_ads.dart';
 | |
| import 'utils/ads_exception.dart';
 | |
| import 'package:device_info_plus/device_info_plus.dart';
 | |
| 
 | |
| part 'ads_global_property.dart';
 | |
| 
 | |
| /// Created by Haoyi on 2022/3/2
 | |
| 
 | |
| class AdsManager extends AdsManagerDelegate {
 | |
|   static final AdsManager _instance = AdsManager._();
 | |
| 
 | |
|   static AdsManager get instance => _instance;
 | |
| 
 | |
|   AdsManager._();
 | |
| 
 | |
|   final Map<AdUnitId, Ads> interstitialAds = {};
 | |
| 
 | |
|   final Map<AdUnitId, ApplovinRewardedAds> rewardsAds = {};
 | |
| 
 | |
|   final AdImpressionController adImpressionController =
 | |
|       AdImpressionController();
 | |
| 
 | |
|   final BehaviorSubject<AdsConfig> _adsConfigSubject =
 | |
|       BehaviorSubject.seeded(AdsConfig.defaultAdsConfig);
 | |
| 
 | |
|   final BehaviorSubject<AdsProfile> _adsProfileSubject =
 | |
|       BehaviorSubject.seeded(GuruApp.instance.adsProfile);
 | |
| 
 | |
|   final BehaviorSubject<bool> _initializedSubject =
 | |
|       BehaviorSubject.seeded(false);
 | |
| 
 | |
|   final BehaviorSubject<bool> noBannerAndInterstitialAdsSubject =
 | |
|       BehaviorSubject.seeded(false);
 | |
| 
 | |
|   final Map<String, dynamic> adsGlobalProperties = <String, dynamic>{};
 | |
| 
 | |
|   static const Set<String> _reservedKeywords = {
 | |
|     "app_version",
 | |
|     "lt",
 | |
|     "paid",
 | |
|     "blv",
 | |
|     "os_version",
 | |
|     "connection"
 | |
|   };
 | |
| 
 | |
|   static const List<int> ltSamples = [
 | |
|     0,
 | |
|     1,
 | |
|     2,
 | |
|     3,
 | |
|     4,
 | |
|     5,
 | |
|     6,
 | |
|     14,
 | |
|     30,
 | |
|     60,
 | |
|     90,
 | |
|     120,
 | |
|     180
 | |
|   ];
 | |
| 
 | |
|   @override
 | |
|   Stream<bool> get observableInitialized => _initializedSubject.stream;
 | |
| 
 | |
|   ConnectivityResult get connectivityStatus => NetworkUtils.currentConnectivityStatus;
 | |
| 
 | |
|   Stream<ConnectivityResult> get observableConnectivityStatus =>
 | |
|       NetworkUtils.observableConnectivityStatus;
 | |
| 
 | |
|   @override
 | |
|   Stream<bool> get observableNoAds => noBannerAndInterstitialAdsSubject.stream;
 | |
| 
 | |
|   final CompositeSubscription subscriptions = CompositeSubscription();
 | |
| 
 | |
|   AdsProfile get adsProfile => _adsProfileSubject.value;
 | |
| 
 | |
|   AdsConfig get adsConfig => _adsConfigSubject.value;
 | |
| 
 | |
|   bool get hasAmazonBannerAds => adsConfig.bannerConfig.amazonEnable;
 | |
| 
 | |
|   bool get hasAmazonInterstitialAds =>
 | |
|       adsConfig.interstitialConfig.amazonEnable;
 | |
| 
 | |
|   bool get hasAmazonAds => hasAmazonBannerAds || hasAmazonInterstitialAds;
 | |
| 
 | |
|   final BehaviorSubject<Map<String, String>> keywordsSubject =
 | |
|       BehaviorSubject.seeded({});
 | |
| 
 | |
|   Stream<Map<String, String>> get observableKeywords => keywordsSubject.stream;
 | |
| 
 | |
|   Map<String, String> get adsKeywords => keywordsSubject.value;
 | |
| 
 | |
|   String? consentTestDeviceId;
 | |
| 
 | |
|   int? consentDebugGeography;
 | |
| 
 | |
|   static final RegExp _nonAlphaNumeric = RegExp('[^a-zA-Z0-9_]');
 | |
|   static final RegExp _alpha = RegExp('[a-zA-Z]');
 | |
| 
 | |
|   @override
 | |
|   bool get isPurchasedNoAd => noBannerAndInterstitialAdsSubject.value;
 | |
| 
 | |
|   void setProperty(String key, String value) {
 | |
|     adsGlobalProperties[key] = value;
 | |
|   }
 | |
| 
 | |
|   void setNoAds(bool noAds) {
 | |
|     noBannerAndInterstitialAdsSubject.addIfChanged(noAds);
 | |
|     GuruSettings.instance.isNoAds.set(noAds);
 | |
|     GuruAnalytics.instance
 | |
|         .setUserProperty("user_type", noAds ? "noads" : "default");
 | |
|     setProperty("user_type", noAds ? "noads" : "default");
 | |
|   }
 | |
| 
 | |
|   void ensureInitialize() {}
 | |
| 
 | |
|   void listenIap() {
 | |
|     final obs = Rx.combineLatest2<bool, AssetsStore<Asset>,
 | |
|             Tuple2<bool, AssetsStore<Asset>>>(
 | |
|         IapManager.instance.observableAvailable,
 | |
|         IapManager.instance.observableAssetStore,
 | |
|         (a, b) => Tuple2(a, b));
 | |
|     subscriptions.add(obs.listen((tuple) {
 | |
|       final available = tuple.item1;
 | |
|       final purchasedStore = tuple.item2;
 | |
|       if (available && purchasedStore.isActive) {
 | |
|         final tempIsNoAds = purchasedStore
 | |
|             .existsAssets(GuruApp.instance.productProfile.noAdsCapIds);
 | |
|         final isNoAds = isPurchasedNoAd;
 | |
|         Log.i(
 | |
|             "purchased store changed active! tempIsNoAds:$tempIsNoAds isNoAds:$isNoAds",
 | |
|             syncFirebase: true);
 | |
|         if (isNoAds != tempIsNoAds) {
 | |
|           if (!tempIsNoAds) {
 | |
|             GuruAnalytics.instance.logException(NoAdsException(
 | |
|                 "The payment system is abnormal, it shouldn't appear that the purchased item become unpurchased"));
 | |
|           }
 | |
| 
 | |
|           setNoAds(tempIsNoAds);
 | |
|         }
 | |
|       }
 | |
|     }));
 | |
|   }
 | |
|   static bool initializedSdk = false;
 | |
|   Future initialize({SaasUser? saasUser}) async {
 | |
|     _adsProfileSubject.addEx(GuruApp.instance.adsProfile);
 | |
|     await initEnv();
 | |
|     final connected = await NetworkUtils.isNetworkConnected();
 | |
|     Log.d("adsManager initialize connected:$connected", tag: "Ads");
 | |
|     if (connected) {
 | |
|       await initSdk(
 | |
|           saasUser: saasUser,
 | |
|           onInitialized: () {
 | |
|             // loadAds();
 | |
|             adImpressionController.init();
 | |
|             checkAndPreload();
 | |
|             // GuruSettings.instance.totalLevelUp
 | |
|             //     .observe()
 | |
|             //     .throttleTime(const Duration(seconds: 1))
 | |
|             //     .listen((count) {
 | |
|             //   checkAndPreload();
 | |
|             // });
 | |
| 
 | |
|             Log.i("ADS Initialized", tag: "Ads", syncFirebase: true);
 | |
|           });
 | |
|     } else {
 | |
|       
 | |
|       NetworkUtils.observableConnectivityTrack.listen((track) async {
 | |
|         if (track.newResult != ConnectivityResult.none) {
 | |
|           if (!initializedSdk) {
 | |
|             initializedSdk = true;
 | |
|             await initSdk(onInitialized: () {
 | |
|               adImpressionController.init();
 | |
|               checkAndPreload();
 | |
|               Log.i("ADS Initialized", tag: "Ads", syncFirebase: true);
 | |
|             });
 | |
|           }
 | |
|         }
 | |
|         if (track.newResult != track.oldResult) {
 | |
|           Log.i("connectivity result changed! retry ads!", tag: "Ads");
 | |
|           setKeyword("connection", track.newResult.toString());
 | |
|           if (LifecycleManager.instance.isAppForeground()) {
 | |
|             if (track.newResult == ConnectivityResult.none &&
 | |
|                 track.oldResult != ConnectivityResult.none) {
 | |
|               Log.i("connectivity changed! retry ads!", tag: "Ads");
 | |
|               retry();
 | |
|             }
 | |
|           }
 | |
|         }
 | |
|       });
 | |
|     }
 | |
| 
 | |
|     subscriptions.add(RemoteConfigManager.instance.observeConfig().listen((_) {
 | |
|       refreshAdsConfig();
 | |
|     }, onError: (error, stacktrace) {
 | |
|       Log.i("init config error!",
 | |
|           tag: "Ads", error: error, stackTrace: stacktrace);
 | |
|     }));
 | |
|     listenIap();
 | |
|   }
 | |
| 
 | |
|   // void initLifecycleConnectivity() {
 | |
|   //   StreamSubscription? streamSubscription;
 | |
|   //   LifecycleManager.instance.observableAppLifecycle.listen((foreground) {
 | |
|   //     if (foreground) {
 | |
|   //       streamSubscription = Connectivity()
 | |
|   //           .onConnectivityChanged
 | |
|   //           .listen((ConnectivityResult result) {
 | |
|   //         Log.i("Connectivity: $result", tag: "Connectivity");
 | |
|   //         if (connectivityStatus == ConnectivityResult.none &&
 | |
|   //             result != ConnectivityResult.none) {
 | |
|   //           Log.i("connectivity changed! retry ads!", tag: "Ads");
 | |
|   //           retry();
 | |
|   //         }
 | |
|   //         final changed = connectivityStatusSubject.addIfChanged(result);
 | |
|   //         if (changed) {
 | |
|   //           setKeyword("connection", result.toString());
 | |
|   //         }
 | |
|   //       });
 | |
|   //     } else {
 | |
|   //       streamSubscription?.cancel();
 | |
|   //       streamSubscription = null;
 | |
|   //     }
 | |
|   //   });
 | |
|   // }
 | |
| 
 | |
|   void initAdsProfile() {
 | |
|     final _hasAmazonBannerAds = hasAmazonBannerAds;
 | |
|     final _hasAmazonInterstitialAds = hasAmazonInterstitialAds;
 | |
|     final _hasAmazonAds = _hasAmazonBannerAds || _hasAmazonInterstitialAds;
 | |
|     final defaultAdsProfile = GuruApp.instance.adsProfile;
 | |
|     final strategyInterstitialIds = adsConfig.strategyAdsConfig.interstitialIds;
 | |
|     final newAdsProfile = adsProfile.copyWith(
 | |
|         amazonAppId: _hasAmazonAds ? defaultAdsProfile.amazonAppId : null,
 | |
|         amazonBannerSlotId:
 | |
|             _hasAmazonBannerAds ? defaultAdsProfile.amazonBannerSlotId : null,
 | |
|         amazonInterstitialSlotId: _hasAmazonInterstitialAds
 | |
|             ? defaultAdsProfile.amazonInterstitialSlotId
 | |
|             : null,
 | |
|         strategyInterstitialIds: strategyInterstitialIds);
 | |
|     _adsProfileSubject.addEx(newAdsProfile);
 | |
|   }
 | |
| 
 | |
|   Future initEnv() async {
 | |
|     final adsPropertyBundle =
 | |
|         await AppProperty.getInstance().loadValuesByTag(PropertyTags.ads);
 | |
|     final isNoAds = adsPropertyBundle.getBool(PropertyKeys.isNoAds) ?? false;
 | |
| 
 | |
|     consentTestDeviceId =
 | |
|         adsPropertyBundle.getString(PropertyKeys.admobConsentTestDeviceId);
 | |
|     consentDebugGeography =
 | |
|         adsPropertyBundle.getInt(PropertyKeys.admobConsentDebugGeography);
 | |
|     noBannerAndInterstitialAdsSubject.addIfChanged(isNoAds);
 | |
|     GuruAnalytics.instance
 | |
|         .setUserProperty("user_type", isNoAds ? "noads" : "default");
 | |
|     setProperty("user_type", isNoAds ? "noads" : "default");
 | |
| 
 | |
|     final result = await Connectivity().checkConnectivity().catchError((error) {
 | |
|       Log.w("checkConnectivity error! $error");
 | |
|     });
 | |
|     // connectivityStatusSubject.addEx(result);
 | |
|     setProperty("connectivityStatus", result.toString());
 | |
| 
 | |
|     refreshAdsConfig();
 | |
|     initAdsProfile();
 | |
|   }
 | |
| 
 | |
|   Future initSdk(
 | |
|       {SaasUser? saasUser,
 | |
|       required VoidCallback onInitialized,
 | |
|       Duration retryPeriod = const Duration(seconds: 15)}) async {
 | |
|     final _adsProfile = adsProfile;
 | |
|     bool initializeResult = false;
 | |
|     if (GuruApp.instance.appSpec.deployment.adsCompliantInitialization &&
 | |
|         adsConfig.commonAdsConfig.compliantInitialization &&
 | |
|         Platform.isAndroid) {
 | |
|       initializeResult = await GuruApplovinFlutter.instance
 | |
|               .gatherConsentAndInitialize(
 | |
|                   userId: saasUser?.uid,
 | |
|                   amazonAppId: _adsProfile.amazonAppId?.id,
 | |
|                   pubmaticStoreUrl: adsProfile.pubmaticAppStoreUrl,
 | |
|                   testDeviceId: consentTestDeviceId,
 | |
|                   debugGeography: consentDebugGeography)
 | |
|               .catchError((err) => false) ??
 | |
|           false;
 | |
|     } else {
 | |
|       initializeResult = await GuruApplovinFlutter.instance
 | |
|               .initialize(
 | |
|                   userId: saasUser?.uid,
 | |
|                   amazonAppId: _adsProfile.amazonAppId?.id,
 | |
|                   pubmaticStoreUrl: adsProfile.pubmaticAppStoreUrl)
 | |
|               .catchError((err) => false) ??
 | |
|           false;
 | |
|     }
 | |
| 
 | |
|     _initializedSubject.addEx(initializeResult);
 | |
|     Log.d("MAX sdk initialize result: $initializeResult");
 | |
|     if (initializeResult) {
 | |
|       try {
 | |
|         await initKeywords();
 | |
|       } catch (error, stacktrace) {
 | |
|         Log.e("initKeywords error! $error $stacktrace", tag: "Ads");
 | |
|       }
 | |
|       onInitialized.call();
 | |
|     } else {
 | |
|       Future.delayed(retryPeriod, () {
 | |
|         initSdk(onInitialized: onInitialized, retryPeriod: retryPeriod);
 | |
|       });
 | |
|       Log.w("Ads Initialize error! retry", tag: "Ads", syncFirebase: true);
 | |
|     }
 | |
|     return initializeResult;
 | |
|   }
 | |
| 
 | |
|   void checkAndPreload(
 | |
|       {AdsValidator? rewardedValidator,
 | |
|       AdsValidator? interstitialValidator}) async {
 | |
|     final canPreloadReward =
 | |
|         await adsConfig.rewardedConfig.canPreload(validator: rewardedValidator);
 | |
|     if (canPreloadReward) {
 | |
|       Log.d("preload reward canPreload!");
 | |
|       final reward = await getRewardsAds();
 | |
|       if (reward.loadCount <= 0) {
 | |
|         reward.preload();
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     final canPreloadInterstitial = await adsConfig.interstitialConfig
 | |
|         .canPreload(validator: interstitialValidator);
 | |
|     if (!isPurchasedNoAd && canPreloadInterstitial) {
 | |
|       Log.d("preload interstitial canPreload!");
 | |
|       final interstitial = await getInterstitialAds();
 | |
|       if (interstitial is AdsAudit && interstitial.loadCount <= 0) {
 | |
|         interstitial.preload();
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   void retry() async {
 | |
|     final canPreloadReward = await adsConfig.rewardedConfig.canPreload();
 | |
|     if (canPreloadReward) {
 | |
|       final reward = await getRewardsAds();
 | |
|       reward.retry();
 | |
|     }
 | |
| 
 | |
|     final canPreload = await adsConfig.interstitialConfig.canPreload();
 | |
|     if (canPreload) {
 | |
|       Log.d("preload interstitial canPreload!");
 | |
|       final interstitial = await getInterstitialAds();
 | |
|       interstitial.retry();
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   static int _nearestLt(int low, int high, int lt) {
 | |
|     if (low > high) {
 | |
|       return -low;
 | |
|     }
 | |
|     while (low <= high) {
 | |
|       final int mid = (low + high) >> 1;
 | |
|       if (lt == ltSamples[mid]) {
 | |
|         return mid;
 | |
|       } else if (lt < ltSamples[mid]) {
 | |
|         return _nearestLt(low, mid - 1, lt);
 | |
|       } else {
 | |
|         return _nearestLt(mid + 1, high, lt);
 | |
|       }
 | |
|     }
 | |
|     return -low;
 | |
|   }
 | |
| 
 | |
|   Future<int> getKeywordLt() async {
 | |
|     final latestLtDate = await AppProperty.getInstance().getLatestLtDate();
 | |
|     final dateNum = DateTimeUtils.yyyyMMddUtcNum;
 | |
| 
 | |
|     int lt = await AppProperty.getInstance().getLtDays();
 | |
|     if (dateNum != latestLtDate) {
 | |
|       if (dateNum > latestLtDate) {
 | |
|         lt = lt + 1;
 | |
|         await AppProperty.getInstance().setLtDays(lt);
 | |
|       }
 | |
|       await AppProperty.getInstance().setLatestLtDate(dateNum);
 | |
|     }
 | |
|     final idx = _nearestLt(0, ltSamples.lastIndex, lt)
 | |
|         .abs()
 | |
|         .clamp(0, ltSamples.lastIndex);
 | |
|     Log.d(
 | |
|         "getKeywordLt: installTime:$latestLtDate now:$dateNum lt:$lt keywordLt:${ltSamples[idx]}");
 | |
| 
 | |
|     return ltSamples[idx];
 | |
|   }
 | |
| 
 | |
|   Future<String> getOSVersion() async {
 | |
|     try {
 | |
|       final deviceInfo = DeviceInfoPlugin();
 | |
|       if (Platform.isAndroid) {
 | |
|         final info = await deviceInfo.androidInfo;
 | |
|         return info.version.release;
 | |
|       } else if (Platform.isIOS) {
 | |
|         final info = await deviceInfo.iosInfo;
 | |
|         return info.systemVersion;
 | |
|       }
 | |
|     } catch (error, stacktrace) {
 | |
|       Log.w("getOSVersion error! $error");
 | |
|     }
 | |
|     return "unknown";
 | |
|   }
 | |
| 
 | |
|   Future<String> getConnection() async {
 | |
|     try {
 | |
|       final connectivity = await Connectivity().checkConnectivity();
 | |
|       return connectivity.toString();
 | |
|     } catch (error, stacktrace) {
 | |
|       Log.w("getConnection error! $error");
 | |
|     }
 | |
|     return "unknown";
 | |
|   }
 | |
| 
 | |
|   Future initKeywords() async {
 | |
|     final paidUser = await AppProperty.getInstance().isPaidUser();
 | |
|     final version = Settings.get().version.get();
 | |
|     final lt = await getKeywordLt();
 | |
|     final osVersion = await getOSVersion();
 | |
|     final connection = await getConnection();
 | |
|     final keywords = <String, String>{
 | |
|       "app_version": version,
 | |
|       "paid": paidUser ? "true" : "false",
 | |
|       "lt": lt.toString(),
 | |
|       "os_version": osVersion,
 | |
|       "connection": connection
 | |
|     };
 | |
| 
 | |
|     keywordsSubject.stream.listen((keywords) {
 | |
|       if (keywords.isNotEmpty) {
 | |
|         Log.i("invoke setKeywords: $keywords", tag: "Ads");
 | |
|         GuruApplovinFlutter.instance.setKeywords(keywords);
 | |
|       }
 | |
|     });
 | |
|     keywordsSubject.addEx(keywords);
 | |
|   }
 | |
| 
 | |
|   Future restoreKeywords(Map<String, String> keywords) async {
 | |
|     if (GuruSettings.instance.debugMode.get()) {
 | |
|       final newKeywords = Map.of(keywords);
 | |
|       keywordsSubject.addEx(newKeywords);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   void setKeyword(String key, String value, {bool debugForce = false}) {
 | |
|     if (!GuruSettings.instance.debugMode.get() || !debugForce) {
 | |
|       if (_reservedKeywords.contains(key)) {
 | |
|         Log.w("setKeyword error! the key($key) is reserved and cannot be used!",
 | |
|             tag: "Ads");
 | |
|         return;
 | |
|       }
 | |
| 
 | |
|       if (key.isEmpty ||
 | |
|           key.length > 36 ||
 | |
|           key.indexOf(_alpha) != 0 ||
 | |
|           key.contains(_nonAlphaNumeric)) {
 | |
|         Log.w(
 | |
|             "setKeyword error! the key($key) must contain 1 to 36 alphanumeric characters.",
 | |
|             tag: "Ads");
 | |
|         return;
 | |
|       }
 | |
|     }
 | |
|     final newKeywords = Map.of(keywordsSubject.value);
 | |
|     newKeywords[key] = value;
 | |
|     keywordsSubject.addEx(newKeywords);
 | |
|   }
 | |
| 
 | |
|   void removeKeyword(String key, {bool debugForce = false}) {
 | |
|     if (!GuruSettings.instance.debugMode.get() || !debugForce) {
 | |
|       if (_reservedKeywords.contains(key)) {
 | |
|         Log.w(
 | |
|             "removeKeyword error! the key($key) is reserved and cannot be used!",
 | |
|             tag: "Ads");
 | |
|         return;
 | |
|       }
 | |
| 
 | |
|       if (key.isEmpty ||
 | |
|           key.length > 36 ||
 | |
|           key.indexOf(_alpha) != 0 ||
 | |
|           key.contains(_nonAlphaNumeric)) {
 | |
|         Log.w(
 | |
|             "removeKeyword error! the key($key) must contain 1 to 36 alphanumeric characters.",
 | |
|             tag: "Ads");
 | |
|         return;
 | |
|       }
 | |
|     }
 | |
|     final newKeywords = Map.of(keywordsSubject.value);
 | |
|     newKeywords.remove(key);
 | |
|     keywordsSubject.addEx(newKeywords);
 | |
|   }
 | |
| 
 | |
|   Future<int> checkConsentDialogStatus() async {
 | |
|     return await GuruApplovinFlutter.instance.checkConsentDialogStatus();
 | |
|   }
 | |
| 
 | |
|   Future<bool> afterAcceptPrivacy(bool consentResult) async {
 | |
|     return await GuruApplovinFlutter.instance.afterAcceptPrivacy(consentResult);
 | |
|   }
 | |
| 
 | |
|   bool testParseAdsDefaultConfig() {
 | |
|     final iadsConfigString =
 | |
|         RemoteConfigReservedConstants.getDefaultConfigString(
 | |
|                 RemoteConfigReservedConstants.iadsConfig) ??
 | |
|             "";
 | |
|     final radsConfigString =
 | |
|         RemoteConfigReservedConstants.getDefaultConfigString(
 | |
|                 RemoteConfigReservedConstants.radsConfig) ??
 | |
|             "";
 | |
|     final badsConfigString =
 | |
|         RemoteConfigReservedConstants.getDefaultConfigString(
 | |
|                 RemoteConfigReservedConstants.badsConfig) ??
 | |
|             "";
 | |
|     final iosAttConfigString =
 | |
|         RemoteConfigReservedConstants.getDefaultConfigString(
 | |
|                 RemoteConfigReservedConstants.iosAttConfig) ??
 | |
|             "";
 | |
|     try {
 | |
|       final adInterstitial =
 | |
|           AdInterstitialConfig.fromJson(json.decode(iadsConfigString));
 | |
|       final adBanner = AdBannerConfig.fromJson(json.decode(badsConfigString));
 | |
|       final iosAttConfig =
 | |
|           IOSAttConfig.fromJson(json.decode(iosAttConfigString));
 | |
|       Log.d("==== ADS AdsConfig ====");
 | |
|       Log.d("  ---> [INTERSTITIAL]: $iadsConfigString");
 | |
|       Log.d("  ---> [BANNER]: $badsConfigString");
 | |
|       Log.d("  ---> [IOSATT]: $iosAttConfigString");
 | |
|       Log.d("=======================");
 | |
| 
 | |
|       _adsConfigSubject.addEx(AdsConfig.build(
 | |
|           interstitialConfig: adInterstitial,
 | |
|           bannerConfig: adBanner,
 | |
|           iosAttConfig: iosAttConfig));
 | |
|       return true;
 | |
|     } catch (error, stacktrace) {
 | |
|       Log.e("refreshAdsConfig error $error $stacktrace");
 | |
|       rethrow;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   bool refreshAdsConfig() {
 | |
|     try {
 | |
|       final commonAdsConfig = RemoteConfigManager.instance.getCommonAdsConfig();
 | |
|       final adInterstitial = RemoteConfigManager.instance.getIadsConfig();
 | |
|       final adReward = RemoteConfigManager.instance.getRadsConfig();
 | |
|       final adBanner = RemoteConfigManager.instance.getBadsConfig();
 | |
|       final strategyAdsConfig =
 | |
|           RemoteConfigManager.instance.getStrategyAdsConfig();
 | |
|       final iosAttConfig = RemoteConfigManager.instance.getIOSAttConfig();
 | |
|       Log.d("==== ADS AdsConfig ====", tag: PropertyTags.ads);
 | |
|       Log.d("  ---> [COMMON]: ${commonAdsConfig.toJson()}",
 | |
|           tag: PropertyTags.ads);
 | |
|       Log.d("  ---> [INTERSTITIAL]: ${adInterstitial.toJson()}",
 | |
|           tag: PropertyTags.ads);
 | |
|       Log.d("  ---> [REWARD]: ${adReward.toJson()}", tag: PropertyTags.ads);
 | |
|       Log.d("  ---> [BANNER]: ${adBanner.toJson()}", tag: PropertyTags.ads);
 | |
|       Log.d("  ---> [STRATEGY]: ${strategyAdsConfig.toJson()}",
 | |
|           tag: PropertyTags.ads);
 | |
|       Log.d("  ---> [IOSATT]: ${iosAttConfig.toJson()}", tag: PropertyTags.ads);
 | |
|       Log.d("=======================", tag: PropertyTags.ads);
 | |
|       _adsConfigSubject.addEx(AdsConfig.build(
 | |
|           commonAdsConfig: commonAdsConfig,
 | |
|           interstitialConfig: adInterstitial,
 | |
|           rewardedConfig: adReward,
 | |
|           bannerConfig: adBanner,
 | |
|           strategyAdsConfig: strategyAdsConfig,
 | |
|           iosAttConfig: iosAttConfig));
 | |
|       return true;
 | |
|     } catch (error, stacktrace) {
 | |
|       Log.e("refreshAdsConfig error $error $stacktrace");
 | |
|       rethrow;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   @override
 | |
|   Future<Ads> getInterstitialAds() async {
 | |
|     final _adsProfile = adsProfile;
 | |
|     final strategyInterstitialIds = adsProfile.strategyInterstitialIds ?? [];
 | |
|     Ads? ad;
 | |
|     if (strategyInterstitialIds.isNotEmpty) {
 | |
|       if (strategyInterstitialIds.length > 1) {
 | |
|         ad = interstitialAds[strategyInterstitialIds.first.adUnitId] ??=
 | |
|             MaxStrategyInterstitialAds.create(strategyInterstitialIds)..init();
 | |
|       } else {
 | |
|         ad = interstitialAds[strategyInterstitialIds.first.adUnitId] ??=
 | |
|             ApplovinInterstitialAds.create(
 | |
|                 strategyInterstitialIds.first.adUnitId,
 | |
|                 strategyInterstitialIds.first.amazonAdSlotId)
 | |
|               ..init();
 | |
|       }
 | |
|     } else {
 | |
|       ad = interstitialAds[_adsProfile.interstitialId] ??=
 | |
|           ApplovinInterstitialAds.create(
 | |
|               _adsProfile.interstitialId, _adsProfile.amazonInterstitialSlotId)
 | |
|             ..init();
 | |
|     }
 | |
|     return ad;
 | |
|   }
 | |
| 
 | |
|   @override
 | |
|   Future<ApplovinRewardedAds> getRewardsAds() async {
 | |
|     final _adsProfile = adsProfile;
 | |
|     ApplovinRewardedAds? ad = rewardsAds[_adsProfile.rewardsId];
 | |
|     if (ad == null) {
 | |
|       ad = ApplovinRewardedAds.create(_adsProfile.rewardsId,
 | |
|           adAmazonSlotId: _adsProfile.amazonRewardedSlotId)
 | |
|         ..init();
 | |
|       rewardsAds[_adsProfile.rewardsId] = ad;
 | |
|     }
 | |
|     return ad;
 | |
|   }
 | |
| 
 | |
|   Future<int> requestGdpr({int? debugGeography, String? testDeviceId}) async {
 | |
|     Log.d(
 | |
|         "requestGdpr! debugGeography:$debugGeography testDeviceId:$testDeviceId",
 | |
|         tag: "Ads");
 | |
|     // adb logcat -s UserMessagingPlatform
 | |
|     // Use new ConsentDebugSettings.Builder().addTestDeviceHashedId("xxxx") to set this as a debug device.
 | |
|     final result = await GuruApplovinFlutter.instance.requestGdpr(
 | |
|         debugGeography: debugGeography, testDeviceId: testDeviceId);
 | |
|     final consentResult = await GuruAnalytics.instance.refreshConsents();
 | |
|     Log.d("requestGdpr result:$result consentResult:$consentResult");
 | |
|     return result;
 | |
|   }
 | |
| 
 | |
|   Future<bool> resetGdpr() {
 | |
|     return GuruApplovinFlutter.instance.resetGdpr();
 | |
|   }
 | |
| 
 | |
|   Future<bool> updateOrientation(int orientation) async {
 | |
|     final result =
 | |
|         await GuruApplovinFlutter.instance.updateOrientation(orientation);
 | |
|     return result == true;
 | |
|   }
 | |
| 
 | |
|   @override
 | |
|   Future<ApplovinBannerAds> createBannerAds(
 | |
|       {String? scene, AdsLifecycleObserver? observer}) async {
 | |
|     final _adsProfile = adsProfile;
 | |
|     return ApplovinBannerAds.create(
 | |
|         _adsProfile.bannerId, _adsProfile.amazonBannerSlotId,
 | |
|         scene: scene, observer: observer);
 | |
|   }
 | |
| 
 | |
|   AdCause canShowInterstitial(String scene) {
 | |
|     if (isPurchasedNoAd) {
 | |
|       return AdCause.noAds;
 | |
|     }
 | |
|     final hiddenAt = AdsManager.instance.latestFullscreenAdsHiddenTimestamps;
 | |
|     final now = DateTimeUtils.currentTimeInMillis();
 | |
|     final impGapInMillis = AdsManager.instance.adsConfig.interstitialConfig
 | |
|             .getSceneImpGapInSeconds(scene) *
 | |
|         1000;
 | |
|     Log.d(
 | |
|         "canShowInterstitial($scene): now:$now latestFullscreenAdsHiddenTimestamps:$latestFullscreenAdsHiddenTimestamps hiddenAt:$hiddenAt impGapInMillis:$impGapInMillis",
 | |
|         tag: "Ads");
 | |
|     if ((now - hiddenAt) < impGapInMillis) {
 | |
|       Log.d("show ads too frequency", syncFirebase: true);
 | |
|       return AdCause.tooFrequent;
 | |
|     }
 | |
|     return AdCause.success;
 | |
|   }
 | |
| 
 | |
|   @override
 | |
|   Future<AdCause> validateInterstitial(String? scene,
 | |
|       {AdsValidator? validator}) {
 | |
|     final interstitialConfig = adsConfig.interstitialConfig;
 | |
|     return interstitialConfig.check(scene ?? "", validator: validator);
 | |
|   }
 | |
| 
 | |
|   @override
 | |
|   Future<AdCause> validateRewards(String? scene, {AdsValidator? validator}) {
 | |
|     final rewardedConfig = adsConfig.rewardedConfig;
 | |
|     return rewardedConfig.check(scene ?? "", validator: validator);
 | |
|   }
 | |
| 
 | |
|   @override
 | |
|   Future<AdCause> validateBanner(String? scene, {AdsValidator? validator}) {
 | |
|     final rewardedConfig = adsConfig.bannerConfig;
 | |
|     return rewardedConfig.check(scene ?? "", validator: validator);
 | |
|   }
 | |
| 
 | |
|   @override
 | |
|   dynamic getConfig(String type) {
 | |
|     switch (type) {
 | |
|       case "bannerAutoDisposeInterval":
 | |
|         return adsConfig.bannerConfig.autoDisposeIntervalInMinutes;
 | |
|       case "allowInterstitialAsAlternativeReward":
 | |
|         return GuruApp
 | |
|             .instance.appSpec.deployment.allowInterstitialAsAlternativeReward;
 | |
|       case "showInternalAdsWhenBannerUnavailable":
 | |
|         return GuruApp
 | |
|             .instance.appSpec.deployment.showInternalAdsWhenBannerUnavailable;
 | |
|     }
 | |
|   }
 | |
| }
 |