Search code examples
c#androidxamarinxamarin.formsandroid-immersive

Keep immersive mode all over the app on Android, Xamarin.forms


I want to keep immersive mode all over the app on Android using Xamarin.forms. I have read few blogs and posts. This one works but only until I press an input text box or I scroll over the screen. I want to avoid this. This question debate my concern but I don't understand the solution. Maybe it is obsolete. My android device is 7.1 and this is my code on my .Android MainActivity, void OnCreate, void OnCreate:

public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity{
    protected override void OnCreate(Bundle savedInstanceState){
        Xamarin.Essentials.Platform.Init(this, savedInstanceState);
        global::Xamarin.Forms.Forms.Init(this, savedInstanceState);

        TabLayoutResource = Resource.Layout.Tabbar;
        ToolbarResource = Resource.Layout.Toolbar;
        base.OnCreate(savedInstanceState);
        LoadApplication(new App());

        ImmersiveMode();
    }

    public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults){
        Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);

        base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
    }

    public void ImmersiveMode() {
        int uiOptions = (int)(Forms.Context as Activity).Window.DecorView.SystemUiVisibility;
        uiOptions |= (int)SystemUiFlags.Fullscreen;
        uiOptions |= (int)SystemUiFlags.HideNavigation;
        uiOptions |= (int)SystemUiFlags.ImmersiveSticky;
        (Forms.Context as Activity).Window.DecorView.SystemUiVisibility = (StatusBarVisibility)uiOptions;
    }
}

How could I resolve this? Could you be specific on where I should located the new code. I'm noob on Xamarin.Forms and it looks tricky for me. Thank you

Edit: this code:

this.Window.AddFlags(WindowManagerFlags.Fullscreen); on the .Android MainActivity hides the Android bar with the wifi signal, time, bluetooth, etc.

Is there a line like that one to hide this android buttons?

enter image description here


Solution

  • Window.DecorView.SystemUiVisibility is deprecated as of Android R (11). There is a new way to do this in this API version.

    Below is the code I used in one of my apps. I had to override a bunch of methods in MainActivity class.

    using System;
    
    using Android.App;
    using Android.Content.PM;
    using Android.Runtime;
    using Android.OS;
    using Android.Views;
    
    namespace SampleApp.Droid
    {
        [Activity(Label = "SampleApp", Icon = "@mipmap/icon", Theme = "@style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize )]
        public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
        {
            protected override void OnCreate(Bundle savedInstanceState)
            {
                base.OnCreate(savedInstanceState);
    
                Xamarin.Essentials.Platform.Init(this, savedInstanceState);
                global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
                LoadApplication(new App());
                SetFullscreenFlags();
            }
    
            protected override void OnResume()
            {
                base.OnResume();
                SetFullscreenFlags();
            }
    
            public override void OnWindowFocusChanged(bool hasFocus)
            {
                base.OnWindowFocusChanged(hasFocus);
                SetFullscreenFlags();
            }
    
            protected void SetFullscreenFlags()
            {
                var attrs = Window.Attributes;
                attrs.Flags |= WindowManagerFlags.Fullscreen;
                Window.Attributes = attrs;
    
                // BuildVersionCodes has a wrong value for Android R (1000 instead of 30)
                // https://github.com/xamarin/xamarin-android/issues/5723
                // if (Build.VERSION.SdkInt >= BuildVersionCodes.R)
                if ((int)Build.VERSION.SdkInt >= 30)
                {
                    Window.SetDecorFitsSystemWindows(false);
    
                    if (Window.InsetsController != null)
                    {
                        Window.InsetsController.Hide(WindowInsets.Type.NavigationBars());
                        Window.InsetsController.Hide(WindowInsets.Type.StatusBars());
                        Window.InsetsController.Hide(WindowInsets.Type.SystemBars());
                        Window.InsetsController.Show(WindowInsets.Type.Ime());
                    }
                }
                else
                {
                    var uiOptions = (int)Window.DecorView.SystemUiVisibility;
                    var newUiOptions = (int)uiOptions;
    
                    newUiOptions |=
                        (int)SystemUiFlags.LayoutStable |
                        (int)SystemUiFlags.LayoutHideNavigation |
                        (int)SystemUiFlags.LayoutFullscreen |
                        (int)SystemUiFlags.HideNavigation |
                        (int)SystemUiFlags.Fullscreen |
                        (int)SystemUiFlags.ImmersiveSticky;
    
                    Window.DecorView.SystemUiVisibility = (StatusBarVisibility)newUiOptions;
                }
            }
    
            public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
            {
                Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
    
                base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
            }
    
        }
    }
    

    Previously, I also implemented the View.IOnSystemUiVisibilityChangeListener interface. But it is also deprecated and will be removed in future versions:

    public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity, View.IOnSystemUiVisibilityChangeListener
    {
        protected override void OnCreate(Bundle savedInstanceState)
        {
            // omitted code
    
            Window.DecorView.SetOnSystemUiVisibilityChangeListener(this);
        }
    
        public void OnSystemUiVisibilityChange([GeneratedEnum] StatusBarVisibility visibility)
        {
            SetFullscreenFlags();
        }
    }
    

    EDIT:

    If you want to keep immersive mode after user focus some input control on your screen you can implement ViewTreeObserver.IOnGlobalFocusChangeListener interface. Here's how I did:

    public class MainActivity : 
        global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity, 
        ViewTreeObserver.IOnGlobalFocusChangeListener
    {
        protected override void OnCreate(Bundle savedInstanceState)
        {
            // ommited code
        }
    
        public override void SetContentView(View view)
        {
            base.SetContentView(view);
    
            if (view?.ViewTreeObserver != null)
                view.ViewTreeObserver.AddOnGlobalFocusChangeListener(this);
        }
    
        public override void SetContentView(View view, ViewGroup.LayoutParams @params)
        {
            base.SetContentView(view, @params);
    
            if (view?.ViewTreeObserver != null)
                view.ViewTreeObserver.AddOnGlobalFocusChangeListener(this);
        }
    
        public void OnGlobalFocusChanged(View oldFocus, View newFocus)
        {
            SetFullscreenFlags();
        }
    }