From d4bace023aeb58a601c63ea1e31f9548b1213475 Mon Sep 17 00:00:00 2001 From: Li Date: Mon, 1 Jul 2024 22:51:25 +1000 Subject: [PATCH] Fix issue where android keyboard covers input --- LiftLog.App/Platforms/Android/MainActivity.cs | 26 +++++- .../Android/WebViewSoftInputPatch.cs | 86 +++++++++++++++++++ 2 files changed, 108 insertions(+), 4 deletions(-) create mode 100644 LiftLog.App/Platforms/Android/WebViewSoftInputPatch.cs diff --git a/LiftLog.App/Platforms/Android/MainActivity.cs b/LiftLog.App/Platforms/Android/MainActivity.cs index 14729987..17bc0d48 100644 --- a/LiftLog.App/Platforms/Android/MainActivity.cs +++ b/LiftLog.App/Platforms/Android/MainActivity.cs @@ -41,12 +41,20 @@ protected override void OnCreate(Bundle? savedInstanceState) { base.OnCreate(savedInstanceState); WindowCompat.SetDecorFitsSystemWindows(Window!, false); + WebViewSoftInputPatch.Initialize(); var insetsManager = IPlatformApplication.Current?.Services.GetRequiredService(); - ViewCompat.SetOnApplyWindowInsetsListener( - Window!.DecorView, - new WindowInsetsListener(insetsManager!, Resources!.DisplayMetrics!.Density) - ); + if (insetsManager is not null) + { + insetsManager.SystemSafeInsetBottom = + $"{WebViewSoftInputPatch.GetNavBarHeight() / Resources!.DisplayMetrics!.Density}px"; + insetsManager.SystemSafeInsetTop = + $"{WebViewSoftInputPatch.GetStatusBarHeight() / Resources.DisplayMetrics.Density}px"; + ViewCompat.SetOnApplyWindowInsetsListener( + Window!.DecorView, + new WindowInsetsListener(insetsManager!, Resources.DisplayMetrics.Density) + ); + } } private class WindowInsetsListener(InsetsManager insetsManager, float density) @@ -59,8 +67,18 @@ WindowInsetsCompat insets ) { // convert android px to css px + var top = insets.SystemWindowInsetTop / density; var bottom = insets.SystemWindowInsetBottom / density; + if (top == 0 || bottom == 0) + { + insetsManager.SystemSafeInsetBottom = + $"{WebViewSoftInputPatch.GetNavBarHeight() / density}px"; + insetsManager.SystemSafeInsetTop = + $"{WebViewSoftInputPatch.GetStatusBarHeight() / density}px"; + insetsManager.NotifyInsetsChanged(); + return insets; + } insetsManager.SystemSafeInsetTop = $"{top}px"; insetsManager.SystemSafeInsetBottom = $"{bottom}px"; insetsManager.NotifyInsetsChanged(); diff --git a/LiftLog.App/Platforms/Android/WebViewSoftInputPatch.cs b/LiftLog.App/Platforms/Android/WebViewSoftInputPatch.cs new file mode 100644 index 00000000..980ffb32 --- /dev/null +++ b/LiftLog.App/Platforms/Android/WebViewSoftInputPatch.cs @@ -0,0 +1,86 @@ +using System.Runtime.Versioning; +using Android.Content.Res; +using Android.Views; +using Android.Widget; +using static Android.Resource; +using Activity = Android.App.Activity; +using Rect = Android.Graphics.Rect; +using View = Android.Views.View; + +namespace LiftLog.App; + +[SupportedOSPlatform("Android")] +public static class WebViewSoftInputPatch +{ + static Activity Activity => + Microsoft.Maui.ApplicationModel.Platform.CurrentActivity + ?? throw new InvalidOperationException("Android Activity can't be null."); + static View MChildOfContent; + static FrameLayout.LayoutParams FrameLayoutParams; + static int UsableHeightPrevious = 0; + + public static void Initialize() + { + FrameLayout content = (FrameLayout)Activity.FindViewById(Id.Content); + MChildOfContent = content.GetChildAt(0); + MChildOfContent.ViewTreeObserver.GlobalLayout += (s, o) => PossiblyResizeChildOfContent(); + FrameLayoutParams = (FrameLayout.LayoutParams)MChildOfContent?.LayoutParameters; + } + + static void PossiblyResizeChildOfContent() + { + int usableHeightNow = ComputeUsableHeight(); + if (usableHeightNow != UsableHeightPrevious) + { + int usableHeightSansKeyboard = MChildOfContent.RootView.Height; + int heightDifference = usableHeightSansKeyboard - usableHeightNow; + if (heightDifference < 0) + { + usableHeightSansKeyboard = MChildOfContent.RootView.Width; + heightDifference = usableHeightSansKeyboard - usableHeightNow; + } + + if (heightDifference > usableHeightSansKeyboard / 4) + { + FrameLayoutParams.Height = usableHeightSansKeyboard - heightDifference; + } + else + { + FrameLayoutParams.Height = usableHeightNow; + } + } + + MChildOfContent.RequestLayout(); + UsableHeightPrevious = usableHeightNow; + } + + static int ComputeUsableHeight() + { + Rect rect = new Rect(); + MChildOfContent.GetWindowVisibleDisplayFrame(rect); + return rect.Bottom + GetNavBarHeight(); + } + + public static int GetNavBarHeight() + { + int result = 0; + Resources resources = Activity.Resources; + int resourceId = resources.GetIdentifier("navigation_bar_height", "dimen", "android"); + if (resourceId > 0) + { + result = resources.GetDimensionPixelSize(resourceId); + } + return result; + } + + public static int GetStatusBarHeight() + { + int result = 0; + int resourceId = Activity.Resources.GetIdentifier("status_bar_height", "dimen", "android"); + if (resourceId > 0) + { + result = Activity.Resources.GetDimensionPixelSize(resourceId); + } + return result; + } +}