Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -576,10 +576,6 @@ class InAppWebViewController {
///{@macro flutter_inappwebview_platform_interface.PlatformInAppWebViewController.getViewId}
dynamic getViewId() => platform.getViewId();

///{@macro flutter_inappwebview_platform_interface.PlatformInAppWebViewController.getNativeWebViewByInstanceId}
static Future<dynamic> getNativeWebViewByInstanceId(String instanceId) =>
PlatformInAppWebViewController.static().getNativeWebViewByInstanceId(instanceId);

///{@macro flutter_inappwebview_platform_interface.PlatformInAppWebViewController.getAllRegisteredInstanceIds}
static Future<List<String>> getAllRegisteredInstanceIds() =>
PlatformInAppWebViewController.static().getAllRegisteredInstanceIds();
Expand Down
1 change: 1 addition & 0 deletions flutter_inappwebview/lib/src/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ export 'tracing_controller.dart';
export 'process_global_config.dart';
export 'in_app_localhost_server.dart';
export 'webview_environment/main.dart';
export 'omid/omid_session_controller.dart';
39 changes: 39 additions & 0 deletions flutter_inappwebview/lib/src/omid/omid_session_controller.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import 'package:flutter/services.dart';

/// Controller for managing OMID sessions.
class OmidSessionController {
OmidSessionController._();

static const _channel = MethodChannel('kontext_omid');

/// Starts an OMID session.
static Future<void> startSession({
required String instanceId,
required String partnerName,
required String partnerVersion,
String? contentUrl,
String? customReferenceData,
}) async {
return _invoke('startOmidSession', {
'instanceId': instanceId,
'partnerName': partnerName,
'partnerVersion': partnerVersion,
'contentUrl': contentUrl,
'customReferenceData': customReferenceData,
});
}

/// Stops and cleans an OMID session.
static Future<void> stopSession(String instanceId) async {
return _invoke('stopOmidSession', {'instanceId': instanceId});
}

static Future<void> _invoke(String method, [Map<String, dynamic>? args]) async {
try {
final foo = await _channel.invokeMethod(method, args);
print('OMID method $method invoked with result: $foo');
} on MissingPluginException catch (e) {
throw PlatformException(code: 'missing_plugin', message: e.message);
}
}
}
25 changes: 19 additions & 6 deletions flutter_inappwebview_android/android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,22 @@ rootProject.allprojects {
repositories {
google()
mavenCentral()
flatDir {
def pluginProject = rootProject.findProject(':flutter_inappwebview_android')
dirs pluginProject != null ? pluginProject.file('libs') : file('libs')
}
}
}

apply plugin: 'com.android.library'

repositories {
flatDir {
dirs 'libs'
}
}


android {
// Conditional for compatibility with AGP <4.2.
if (project.android.hasProperty("namespace")) {
Expand Down Expand Up @@ -48,10 +59,12 @@ android {
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
dependencies {
implementation 'androidx.webkit:webkit:1.12.0'
implementation 'androidx.browser:browser:1.8.0'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
}
}

dependencies {
implementation 'androidx.webkit:webkit:1.12.0'
implementation 'androidx.browser:browser:1.8.0'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
implementation(name: 'omsdk-1.5.6', ext: 'aar')
}
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import com.pichillilorenzo.flutter_inappwebview_android.headless_in_app_webview.HeadlessInAppWebViewManager;
import com.pichillilorenzo.flutter_inappwebview_android.in_app_browser.InAppBrowserManager;
import com.pichillilorenzo.flutter_inappwebview_android.print_job.PrintJobManager;
import com.pichillilorenzo.flutter_inappwebview_android.omid.OmidSessionManager;
import com.pichillilorenzo.flutter_inappwebview_android.process_global_config.ProcessGlobalConfigManager;
import com.pichillilorenzo.flutter_inappwebview_android.proxy.ProxyManager;
import com.pichillilorenzo.flutter_inappwebview_android.service_worker.ServiceWorkerManager;
Expand Down Expand Up @@ -58,6 +59,8 @@ public class InAppWebViewFlutterPlugin implements FlutterPlugin, ActivityAware {
@Nullable
public PrintJobManager printJobManager;
@Nullable
public OmidSessionManager omidSessionManager;
@Nullable
public TracingControllerManager tracingControllerManager;
@Nullable
public ProcessGlobalConfigManager processGlobalConfigManager;
Expand Down Expand Up @@ -114,6 +117,7 @@ private void onAttachedToEngine(Context applicationContext, BinaryMessenger mess
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
printJobManager = new PrintJobManager(this);
}
omidSessionManager = new OmidSessionManager(this);
tracingControllerManager = new TracingControllerManager(this);
processGlobalConfigManager = new ProcessGlobalConfigManager(this);
}
Expand Down Expand Up @@ -172,6 +176,10 @@ public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
printJobManager.dispose();
printJobManager = null;
}
if (omidSessionManager != null) {
omidSessionManager.dispose();
omidSessionManager = null;
}
if (tracingControllerManager != null) {
tracingControllerManager.dispose();
tracingControllerManager = null;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
package com.pichillilorenzo.flutter_inappwebview_android.omid;

import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.text.TextUtils;

import androidx.annotation.NonNull;

import com.iab.omid.library.megabrainco.Omid;
import com.iab.omid.library.megabrainco.adsession.AdSession;
import com.iab.omid.library.megabrainco.adsession.AdSessionConfiguration;
import com.iab.omid.library.megabrainco.adsession.AdSessionContext;
import com.iab.omid.library.megabrainco.adsession.CreativeType;
import com.iab.omid.library.megabrainco.adsession.ImpressionType;
import com.iab.omid.library.megabrainco.adsession.Owner;
import com.iab.omid.library.megabrainco.adsession.Partner;
import com.pichillilorenzo.flutter_inappwebview_android.InAppWebViewFlutterPlugin;
import com.pichillilorenzo.flutter_inappwebview_android.types.ChannelDelegateImpl;
import com.pichillilorenzo.flutter_inappwebview_android.webview.WebViewInstanceRegistry;
import com.pichillilorenzo.flutter_inappwebview_android.webview.in_app_webview.InAppWebView;

import java.lang.ref.WeakReference;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;

public class OmidSessionManager extends ChannelDelegateImpl {
public static final String METHOD_CHANNEL_NAME = "kontext_omid";
private static final int WEBVIEW_HOLD_DURATION_MS = 1100;

@NonNull
private final InAppWebViewFlutterPlugin plugin;
@NonNull
private final Handler mainHandler = new Handler(Looper.getMainLooper());
@NonNull
private final Map<String, OmidSessionState> sessions = new ConcurrentHashMap<>();

public OmidSessionManager(@NonNull InAppWebViewFlutterPlugin plugin) {
super(new MethodChannel(plugin.messenger, METHOD_CHANNEL_NAME));
this.plugin = plugin;
}

@Override
public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
switch (call.method) {
case "startOmidSession":
startSession(call, result);
break;
case "stopOmidSession":
stopSession(call, result);
break;
default:
result.notImplemented();
break;
}
}

private void startSession(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
final String instanceId = call.argument("instanceId");
if (TextUtils.isEmpty(instanceId)) {
result.error("missing_instance_id", "Expected a non-empty instanceId.", null);
return;
}
if (sessions.containsKey(instanceId)) {
result.error("duplicate_session", "An OMID session already exists for instanceId " + instanceId + ".", null);
return;
}

final InAppWebView webView = WebViewInstanceRegistry.get(instanceId);
if (webView == null) {
result.error("webview_not_found", "No WebView found for instanceId " + instanceId + ".", null);
return;
}

final String partnerName = call.argument("partnerName");
final String partnerVersion = call.argument("partnerVersion");
if (TextUtils.isEmpty(partnerName) || TextUtils.isEmpty(partnerVersion)) {
result.error("invalid_partner", "partnerName and partnerVersion are required.", null);
return;
}

final String contentUrl = call.argument("contentUrl");
final String customReferenceData = call.argument("customReferenceData");

if (contentUrl != null && contentUrl.length() > 512) {
result.error("invalid_content_url", "contentUrl must be 512 characters or fewer.", null);
return;
}
if (customReferenceData != null && customReferenceData.length() > 256) {
result.error("invalid_custom_reference_data", "customReferenceData must be 256 characters or fewer.", null);
return;
}

mainHandler.post(() -> {
if (sessions.containsKey(instanceId)) {
result.error("duplicate_session", "An OMID session already exists for instanceId " + instanceId + ".", null);
return;
}
if (!ensureOmidActivated(result)) {
return;
}
try {
Partner partner = Partner.createPartner(partnerName, partnerVersion);
AdSessionContext context = AdSessionContext.createHtmlAdSessionContext(
partner,
webView,
contentUrl,
customReferenceData
);
AdSessionConfiguration configuration = AdSessionConfiguration.createAdSessionConfiguration(
CreativeType.HTML_DISPLAY,
ImpressionType.BEGIN_TO_RENDER,
Owner.JAVASCRIPT,
Owner.NONE,
false
);
AdSession adSession = AdSession.createAdSession(configuration, context);
adSession.registerAdView(webView);
adSession.start();

sessions.put(instanceId, new OmidSessionState(webView, adSession));
result.success(true);
} catch (Exception e) {
result.error("session_start_failed", "Failed to start OMID session: " + e.getMessage(), null);
return;
}
});
}

private void stopSession(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
final String instanceId = call.argument("instanceId");
if (TextUtils.isEmpty(instanceId)) {
result.error("missing_instance_id", "Expected a non-empty instanceId.", null);
return;
}
mainHandler.post(() -> {
if (!Omid.isActive()) {
result.error("omid_inactive", "OM SDK is not active.", null);
return;
}
OmidSessionState sessionState = sessions.remove(instanceId);
if (sessionState == null) {
result.error("session_not_found", "No active OMID session for instanceId " + instanceId + ".", null);
return;
}
try {
sessionState.adSession.finish();
} catch (Exception e) {
result.error("omid_stop_failed", "Failed to finish OMID session: " + e.getMessage(), null);
return;
}
InAppWebView webView = sessionState.webViewRef.get();
if (webView != null) {
// The 1.1 second delay (OMID guidance) gives the system time to properly clean up
// any remaining ad-related operations before allowing the WebView to be disposed of.
webView.postDelayed(() -> webView.hashCode(), WEBVIEW_HOLD_DURATION_MS);
}
result.success(true);
});
}

private boolean ensureOmidActivated(@NonNull MethodChannel.Result result) {
if (Omid.isActive()) {
return true;
}
Context context = plugin.applicationContext;
if (context == null) {
result.error("omid_activation_failed", "Application context is unavailable for OM SDK activation.", null);
return false;
}
try {
Omid.activate(context);
return true;
} catch (Exception e) {
result.error("omid_activation_failed", "Failed to activate OM SDK: " + e.getMessage(), null);
return false;
}
}

private static class OmidSessionState {
final WeakReference<InAppWebView> webViewRef;
final AdSession adSession;

OmidSessionState(@NonNull InAppWebView webView, @NonNull AdSession adSession) {
this.webViewRef = new WeakReference<>(webView);
this.adSession = adSession;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -165,24 +165,6 @@ public void onReceiveValue(Boolean value) {
}
result.success(true);
break;
case "getNativeWebViewByInstanceId":
{
String instanceId = (String) call.argument("instanceId");
if (instanceId == null) {
result.success(null);
break;
}
InAppWebView nativeWebView = WebViewInstanceRegistry.get(instanceId);
if (nativeWebView != null) {
HashMap<String, Object> webViewInfo = new HashMap<>();
webViewInfo.put("instanceId", instanceId);
webViewInfo.put("hashCode", System.identityHashCode(nativeWebView));
result.success(webViewInfo);
} else {
result.success(null);
}
}
break;
case "getAllRegisteredInstanceIds":
result.success(new ArrayList<>(WebViewInstanceRegistry.getRegisteredInstanceIds()));
break;
Expand All @@ -191,7 +173,7 @@ public void onReceiveValue(Boolean value) {
String instanceId = (String) call.argument("instanceId");
result.success(instanceId != null && WebViewInstanceRegistry.isRegistered(instanceId));
}
break;
break;
default:
result.notImplemented();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2751,11 +2751,6 @@ class AndroidInAppWebViewController extends PlatformInAppWebViewController
return id;
}

@override
Future<dynamic> getNativeWebViewByInstanceId(String instanceId) async {
return await _staticChannel.invokeMethod('getNativeWebViewByInstanceId', {'instanceId': instanceId});
}

@override
Future<List<String>> getAllRegisteredInstanceIds() async {
final result = await _staticChannel.invokeMethod<List<dynamic>>('getAllRegisteredInstanceIds');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,17 +50,6 @@ public class InAppWebViewManager: ChannelDelegate {
clearAllCache(includeDiskFiles: includeDiskFiles, completionHandler: {
result(true)
})
case "getNativeWebViewByInstanceId":
if let instanceId = arguments?["instanceId"] as? String,
let nativeWebView = WebViewInstanceRegistry.get(instanceId: instanceId) {
let webViewInfo: [String: Any] = [
"instanceId": instanceId,
"hashCode": Int(bitPattern: Unmanaged.passUnretained(nativeWebView).toOpaque())
]
result(webViewInfo)
} else {
result(nil)
}
case "getAllRegisteredInstanceIds":
result(WebViewInstanceRegistry.getRegisteredInstanceIds())
case "isInstanceIdRegistered":
Expand Down
Loading