mnemo_cards/games/ads_example/lib/vast_webview_player.dart

418 lines
12 KiB
Dart
Raw Normal View History

2025-11-10 23:55:41 +00:00
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'package:url_launcher/url_launcher.dart';
class VastWebViewPlayer extends StatefulWidget {
final String? vastUrl;
final String? customXmlContent;
final VoidCallback? onAdComplete;
final VoidCallback? onAdError;
final VoidCallback? onAdClick;
final VoidCallback? onAdStarted;
final VoidCallback? onAdStopped;
final VoidCallback? onAdLoaded;
final VoidCallback? onAdSkipped;
final VoidCallback? onAdSkippableStateChange;
final VoidCallback? onAdSizeChange;
final VoidCallback? onAdLinearChange;
final VoidCallback? onAdDurationChange;
final VoidCallback? onAdExpandedChange;
final VoidCallback? onAdRemainingTimeChange;
final VoidCallback? onAdVolumeChange;
final VoidCallback? onAdImpression;
final VoidCallback? onAdVideoStart;
final VoidCallback? onAdVideoFirstQuartile;
final VoidCallback? onAdVideoMidpoint;
final VoidCallback? onAdVideoThirdQuartile;
final VoidCallback? onAdVideoComplete;
final VoidCallback? onAdClickThru;
final VoidCallback? onAdInteraction;
final VoidCallback? onAdUserAcceptInvitation;
final VoidCallback? onAdUserMinimize;
final VoidCallback? onAdUserClose;
final VoidCallback? onAdPaused;
final VoidCallback? onAdPlaying;
final VoidCallback? onAdLog;
const VastWebViewPlayer({
super.key,
this.vastUrl,
this.customXmlContent,
this.onAdComplete,
this.onAdError,
this.onAdClick,
this.onAdStarted,
this.onAdStopped,
this.onAdLoaded,
this.onAdSkipped,
this.onAdSkippableStateChange,
this.onAdSizeChange,
this.onAdLinearChange,
this.onAdDurationChange,
this.onAdExpandedChange,
this.onAdRemainingTimeChange,
this.onAdVolumeChange,
this.onAdImpression,
this.onAdVideoStart,
this.onAdVideoFirstQuartile,
this.onAdVideoMidpoint,
this.onAdVideoThirdQuartile,
this.onAdVideoComplete,
this.onAdClickThru,
this.onAdInteraction,
this.onAdUserAcceptInvitation,
this.onAdUserMinimize,
this.onAdUserClose,
this.onAdPaused,
this.onAdPlaying,
this.onAdLog,
});
@override
State<VastWebViewPlayer> createState() => _VastWebViewPlayerState();
}
class _VastWebViewPlayerState extends State<VastWebViewPlayer> {
InAppWebViewController? _webViewController;
@override
Widget build(BuildContext context) {
return InAppWebView(
initialFile: "assets/vast_player_new.html",
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
useShouldOverrideUrlLoading: true,
mediaPlaybackRequiresUserGesture: false,
javaScriptEnabled: true,
),
android: AndroidInAppWebViewOptions(
useHybridComposition: true,
mixedContentMode: AndroidMixedContentMode.MIXED_CONTENT_ALWAYS_ALLOW,
),
ios: IOSInAppWebViewOptions(allowsInlineMediaPlayback: true),
),
onWebViewCreated: (InAppWebViewController controller) {
_webViewController = controller;
_setupJavaScriptHandlers();
},
onLoadStart: (controller, url) {
setState(() {});
},
onLoadStop: (controller, url) {
setState(() {});
// Загружаем рекламу с указанным URL или пользовательским XML
if (widget.customXmlContent != null && widget.customXmlContent!.isNotEmpty) {
_loadCustomXml(widget.customXmlContent!);
} else if (widget.vastUrl != null && widget.vastUrl!.isNotEmpty) {
_loadVastAd(widget.vastUrl!);
}
},
onLoadError: (controller, url, code, message) {
setState(() {});
widget.onAdError?.call();
},
onConsoleMessage: (controller, consoleMessage) {
print('WebView Console: ${consoleMessage.message}');
},
shouldOverrideUrlLoading: (controller, navigationAction) async {
// Разрешаем переходы по ссылкам рекламы
if (navigationAction.request.url != null) {
final url = navigationAction.request.url.toString();
print('Attempting to navigate to: $url');
// Вызываем callback для обработки клика
widget.onAdClick?.call();
// Открываем URL в браузере
try {
final uri = Uri.parse(url);
if (await canLaunchUrl(uri)) {
await launchUrl(uri, mode: LaunchMode.externalApplication);
print('Opened URL in browser: $url');
} else {
print('Cannot open URL: $url');
}
} catch (e) {
print('Error opening URL: $e');
}
// Блокируем переход в WebView, так как открываем в браузере
return NavigationActionPolicy.CANCEL;
}
return NavigationActionPolicy.ALLOW;
},
);
}
Future<void> _loadVastAd(String vastUrl) async {
try {
print('Loading VAST ad from URL: $vastUrl');
await _webViewController?.evaluateJavascript(
source: 'loadVastAd("$vastUrl");',
);
} catch (e) {
print('Error loading VAST ad from URL: $e');
setState(() {});
widget.onAdError?.call();
}
}
Future<void> _loadCustomXml(String xmlContent) async {
try {
print('Loading custom XML ad, content length: ${xmlContent.length}');
// Экранируем специальные символы в XML для JavaScript
final escapedXml = xmlContent
.replaceAll('\\', '\\\\')
.replaceAll('"', '\\"')
.replaceAll('\n', '\\n')
.replaceAll('\r', '\\r');
await _webViewController?.evaluateJavascript(
source: 'loadCustomXml("$escapedXml");',
);
} catch (e) {
print('Error loading custom XML: $e');
setState(() {});
widget.onAdError?.call();
}
}
void _setupJavaScriptHandlers() {
if (_webViewController == null) return;
// Основные события
_webViewController!.addJavaScriptHandler(
handlerName: 'onAdComplete',
callback: (args) {
print('Ad completed');
widget.onAdComplete?.call();
},
);
_webViewController!.addJavaScriptHandler(
handlerName: 'onAdError',
callback: (args) {
print('Ad error received: $args');
widget.onAdError?.call();
},
);
_webViewController!.addJavaScriptHandler(
handlerName: 'onAdClick',
callback: (args) {
print('Ad clicked ${args}');
widget.onAdClick?.call();
},
);
_webViewController!.addJavaScriptHandler(
handlerName: 'onAdStarted',
callback: (args) {
print('Ad started');
widget.onAdStarted?.call();
},
);
_webViewController!.addJavaScriptHandler(
handlerName: 'onAdStopped',
callback: (args) {
print('Ad stopped');
widget.onAdStopped?.call();
},
);
// Дополнительные события из нового HTML
_webViewController!.addJavaScriptHandler(
handlerName: 'onAdLoaded',
callback: (args) {
print('Ad loaded');
widget.onAdLoaded?.call();
},
);
_webViewController!.addJavaScriptHandler(
handlerName: 'onAdSkipped',
callback: (args) {
print('Ad skipped');
widget.onAdSkipped?.call();
},
);
_webViewController!.addJavaScriptHandler(
handlerName: 'onAdSkippableStateChange',
callback: (args) {
print('Ad skippable state changed: $args');
widget.onAdSkippableStateChange?.call();
},
);
_webViewController!.addJavaScriptHandler(
handlerName: 'onAdSizeChange',
callback: (args) {
print('Ad size changed: $args');
widget.onAdSizeChange?.call();
},
);
_webViewController!.addJavaScriptHandler(
handlerName: 'onAdLinearChange',
callback: (args) {
print('Ad linear changed: $args');
widget.onAdLinearChange?.call();
},
);
_webViewController!.addJavaScriptHandler(
handlerName: 'onAdDurationChange',
callback: (args) {
print('Ad duration changed: $args');
widget.onAdDurationChange?.call();
},
);
_webViewController!.addJavaScriptHandler(
handlerName: 'onAdExpandedChange',
callback: (args) {
print('Ad expanded changed: $args');
widget.onAdExpandedChange?.call();
},
);
_webViewController!.addJavaScriptHandler(
handlerName: 'onAdRemainingTimeChange',
callback: (args) {
print('Ad remaining time changed: $args');
widget.onAdRemainingTimeChange?.call();
},
);
_webViewController!.addJavaScriptHandler(
handlerName: 'onAdVolumeChange',
callback: (args) {
print('Ad volume changed: $args');
widget.onAdVolumeChange?.call();
},
);
_webViewController!.addJavaScriptHandler(
handlerName: 'onAdImpression',
callback: (args) {
print('Ad impression');
widget.onAdImpression?.call();
},
);
_webViewController!.addJavaScriptHandler(
handlerName: 'onAdVideoStart',
callback: (args) {
print('Ad video start');
widget.onAdVideoStart?.call();
},
);
_webViewController!.addJavaScriptHandler(
handlerName: 'onAdVideoFirstQuartile',
callback: (args) {
print('Ad video first quartile');
widget.onAdVideoFirstQuartile?.call();
},
);
_webViewController!.addJavaScriptHandler(
handlerName: 'onAdVideoMidpoint',
callback: (args) {
print('Ad video midpoint');
widget.onAdVideoMidpoint?.call();
},
);
_webViewController!.addJavaScriptHandler(
handlerName: 'onAdVideoThirdQuartile',
callback: (args) {
print('Ad video third quartile');
widget.onAdVideoThirdQuartile?.call();
},
);
_webViewController!.addJavaScriptHandler(
handlerName: 'onAdVideoComplete',
callback: (args) {
print('Ad video complete');
widget.onAdVideoComplete?.call();
},
);
_webViewController!.addJavaScriptHandler(
handlerName: 'onAdClickThru',
callback: (args) {
print('Ad click through');
widget.onAdClickThru?.call();
},
);
_webViewController!.addJavaScriptHandler(
handlerName: 'onAdInteraction',
callback: (args) {
print('Ad interaction: $args');
widget.onAdInteraction?.call();
},
);
_webViewController!.addJavaScriptHandler(
handlerName: 'onAdUserAcceptInvitation',
callback: (args) {
print('Ad user accept invitation');
widget.onAdUserAcceptInvitation?.call();
},
);
_webViewController!.addJavaScriptHandler(
handlerName: 'onAdUserMinimize',
callback: (args) {
print('Ad user minimize');
widget.onAdUserMinimize?.call();
},
);
_webViewController!.addJavaScriptHandler(
handlerName: 'onAdUserClose',
callback: (args) {
print('Ad user close');
widget.onAdUserClose?.call();
},
);
_webViewController!.addJavaScriptHandler(
handlerName: 'onAdPaused',
callback: (args) {
print('Ad paused');
widget.onAdPaused?.call();
},
);
_webViewController!.addJavaScriptHandler(
handlerName: 'onAdPlaying',
callback: (args) {
print('Ad playing');
widget.onAdPlaying?.call();
},
);
_webViewController!.addJavaScriptHandler(
handlerName: 'onAdLog',
callback: (args) {
print('Ad log: $args');
widget.onAdLog?.call();
},
);
}
@override
void dispose() {
_webViewController?.dispose();
super.dispose();
}
}