mnemo_cards/games/ads_example/lib/vast_webview_player.dart
2025-11-11 02:55:41 +03:00

417 lines
12 KiB
Dart
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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();
}
}