Android Question Transparent System Bars issue in B4XMainPage

Hi,
In B4A I’m trying to use Edge-To-Edge for Android 15+ (SDK 35+), and it works correctly.
For devices below Android 15, I use the following code to make the StatusBar and NavigationBar transparent:
B4X:
Sub MakeSystemBarsTransparent(act As Activity)
    Dim jo As JavaObject
    Dim window As JavaObject = jo.InitializeContext.RunMethod("getWindow", Null)

    Try
        Dim WindowCompat As JavaObject
        WindowCompat.InitializeStatic("androidx.core.view.WindowCompat")
        WindowCompat.RunMethod("setDecorFitsSystemWindows", Array(window, False))
    Catch
    End Try

    Try
        window.RunMethod("setNavigationBarContrastEnforced", Array(False))
    Catch
    End Try

    Try
        window.RunMethod("clearFlags", Array(Bit.Or(0x04000000, 0x00000002)))
        window.RunMethod("addFlags", Array(0x80000000))
    Catch
    End Try

    Try
        window.RunMethod("setStatusBarColor", Array(0x00000000))
        window.RunMethod("setNavigationBarColor", Array(0x00000000))
    Catch
    End Try

    Try
        window.RunMethod("setNavigationBarDividerColor", Array(0x00000000))
    Catch
    End Try

    Try
        Dim decor As JavaObject = window.RunMethod("getDecorView", Null)
        Dim controller As JavaObject
        controller.InitializeNewInstance("androidx.core.view.WindowInsetsControllerCompat", Array(window, decor))
        controller.RunMethod("setAppearanceLightStatusBars", Array(False))
        Try
            controller.RunMethod("setAppearanceLightNavigationBars", Array(False))
        Catch
        End Try
    Catch
    End Try

    Try
        Dim decor As JavaObject = window.RunMethod("getDecorView", Null)
        Dim flags As Int = Bit.Or(Bit.Or(0x00000400, 0x00000200), 0x00000100)
        If GetDeviceSDKVersion >= 19 Then flags = Bit.Or(flags, 0x00001000)
        decor.RunMethod("setSystemUiVisibility", Array(flags))
    Catch
    End Try

    If GetDeviceSDKVersion < 35 Then
        act.Height = act.Height + GetStatusBarHeight + GetNavigationBarHeight
    End If
End Sub
I call this method once in Main.
When I create a new Page, everything works properly.
However, inside B4XMainPage, on devices below Android 15, I get the following issue:
The Root panel does not extend fully to the bottom of the Activity. There is an empty gap at the bottom of the screen, as if the Root height is shortened. Even this line has no effect:
B4X:
Root.Height = Root.Height + 50dip
These are my manifest entries:
B4X:
AddManifestText(
<uses-sdk android:minSdkVersion="23" android:targetSdkVersion="35"/>
<supports-screens android:largeScreens="true"
    android:normalScreens="true"
    android:smallScreens="true"
    android:anyDensity="true"/>)
SetApplicationAttribute(android:icon, "@drawable/icon")
SetApplicationAttribute(android:label, "$LABEL$")

'End of default text.
CreateResource(values, theme.xml,
<resources>
    <style name="MyAppTheme" parent="Theme.AppCompat.DayNight.DarkActionBar">
        <item name="android:windowDisablePreview">true</item>
        <item name="colorPrimary">#191919</item>
        <item name="colorPrimaryDark">#1F1F1F</item>
        <item name="colorAccent">#5F3FAF</item>
        <item name="windowNoTitle">true</item>
        <item name="windowActionBar">false</item>
        <item name="android:windowDrawsSystemBarBackgrounds">true</item>
        <item name="android:statusBarColor">@android:color/transparent</item>
        <item name="android:navigationBarColor">@android:color/transparent</item>
    </style>
</resources>

)
SetApplicationAttribute(android:theme, "@style/MyAppTheme")

CreateResourceFromFile(Macro, FirebaseAnalytics.Firebase)

SetActivityAttribute(main, android:windowSoftInputMode, adjustResize|stateHidden)
Any help would be appreciated. Thanks! 🙏
 
Thank you, Erel.
I checked the example, but it only enables fullscreen mode. Unfortunately, I couldn’t adapt it to what I need.

Would it be possible for you to provide a small example that enables Edge-To-Edge (or transparent system bars) on all Android versions from 23 to 36, so that the Root and views are drawn underneath the status bar and navigation bar?

It would really help me understand the correct way to implement this. Thanks again!
 
Upvote 0

b4x-de

