feat(ads): ConsentService enforcing UMP -> ATT -> SDK init order
This commit is contained in:
@@ -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<void> ensureConsentAndInitialize() async {
|
||||
if (_ran) return;
|
||||
_ran = true;
|
||||
await _requestUmp();
|
||||
await _requestAtt();
|
||||
await _initializeAds();
|
||||
}
|
||||
|
||||
Future<void> _requestUmp() async {
|
||||
try {
|
||||
final params = ConsentRequestParameters();
|
||||
final completer = Completer<void>();
|
||||
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<void> _loadAndShowFormIfRequired() async {
|
||||
final completer = Completer<void>();
|
||||
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<void> _requestAtt() async {
|
||||
if (!Platform.isIOS) return;
|
||||
try {
|
||||
final status =
|
||||
await AppTrackingTransparency.trackingAuthorizationStatus;
|
||||
if (status == TrackingStatus.notDetermined) {
|
||||
await AppTrackingTransparency.requestTrackingAuthorization();
|
||||
}
|
||||
} catch (_) {/* ATT optional */}
|
||||
}
|
||||
|
||||
Future<void> _initializeAds() async {
|
||||
try {
|
||||
await MobileAds.instance.initialize();
|
||||
_adService.onSdkReady();
|
||||
} catch (e) {
|
||||
debugPrint('MobileAds init failed: $e');
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user