| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329 |
- import 'dart:async';
- import 'package:flutter/material.dart';
- import 'package:fluttertoast/fluttertoast.dart';
- import 'package:logging/logging.dart';
- import 'package:provider/provider.dart';
- import 'package:puzzleweave/ads/applovin_ads_controller.dart';
- import 'package:puzzleweave/l10n/app_localizations.dart';
- import 'package:puzzleweave/models/data.dart';
- import 'package:puzzleweave/persistence/persistence.dart';
- import 'package:puzzleweave/skin/skin.dart';
- import '../remote_config/remote_config.dart';
- import 'ad_helper.dart';
- final Logger _log = Logger('AdsState');
- /// Add by guoziyi
- /// 自带广告的 StatefulWidget State
- /// 从该类继承的 State 自动带有广告功能
- abstract class AdsState<T extends StatefulWidget> extends State<T> {
- ///这个值用于数据统计,子类应该在initState初始化的时候正确设置skuId
- String skuId = "";
- VoidCallback? onEnterBackground; // app 进入后台回调, 外部如关心, 可设置此回调
- Function(Duration duration)? onEnterForeground; // app 回到前台回调, 外部如关心, 可设置此回调
- Function(AdState state)? onRewardAdState; // reward广告状态回调,外部如关心,可设置此回调
- Function(AdState state)? onInterstitialAdState; // reward广告状态回调,外部如关心,可设置此回调
- late Data data;
- late ApplovinAdsController _applovinAdsController;
- late ValueNotifier<AppLifecycleState> lifecycleNotifier;
- Completer<bool>? _intersCompleter;
- Completer<bool>? _rewardCompleter;
- bool earnedReward = false; //是否确定获得激励
- //插屏广告是否准备好
- late ValueNotifier<bool> intersReadyNotifier;
- bool get isIntersReady => intersReadyNotifier.value;
- //激励广告是否准备好
- late ValueNotifier<bool> rewardReadyNotifier;
- bool get isRewardReady => rewardReadyNotifier.value;
- //插屏广告当前状态
- AdState get intersState => _applovinAdsController.interstitialAdState.value;
- //激励广告当前状态
- AdState get rewardState => _applovinAdsController.rewardedAdState.value;
- // 新增:用于控制 Banner 实际显示的变量
- bool _isBannerVisible = false;
- bool get isBannerVisible => _isBannerVisible;
- @override
- void initState() {
- super.initState();
- data = context.read<Data>();
- // 1. 监听 ValueNotifier 的变化
- // 当 completedWorks.value 被重新赋值时(见 data.dart 的 workDone 方法),触发刷新
- data.completedWorks.addListener(_onLevelChanged);
- _applovinAdsController = context.read<ApplovinAdsController>();
- _applovinAdsController.interstitialAdState.removeListener(_onInterstitialAdState);
- _applovinAdsController.interstitialAdState.addListener(_onInterstitialAdState);
- _applovinAdsController.rewardedAdState.removeListener(_onRewardAdState);
- _applovinAdsController.rewardedAdState.addListener(_onRewardAdState);
- intersReadyNotifier = ValueNotifier(intersState == AdState.ready);
- rewardReadyNotifier = ValueNotifier(rewardState == AdState.ready);
- lifecycleNotifier = context.read<ValueNotifier<AppLifecycleState>>();
- lifecycleNotifier.removeListener(_handleAppLifecycle);
- lifecycleNotifier.addListener(_handleAppLifecycle);
- _applovinAdsController.loadInterstitialAd();
- _applovinAdsController.loadRewardedAd();
- // 初始检查一次 Banner
- _updateBannerVisibility();
- }
- // 关卡变化的回调
- void _onLevelChanged() {
- _log.info("Level changed detected via ValueNotifier, updating banner...");
- _updateBannerVisibility();
- }
- // 核心改进:主动更新 Banner 状态
- Future<void> _updateBannerVisibility() async {
- if (!mounted) return;
- // 1. 确保 SDK 已初始化
- bool ready = await _applovinAdsController.completer.future;
- // 2. 获取当前关卡数
- int doneLevels = data.currentLevel;
- // 3. 综合判断:SDK就绪 + 逻辑允许 + 应用在前台
- bool shouldShow = ready && shouldShowBannerAd(doneLevels) && lifecycleNotifier.value == AppLifecycleState.resumed;
- if (_isBannerVisible != shouldShow) {
- setState(() {
- _isBannerVisible = shouldShow;
- });
- _log.info("Banner visibility updated: $_isBannerVisible");
- }
- }
- @override
- void dispose() {
- data.completedWorks.removeListener(_onLevelChanged);
- intersReadyNotifier.dispose();
- rewardReadyNotifier.dispose();
- lifecycleNotifier.removeListener(_handleAppLifecycle);
- if (_intersCompleter != null && !_intersCompleter!.isCompleted) {
- _intersCompleter!.complete(false);
- }
- _intersCompleter = null;
- if (_rewardCompleter != null && !_rewardCompleter!.isCompleted) {
- _rewardCompleter!.complete(false);
- }
- _rewardCompleter = null;
- _applovinAdsController.interstitialAdState.removeListener(_onInterstitialAdState);
- _applovinAdsController.rewardedAdState.removeListener(_onRewardAdState);
- super.dispose();
- }
- /// 广告 sdk 是否初始化完成
- Future<bool> adSDKReady() async {
- return _applovinAdsController.completer.future;
- }
- /// 检查是否应该展示banner广告
- bool shouldShowBannerAd(int doneLevels) {
- if (doneLevels == 0) {
- _log.info("首关不展示banner广告");
- return false; // 首关是引导关卡,一定不要显示广告
- }
- bool levelShould = doneLevels >= RemoteConfig().intersFreeLevels;
- DateTime now = DateTime.now();
- int bannerFreeDuration = RemoteConfig().bannerFreeDuration;
- DateTime firstRunTime = Persistence().firstRunTime;
- bool timeShould = now.difference(firstRunTime).inMinutes > bannerFreeDuration;
- return levelShould || timeShould;
- }
- /// banner 广告
- Widget getBanner(String positionKey) {
- return _applovinAdsController.getBannerWidget(positionKey);
- }
- ///显示激励广告
- ///返回奖励获取结果。true:成功获取到奖励;false;未获得奖励
- Future<bool> showRewardAd(String src, String skuId) async {
- if (!(await _applovinAdsController.isRewardedAdReady())) {
- _applovinAdsController.loadRewardedAd();
- if (mounted) {
- Fluttertoast.showToast(
- msg: AppLocalizations.of(context)!.adNotReady,
- toastLength: Toast.LENGTH_SHORT,
- gravity: ToastGravity.CENTER,
- timeInSecForIosWeb: 1,
- backgroundColor: SkinHelper.slotBorderColor,
- textColor: Colors.white,
- fontSize: 16.0,
- );
- }
- return false;
- }
- if (_rewardCompleter != null && !_rewardCompleter!.isCompleted) {
- _rewardCompleter!.complete(earnedReward);
- }
- _rewardCompleter = Completer<bool>();
- this.skuId = skuId;
- earnedReward = false;
- _applovinAdsController.showRewardedlAd(
- adSrc: src,
- skuId: skuId,
- onUserEarnedReward: (ad, reward) {
- _log.info("................earn reward..........");
- earnedReward = true;
- },
- );
- return _rewardCompleter!.future;
- }
- bool shouldShowInterstitialAd(String src, int doneLevels) {
- bool shouldShowAd = false;
- if (src == 'level_enter') {
- shouldShowAd = _shouldShowEnterIntersAd(doneLevels);
- } else {
- shouldShowAd = _shouldShowExitIntersAd(doneLevels);
- }
- return shouldShowAd;
- }
- ///显示插屏广告
- ///返回广告播放结果。true:播放成功;false;播放失败
- Future<bool> showInterstitialAd(String src, String skuId, int doneLevels) async {
- bool shouldShowAd = shouldShowInterstitialAd(src, doneLevels);
- if (!shouldShowAd) return false;
- // 插屏广告没有预备好或者当前不符合播放插屏广告条件,直接返回false
- if (!(await _applovinAdsController.isInterstitialAdReady())) {
- _applovinAdsController.loadInterstitialAd();
- return false;
- }
- if (_intersCompleter != null && !_intersCompleter!.isCompleted) {
- _intersCompleter!.complete(false);
- }
- _intersCompleter = Completer<bool>();
- this.skuId = skuId;
- _applovinAdsController.showInterstitialAd(adSrc: src, skuId: skuId);
- return _intersCompleter!.future;
- }
- // 检查是否应该展示进入插屏广告
- bool _shouldShowEnterIntersAd(int doneLevels) {
- if (doneLevels == 0) return false; // 首关是引导关卡,一定不要显示广告
- bool levelShould = doneLevels >= RemoteConfig().intersFreeLevels;
- DateTime now = DateTime.now();
- int enterIntersDuration = RemoteConfig().enterIntersFreeDuration; //新手保护时长,单位:分钟
- DateTime firstRunTime = Persistence().firstRunTime;
- bool timeShould = now.difference(firstRunTime).inMinutes > enterIntersDuration; //判断当前时间是否已超过新手保护时长
- return levelShould || timeShould; // 时间限制和关卡限制两者只要满足其一都可以播放插屏
- }
- // 检查是否应该展示退出插屏广告
- bool _shouldShowExitIntersAd(int doneLevels) {
- bool levelShould = doneLevels >= RemoteConfig().intersFreeLevels;
- DateTime now = DateTime.now();
- int quitIntersDuration = RemoteConfig().quitIntersFreeDuration; //新手保护时长,单位:分钟
- DateTime firstRunTime = Persistence().firstRunTime;
- bool timeShould = now.difference(firstRunTime).inMinutes > quitIntersDuration;
- return levelShould || timeShould; // 时间限制和关卡限制两者只要满足其一都可以播放插屏
- }
- // 插屏广告回调处理
- _onInterstitialAdState() {
- if (intersState == AdState.dismissed) {
- _log.info("onInterstitialAdState AdState.dismissed");
- if (_intersCompleter != null && !_intersCompleter!.isCompleted) {
- _intersCompleter!.complete(true);
- }
- _intersCompleter = null;
- }
- intersReadyNotifier.value = (intersState == AdState.ready);
- onInterstitialAdState?.call(intersState);
- }
- // 激励广告回调处理
- _onRewardAdState() {
- if (_applovinAdsController.rewardedAdState.value == AdState.dismissed) {
- _log.info("onRewardAdState AdState.dismissed");
- if (_rewardCompleter != null && !_rewardCompleter!.isCompleted) {
- _rewardCompleter!.complete(earnedReward);
- }
- _rewardCompleter = null;
- }
- rewardReadyNotifier.value = (rewardState == AdState.ready);
- onRewardAdState?.call(rewardState);
- }
- ///-----------[Lifecycle]-------------
- ///需要处理可重写状态处理函数
- AppLifecycleState lifeState = AppLifecycleState.resumed;
- bool get isActive => lifeState == AppLifecycleState.resumed;
- void _handleAppLifecycle() {
- _log.info('AppLifecycleState changed: ${lifecycleNotifier.value}');
- lifeState = lifecycleNotifier.value;
- // 当回到前台时,重新检查 Banner;切到后台时,理论上 MaxAdView 会自动处理,
- // 但我们可以通过 setState 强制隐藏它来确保安全。
- _updateBannerVisibility();
- if (lifeState == AppLifecycleState.inactive) {
- //前台可见,但是无法交互
- onInactive();
- } else if (lifeState == AppLifecycleState.paused) {
- //前台不可见
- onPause();
- } else if (lifeState == AppLifecycleState.resumed) {
- //前台可见,可交互
- onResume();
- }
- }
- void onInactive() {}
- DateTime? _leaveTime;
- void onPause() {
- if (intersState == AdState.showing || rewardState == AdState.showing) {
- return;
- }
- _leaveTime = DateTime.now(); // 记录切换到后台的时间
- onEnterBackground?.call();
- _log.info("App enter background, leaveTime: $_leaveTime");
- }
- void onResume() {
- if (_leaveTime != null) {
- var duration = DateTime.now().difference(_leaveTime!);
- _log.info("App enter foreground, leaveTime: $_leaveTime, duration=$duration");
- onEnterForeground?.call(duration);
- }
- _leaveTime = null;
- }
- }
|