Skip to content

HybridWebView/WebView - Implement methods for playsinline, autoplay, and other playback configuration options #23747

@jonmdev

Description

@jonmdev

Description

Now that HybridWebView (fantastic game changing component) is being implemented in .NET 9 (as per Eilon/MauiHybridWebView#70) I have a suggestion.

In order to set videos to play inline and autoplay, the working methods I have found are in current system:

1) iOS

Rewrite the platform view creation as follows using Reflection to access things we don't have access to. Run this anywhere before you create a HybridWebView to reconfigure the default construction.

#if IOS || MACCATALYST

            HybridWebView.HybridWebViewHandler.PlatformViewFactory = (handler) => {
                
                var config = new WKWebViewConfiguration();

                //====================================================
                //CUSTOM CONFIG
                config.AllowsAirPlayForMediaPlayback = true;
                config.AllowsInlineMediaPlayback = true;
                config.AllowsPictureInPictureMediaPlayback = true;
                config.MediaTypesRequiringUserActionForPlayback = WebKit.WKAudiovisualMediaTypes.None;


                Action<Uri, string> MessageReceivedAction = delegate (Uri uri, string message) { // to replace https://github.com/Eilon/MauiHybridWebView/blob/3ca801076a1e3fbe3b8922b2429524df20def6a4/HybridWebView/Platforms/iOS/HybridWebViewHandler.iOS.cs#L40
                    ((HybridWebView.HybridWebView)handler.VirtualView).OnMessageReceived(message);
                };

                //REFLECTION METHOD (https://stackoverflow.com/a/39076814/10305478)
                object CreatePrivateClassInstance(string typeName, object[] parameters) {
                    Type type = AppDomain.CurrentDomain.GetAssemblies().
                             SelectMany(assembly => assembly.GetTypes()).FirstOrDefault(t => t.Name == typeName);
                    return type.GetConstructors()[0].Invoke(parameters);
                }

                IWKScriptMessageHandler webViewScriptMessageHandler = (IWKScriptMessageHandler)CreatePrivateClassInstance("WebViewScriptMessageHandler", [MessageReceivedAction]);
                config.UserContentController.AddScriptMessageHandler(webViewScriptMessageHandler, "webwindowinterop");

                IWKUrlSchemeHandler? wKUrlSchemeHandler = (IWKUrlSchemeHandler?)CreatePrivateClassInstance("SchemeHandler", [handler as HybridWebViewHandler]);
                config.SetUrlSchemeHandler(wKUrlSchemeHandler, urlScheme: "app");
                //============================================


                // Legacy Developer Extras setting.
                var enableWebDevTools = ((HybridWebView.HybridWebView)(handler as HybridWebViewHandler).VirtualView).EnableWebDevTools;
                config.Preferences.SetValueForKey(NSObject.FromObject(enableWebDevTools), new NSString("developerExtrasEnabled"));
                var platformView = new MauiWKWebView(RectangleF.Empty, handler as HybridWebViewHandler, config);

                if (OperatingSystem.IsMacCatalystVersionAtLeast(major: 13, minor: 3) ||
                    OperatingSystem.IsIOSVersionAtLeast(major: 16, minor: 4)) {
                    // Enable Developer Extras for Catalyst/iOS builds for 16.4+
                    platformView.SetValueForKey(NSObject.FromObject(enableWebDevTools), new NSString("inspectable"));
                }

                return platformView;
            };
#endif

2) Windows

From: https://stackoverflow.com/questions/68709227/how-to-enable-media-autoplay-in-microsoft-webview2

#if WINDOWS
     Environment.SetEnvironmentVariable("WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS", "--autoplay-policy=no-user-gesture-required");
#endif

Run this anywhere before creating hybridwebview. This one I believe must be set globally only like this, not per element.

3) Android

From: https://learn.microsoft.com/en-us/dotnet/maui/user-interface/controls/webview?view=net-maui-8.0&pivots=devices-android

(i) Create permission manager

#if ANDROID

    internal class MyWebChromeClient : MauiWebChromeClient {
        public MyWebChromeClient(IWebViewHandler handler) : base(handler) {

        }

        //note you need android permissions for android.permission.MODIFY_AUDIO_SETTINGS, android.permission.RECORD_AUDIO, and android.permission.CAMERA
        public override void OnPermissionRequest(PermissionRequest request) {
            Debug.WriteLine("CHECK MYWEBCHROMECLIENT PERMISSION");
            // Process each request
            foreach (var resource in request.GetResources()) {
                // Check if the web page is requesting permission to the camera
                if (resource.Equals(PermissionRequest.ResourceVideoCapture, StringComparison.OrdinalIgnoreCase)) {
                    // Get the status of the .NET MAUI app's access to the camera
                    PermissionStatus status = Permissions.CheckStatusAsync<Permissions.Camera>().Result;

                    // Deny the web page's request if the app's access to the camera is not "Granted"
                    if (status != PermissionStatus.Granted) {
                        request.Deny();
                    }
                    else {
                        request.Grant(request.GetResources());
                    }
                    return;
                }
                if (resource.Equals(PermissionRequest.ResourceAudioCapture, StringComparison.OrdinalIgnoreCase)) {
                    // Get the status of the .NET MAUI app's access to the camera
                    PermissionStatus status = Permissions.CheckStatusAsync<Permissions.Microphone>().Result;

                    // Deny the web page's request if the app's access to the camera is not "Granted"
                    if (status != PermissionStatus.Granted) {

                        request.Deny();
                    }

                    else {
                        request.Grant(request.GetResources());
                    }
                    return;
                }
            }

            base.OnPermissionRequest(request);
        }
    }
#endif

(ii) Apply to WebView via HandlerChanged

            webView.HandlerChanged += delegate {
#if ANDROID
                var platformView = (webView.Handler as HybridWebViewHandler).PlatformView;
                platformView.Settings.MediaPlaybackRequiresUserGesture = false; //this works to force autoplay https://stackoverflow.com/questions/38975229/auto-play-video-in-webview

                //set permission client as per: https://learn.microsoft.com/en-us/dotnet/maui/user-interface/controls/webview?view=net-maui-8.0&pivots=devices-android
                MyWebChromeClient permissionClient = new(webView.Handler as IWebViewHandler);
                platformView.SetWebChromeClient(permissionClient);
#endif
            };

It would be nice if we could allow each HybridWebView to have its own configuration method (or global configuration) built into the system.

Alternatively, I post this here as reference for working methods for anyone trying to configure this, and hope we at least won't lose these working methods in the new rebuild for .NET9 without alternatives to replace it.

Any thoughts @Eilon ?

Thanks again for the great component.

Public API Changes

Can have global static methods like:

HybridWebView.setPlaysInline(true);
HybridWebView.setAutoPlay(true);

or same functions run per webview (but note in iOS once the webview is constructed as above it is already too late unless we can pass in configuration info like a "HybridWebViewConfig" as an optional argument on construction).

Intended Use-Case

Allow autoplay, inline play, other possible platform specific playback features as part of the basic build in function. Or at least continue to be able to use what we have that works without it breaking by .NET 9 changes.

Thanks.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions