Skip to content

[Android] Allow full screen videos from WebView #15472

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Mar 26, 2025
Merged

[Android] Allow full screen videos from WebView #15472

merged 12 commits into from
Mar 26, 2025

Conversation

jsuarezruiz
Copy link
Contributor

Description of Change

Allow full screen videos from WebView.

image

To validate the changes can use the new added sample to the WebView galleries in the .NET MAUI Gallery App.

Issues Fixed

Fixes #8030
Fixes #7720

@jsuarezruiz jsuarezruiz added t/bug Something isn't working platform/android 🤖 legacy-area-controls Label, Button, CheckBox, Slider, Stepper, Switch, Picker, Entry, Editor area-controls-webview WebView labels Jun 6, 2023
@vhugogarcia
Copy link
Contributor

Hello @jsuarezruiz,

Thanks for adding this new feature to .NET MAUI Webviews on Android. I would like to share some ideas here.

On this method, we need to return to the original orientation of the device. However, there are some points we need to cover and validate:

  • Phone in portrait-mode, enters full screen it goes in landscape-mode, then when returning from full screen the device orientation must change to portrait-mode again.
  • it may happen that the app is compatible with Tablet/Phone in landscape-only. So, if the video goes full screen when returned it must not change to portrait-mode because the app only supports landscape-mode.

How this can be solved? well, if it is possible to know the orientation supported by the app then we can just add a validation for it, otherwise, we need to think how to validate that for the second scenario.

@vhugogarcia
Copy link
Contributor

Also, we need to validate which is the status of the current SystemBars settings are on the app, because for example, I may have an application that only shows the StatusBar and not the navigation bar, so by doing this, we are showing status and navigation bar which may not be the expected result.

context.Window.InsetsController?.Show(WindowInsets.Type.SystemBars());

So, we may need to validate if the user has StatusBarVisible and NavigationBar visible before enter full screen save it on a variable or somewhere, and then when returning we show only what was visible. :)

What do you think @jsuarezruiz ?

@jsuarezruiz
Copy link
Contributor Author

Also, we need to validate which is the status of the current SystemBars settings are on the app, because for example, I may have an application that only shows the StatusBar and not the navigation bar, so by doing this, we are showing status and navigation bar which may not be the expected result.

context.Window.InsetsController?.Show(WindowInsets.Type.SystemBars());

So, we may need to validate if the user has StatusBarVisible and NavigationBar visible before enter full screen save it on a variable or somewhere, and then when returning we show only what was visible. :)

What do you think @jsuarezruiz ?

Good point. Agree. I will review it shortly. Thanks for all the feedback.

@jsuarezruiz jsuarezruiz requested a review from mattleibow June 7, 2023 10:18
@jsuarezruiz
Copy link
Contributor Author

Also, we need to validate which is the status of the current SystemBars settings are on the app, because for example, I may have an application that only shows the StatusBar and not the navigation bar, so by doing this, we are showing status and navigation bar which may not be the expected result.
context.Window.InsetsController?.Show(WindowInsets.Type.SystemBars());
So, we may need to validate if the user has StatusBarVisible and NavigationBar visible before enter full screen save it on a variable or somewhere, and then when returning we show only what was visible. :)
What do you think @jsuarezruiz ?

Good point. Agree. I will review it shortly. Thanks for all the feedback.

Updated adding more validations etc.

@jsuarezruiz jsuarezruiz marked this pull request as draft June 27, 2023 15:06
@jsuarezruiz jsuarezruiz marked this pull request as ready for review July 31, 2023 14:32
@samhouts samhouts added this to the Under Consideration milestone Jul 31, 2023
@jsuarezruiz jsuarezruiz requested a review from a team as a code owner August 18, 2023 11:48
@samhouts samhouts added the stale Indicates a stale issue/pr and will be closed soon label Sep 11, 2023
@vhugogarcia
Copy link
Contributor

Hello @jsuarezruiz , is this going to be available in .NET MAUI 8 GA? 🙂👍🏻

@jsuarezruiz jsuarezruiz removed the stale Indicates a stale issue/pr and will be closed soon label Oct 18, 2023
@samhouts samhouts added the stale Indicates a stale issue/pr and will be closed soon label Dec 7, 2023
@jsuarezruiz jsuarezruiz removed the stale Indicates a stale issue/pr and will be closed soon label Dec 12, 2023
@hartez hartez removed their request for review January 10, 2024 20:28
@jsuarezruiz jsuarezruiz requested review from jfversluis and tj-devel709 and removed request for StephaneDelcroix January 18, 2024 12:44
@jsuarezruiz
Copy link
Contributor Author

@jfversluis, @tj-devel709 Added you as reviewers. Could you take a look when you can? Thanks.

jfversluis
jfversluis previously approved these changes Jan 24, 2024
@rmarinho rmarinho changed the base branch from main to net9.0 March 20, 2024 14:12
@jfversluis jfversluis added this to the .NET 9 SR4 milestone Dec 20, 2024
Copy link
Member

@jfversluis jfversluis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

D:\a_work\1\s\src\Core\src\Platform\Android\MauiWebChromeClient.cs(147,5): error CA1422: This call site is reachable on: 'Android' 21.0 and later. 'Window.SetDecorFitsSystemWindows(bool)' is obsoleted on: 'android' 35.0 and later. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1422) [D:\a_work\1\s\src\Core\src\Core.csproj::TargetFramework=net9.0-android35.0]
D:\a_work\1\s\src\Core\src\Platform\Android\MauiWebChromeClient.cs(108,5): error CA1422: This call site is reachable on: 'Android' 21.0 and later. 'Window.SetDecorFitsSystemWindows(bool)' is obsoleted on: 'android' 35.0 and later. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1422) [D:\a_work\1\s\src\Core\src\Core.csproj::TargetFramework=net9.0-android35.0]

@jfversluis jfversluis self-assigned this Jan 3, 2025
@vikher
Copy link

vikher commented Jan 10, 2025

If you're looking to implement full-screen functionality for Android WebView video playback, you can use the following workaround. This approach involves creating a custom handler that extends MauiWebChromeClient to support full-screen video features.

Steps to Implement:
Create a custom WebPlayerChromeClient:

using Android.App;
using Android.Content.Res;
using Android.OS;
using Android.Views;
using Android.Widget;
using Microsoft.Maui.Handlers;
using Microsoft.Maui.Platform;

namespace Mobile.Handlers
{
    public class WebPlayerChromeClient : MauiWebChromeClient
    {
        private readonly Activity context;
        private int originalUiOptions;
        private Android.Views.View customView;
        private ICustomViewCallback videoViewCallback;
        private bool disposed = false; // To detect redundant calls

        public WebPlayerChromeClient(IWebViewHandler handler) : base(handler)
        {
            this.context = Microsoft.Maui.ApplicationModel.Platform.CurrentActivity;
        }

        public override void OnHideCustomView()
        {
            if (context != null)
            {
                if (context.Window.DecorView is FrameLayout layout)
                    layout.RemoveView(customView);
                
                if (!IsTablet(context))
                    context.RequestedOrientation = Android.Content.PM.ScreenOrientation.Portrait;

                if (Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.R)
                {
                    context.Window.SetDecorFitsSystemWindows(true);
                    context.Window.InsetsController?.Show(WindowInsets.Type.SystemBars());
                }
                else
                {
                   context.Window.DecorView.SystemUiVisibility = (StatusBarVisibility)originalUiOptions;
                }

                videoViewCallback.OnCustomViewHidden();
                customView?.Dispose(); // Dispose the custom view if it implements IDisposable
                customView = null;
                videoViewCallback = null;
            }
        }

