diff --git a/lib/services/consent_service.dart b/lib/services/consent_service.dart new file mode 100644 index 0000000..e3e3e24 --- /dev/null +++ b/lib/services/consent_service.dart @@ -0,0 +1,86 @@ +// lib/services/consent_service.dart +import 'dart:async'; +import 'dart:io' show Platform; + +import 'package:app_tracking_transparency/app_tracking_transparency.dart'; +import 'package:flutter/foundation.dart'; +import 'package:google_mobile_ads/google_mobile_ads.dart'; + +import 'ad_service.dart'; + +/// Runs the App-Review-mandated consent sequence exactly once per launch and +/// in the required order: UMP consent form -> iOS ATT prompt -> MobileAds +/// initialize. Each step is guarded; a failure in one never skips SDK init, +/// because un-initialized ads would silently disable the whole monetization +/// layer. Must be invoked AFTER the first frame (ATT needs the foreground). +class ConsentService { + ConsentService(this._adService); + + final AdService _adService; + bool _ran = false; + + Future ensureConsentAndInitialize() async { + if (_ran) return; + _ran = true; + await _requestUmp(); + await _requestAtt(); + await _initializeAds(); + } + + Future _requestUmp() async { + try { + final params = ConsentRequestParameters(); + final completer = Completer(); + ConsentInformation.instance.requestConsentInfoUpdate( + params, + () async { + try { + if (await ConsentInformation.instance.isConsentFormAvailable()) { + await _loadAndShowFormIfRequired(); + } + } finally { + completer.complete(); + } + }, + (_) => completer.complete(), + ); + await completer.future; + } catch (_) {/* proceed without UMP */} + } + + Future _loadAndShowFormIfRequired() async { + final completer = Completer(); + ConsentForm.loadConsentForm( + (form) async { + final status = await ConsentInformation.instance.getConsentStatus(); + if (status == ConsentStatus.required) { + form.show((_) => completer.complete()); + } else { + completer.complete(); + } + }, + (_) => completer.complete(), + ); + await completer.future; + } + + Future _requestAtt() async { + if (!Platform.isIOS) return; + try { + final status = + await AppTrackingTransparency.trackingAuthorizationStatus; + if (status == TrackingStatus.notDetermined) { + await AppTrackingTransparency.requestTrackingAuthorization(); + } + } catch (_) {/* ATT optional */} + } + + Future _initializeAds() async { + try { + await MobileAds.instance.initialize(); + _adService.onSdkReady(); + } catch (e) { + debugPrint('MobileAds init failed: $e'); + } + } +}