Active Member
Licensed User
Longtime User
You might adapt the solution posted in this thread to b4xpages:

 
Upvote 0
With the help of these codes, I was able to activate Edge To Edge on devices running Android versions lower than 15:
B4X:
Sub MakeSystemBarsTransparent(act As Activity)
    If GetDeviceSDKVersion >= 28 Then
        Dim ctxt As JavaObject
        ctxt.InitializeContext
        ctxt.RunMethodJO("getWindow", Null).RunMethodJO("getAttributes", Null).SetField("layoutInDisplayCutoutMode", 1)
    End If
    
    Dim jo As JavaObject
    Dim window As JavaObject = jo.InitializeContext.RunMethod("getWindow", Null)

    Try
        Dim WindowCompat As JavaObject
        WindowCompat.InitializeStatic("androidx.core.view.WindowCompat")
        WindowCompat.RunMethod("setDecorFitsSystemWindows", Array(window, False))
    Catch
    End Try

    Try
        window.RunMethod("setNavigationBarContrastEnforced", Array(False))
    Catch
    End Try

    Try
        window.RunMethod("clearFlags", Array(Bit.Or(0x04000000, 0x00000002)))
        window.RunMethod("addFlags", Array(0x80000000))
    Catch
    End Try

    Try
        window.RunMethod("setStatusBarColor", Array(0x00000000))
        window.RunMethod("setNavigationBarColor", Array(0x00000000))
    Catch
    End Try

    Try
        window.RunMethod("setNavigationBarDividerColor", Array(0x00000000))
    Catch
    End Try

    Try
        Dim decor As JavaObject = window.RunMethod("getDecorView", Null)
        Dim controller As JavaObject
        controller.InitializeNewInstance("androidx.core.view.WindowInsetsControllerCompat", Array(window, decor))
        controller.RunMethod("setAppearanceLightStatusBars", Array(False))
        Try
            controller.RunMethod("setAppearanceLightNavigationBars", Array(False))
        Catch
        End Try
    Catch
    End Try

    Try
        Dim decor As JavaObject = window.RunMethod("getDecorView", Null)
        Dim flags As Int = Bit.Or(Bit.Or(0x00000400, 0x00000200), 0x00000100)
        If GetDeviceSDKVersion >= 19 Then flags = Bit.Or(flags, 0x00001000)
        decor.RunMethod("setSystemUiVisibility", Array(flags))
    Catch
    End Try
    
    Dim lv As LayoutValues = GetRealSize
    Dim jo As JavaObject = act
    jo.RunMethod("setBottom", Array(lv.Height))
    jo.RunMethod("setRight", Array(lv.Width))
    act.Height = lv.Height
    act.Width = lv.Width

'    If GetDeviceSDKVersion < 35 Then
'        act.Height = act.Height + GetStatusBarHeight + GetNavigationBarHeight
'    End If
End Sub
However, this time the IME stopped working. This code no longer functions:
B4X:
Sub IME_HeightChanged (NewHeight As Int, OldHeight As Int)
    Log(NewHeight)
    B4XPages.GetManager.RaiseEvent(B4XPages.GetManager.GetTopPage, "IME_HeightChanged", Array(NewHeight + CodeBox.GetStatusBarHeight + CodeBox.GetNavigationBarHeight, OldHeight + CodeBox.GetStatusBarHeight + CodeBox.GetNavigationBarHeight))
End Sub
 
Upvote 0
I wrote a Java class that works fine on B4xMainPage, but it doesn't work on other pages and _HeightChanged is not called.
Java:
package com.utils;
import android.view.View;
import android.view.WindowManager;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import anywheresoftware.b4a.BA;
import anywheresoftware.b4a.BA.ActivityObject;
import anywheresoftware.b4a.BA.ShortName;
@ShortName("KeyboardUtil")
@ActivityObject
public class KeyboardUtil {
    private String eventName;
    private Integer lastVisibleHeight = null;
  
    public void Initialize(String EventName) {
        this.eventName = EventName.toLowerCase(BA.cul);
    }
    public void AddHeightChangedEvent(BA ba) {
        if (!(ba.vg instanceof View)) return;
        View rootView = ((View)ba.vg).getRootView();
        if (rootView == null) return;
        ViewCompat.setOnApplyWindowInsetsListener(rootView, null);
        ViewCompat.setOnApplyWindowInsetsListener(rootView, (v, insets) -> {
            WindowInsetsCompat compatInsets = ViewCompat.getRootWindowInsets(v);
            if (compatInsets == null) return insets;
            Insets systemInsets = compatInsets.getInsets(WindowInsetsCompat.Type.systemBars());
            Insets imeInsets = compatInsets.getInsets(WindowInsetsCompat.Type.ime());
            int bottomInset = compatInsets.getInsets(WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.ime()).bottom;
            int topInset = systemInsets.top;
            int visibleHeight = v.getHeight() - topInset - bottomInset;
            if (lastVisibleHeight == null) {
                lastVisibleHeight = visibleHeight;
                return insets;
            }
            if (visibleHeight != lastVisibleHeight) {
                int oldHeight = lastVisibleHeight;
                int newHeight = visibleHeight;
                lastVisibleHeight = newHeight;
                ba.raiseEventFromUI(null, eventName + "_heightchanged",
                        new Object[]{newHeight, oldHeight});
            }
            return insets;
        });
        
        ViewCompat.requestApplyInsets(rootView);
        if (ba.activity != null) {
            ba.activity.getWindow().setDecorFitsSystemWindows(false);
            ba.activity.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
        }
    }
    public void RemoveListener(BA ba) {
        if (ba.vg instanceof View) {
            View root = ((View)ba.vg).getRootView();
            if (root != null) {
                ViewCompat.setOnApplyWindowInsetsListener(root, null);
            }
        }
        lastVisibleHeight = null;
    }
}
 
Upvote 0
Top