        public override void OnShowCustomView(Android.Views.View view, ICustomViewCallback callback)
        {
            if (customView != null)
            {
                OnHideCustomView();
                return;
            }

            if (context == null)
                return;

            videoViewCallback = callback;
            customView = view;
            customView.SetBackgroundColor(Android.Graphics.Color.White);
            context.RequestedOrientation = Android.Content.PM.ScreenOrientation.Landscape;

            if (Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.R)
            {
                context.Window.SetDecorFitsSystemWindows(false);
                context.Window.InsetsController?.Hide(WindowInsets.Type.SystemBars());
            }
            else
            {
                originalUiOptions = (int)context.Window.DecorView.SystemUiVisibility;
                var newUiOptions = originalUiOptions | (int)SystemUiFlags.LayoutStable | (int)SystemUiFlags.LayoutHideNavigation | (int)SystemUiFlags.LayoutHideNavigation |
                                (int)SystemUiFlags.LayoutFullscreen | (int)SystemUiFlags.HideNavigation | (int)SystemUiFlags.Fullscreen | (int)SystemUiFlags.Immersive;

                context.Window.DecorView.SystemUiVisibility = (StatusBarVisibility)newUiOptions;
            }

            if (context.Window.DecorView is FrameLayout layout)
                layout.AddView(customView, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MatchParent, ViewGroup.LayoutParams.MatchParent));
        }

        private bool IsTablet(Activity context)
        {
            return (context.Resources.Configuration.ScreenLayout & ScreenLayout.SizeMask) >= ScreenLayout.SizeLarge;
        }
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this); // Prevent finalization if already disposed
        }

        protected virtual void Dispose(bool disposing)
        {
            if (disposed)
                return;

            if (disposing)
            {
                // Dispose managed resources
                customView?.Dispose();
                // Dispose videoViewCallback if it implements IDisposable
                if (videoViewCallback is IDisposable disposableCallback)
                {
                    disposableCallback.Dispose();
                }
            }

            // Free unmanaged resources here, if any

            disposed = true;
        }

        // Destructor (finalizer) if you use unmanaged resources
        ~WebPlayerChromeClient()
        {
            Dispose(false);
        }

    }
}

Add a handler to enable video features:

using System.Runtime.Versioning;

namespace Mobile.Handlers
{
    public class WebViewVideoHandler
    {
        [SupportedOSPlatform("android26.0")]
        public static void EnableVideoFeatures()
        {
#if ANDROID
            Microsoft.Maui.Handlers.WebViewHandler.Mapper.ModifyMapping(
                nameof(Android.Webkit.WebView.WebChromeClient),
                (handler, view, args) =>
                {
                    if (Android.OS.Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.O)
                    {
                        handler.PlatformView.SetWebChromeClient(new WebPlayerChromeClient(handler));
                    }
                });
#endif
        }
    }
}

Register the handler in MauiProgram.cs:
Add the following line to your CreateMauiApp method:

WebViewVideoHandler.EnableVideoFeatures();

This code sets up the necessary functionality to manage full-screen video playback by handling OnShowCustomView and OnHideCustomView. It also ensures compatibility with both tablets and phones, supporting immersive mode on Android devices.

@jsuarezruiz
Copy link
Contributor Author

jsuarezruiz commented Jan 20, 2025

This PR is from... 2023! It requires changes from the original implementation due to using deprecated APIs. I just rebased, but it would require a change in implementation. I'm moving it to Draft until we have the changes.

@jsuarezruiz jsuarezruiz marked this pull request as draft January 20, 2025 13:40
@PureWeen PureWeen removed this from the .NET 9 SR4 milestone Feb 10, 2025
@jsuarezruiz jsuarezruiz marked this pull request as ready for review March 17, 2025 14:12
@jsuarezruiz
Copy link
Contributor Author

Updated implementation without use obsolete APIs.

@jsuarezruiz jsuarezruiz changed the base branch from main to net10.0 March 25, 2025 12:15
@jsuarezruiz
Copy link
Contributor Author

Retarget to .NET 10.

@rmarinho rmarinho added this to the .NET 10.0-preview3 milestone Mar 25, 2025
@rmarinho rmarinho moved this from Todo to Approved in MAUI SDK Ongoing Mar 25, 2025
@rmarinho rmarinho merged commit 3204eac into net10.0 Mar 26, 2025
122 of 128 checks passed
@rmarinho rmarinho deleted the fix-8030 branch March 26, 2025 13:01
@github-project-automation github-project-automation bot moved this from Approved to Done in MAUI SDK Ongoing Mar 26, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

MAUI WebView Apps do not allow iframe videos to run full screen Android WebView FullScreen for video