Skip to content

Commit

Permalink
Set window soft input via window mapper (#11356)
Browse files Browse the repository at this point in the history
* Set window soft input via window mapper

* - disconnect handlers

* - add additional logging info

* - fix handler typing

* - fix formatting
  • Loading branch information
PureWeen authored Nov 15, 2022
1 parent 0dfcf8a commit 6115146
Show file tree
Hide file tree
Showing 9 changed files with 237 additions and 26 deletions.
2 changes: 2 additions & 0 deletions src/Controls/src/Core/HandlerImpl/Application/Application.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ public partial class Application
new PropertyMapper<Application, ApplicationHandler>(ApplicationHandler.Mapper)
{
#if ANDROID
// There is also a mapper on Window for this property since this property is relevant at the window level for
// Android not the application level
[PlatformConfiguration.AndroidSpecific.Application.WindowSoftInputModeAdjustProperty.PropertyName] = MapWindowSoftInputModeAdjust,
#endif
};
Expand Down
16 changes: 16 additions & 0 deletions src/Controls/src/Core/HandlerImpl/Window/Window.Android.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using Android.App;
using Android.Views;
using AndroidX.AppCompat.App;
using Microsoft.Maui.Controls.Platform;
using Microsoft.Maui.Handlers;

namespace Microsoft.Maui.Controls
Expand All @@ -20,5 +21,20 @@ public static void MapContent(WindowHandler handler, IWindow view)
public static void MapContent(IWindowHandler handler, IWindow view)
{
}

internal static void MapWindowSoftInputModeAdjust(IWindowHandler handler, IWindow view)
{
if (view.Parent is Application app)
{
var setting = PlatformConfiguration.AndroidSpecific.Application.GetWindowSoftInputModeAdjust(app);
view.UpdateWindowSoftInputModeAdjust(setting.ToPlatform());
}
}

private protected override void OnParentChangedCore()
{
base.OnParentChangedCore();
Handler?.UpdateValue(PlatformConfiguration.AndroidSpecific.Application.WindowSoftInputModeAdjustProperty.PropertyName);
}
}
}
15 changes: 13 additions & 2 deletions src/Controls/src/Core/HandlerImpl/Window/Window.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,22 @@ namespace Microsoft.Maui.Controls
{
public partial class Window
{
public static IPropertyMapper<IWindow, WindowHandler> ControlsWindowMapper = new PropertyMapper<IWindow, WindowHandler>(WindowHandler.Mapper);
public static IPropertyMapper<IWindow, WindowHandler> ControlsWindowMapper =
new PropertyMapper<IWindow, WindowHandler>(WindowHandler.Mapper);

// ControlsWindowMapper incorrectly typed to WindowHandler
static IPropertyMapper<IWindow, IWindowHandler> Mapper =
new PropertyMapper<IWindow, IWindowHandler>(ControlsWindowMapper)
{
#if ANDROID
// This property is also on the Application Mapper since that's where the attached property exists
[PlatformConfiguration.AndroidSpecific.Application.WindowSoftInputModeAdjustProperty.PropertyName] = MapWindowSoftInputModeAdjust,
#endif
};

internal static void RemapForControls()
{
WindowHandler.Mapper = ControlsWindowMapper;
WindowHandler.Mapper = Mapper;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Android.App;
using Android.Content;
using Android.Media;
using Android.Views;
using Microsoft.Maui.Controls.PlatformConfiguration.AndroidSpecific;
using AApplication = Android.App.Application;
Expand All @@ -10,32 +11,24 @@ public static class ApplicationExtensions
{
public static void UpdateWindowSoftInputModeAdjust(this AApplication platformView, Application application)
{
var adjust = SoftInput.AdjustPan;

if (Application.Current != null)
if (application is IApplication app)
{
WindowSoftInputModeAdjust elementValue = Application.Current.OnThisPlatform().GetWindowSoftInputModeAdjust();

switch (elementValue)
{
case WindowSoftInputModeAdjust.Resize:
adjust = SoftInput.AdjustResize;
break;
case WindowSoftInputModeAdjust.Unspecified:
adjust = SoftInput.AdjustUnspecified;
break;
default:
adjust = SoftInput.AdjustPan;
break;
}
foreach (var window in app.Windows)
window?.Handler?.UpdateValue(PlatformConfiguration.AndroidSpecific.Application.WindowSoftInputModeAdjustProperty.PropertyName);
}
}

IMauiContext mauiContext = application.FindMauiContext(true);
Context context = mauiContext?.Context;
Activity activity = context.GetActivity();

activity?.Window?.SetSoftInputMode(adjust);

internal static SoftInput ToPlatform(this WindowSoftInputModeAdjust windowSoftInputModeAdjust)
{
switch (windowSoftInputModeAdjust)
{
case WindowSoftInputModeAdjust.Resize:
return SoftInput.AdjustResize;
case WindowSoftInputModeAdjust.Unspecified:
return SoftInput.AdjustUnspecified;
default:
return SoftInput.AdjustPan;
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Maui.Controls;
using Microsoft.Maui.Controls.Platform;
using Microsoft.Maui.Handlers;
using Microsoft.Maui.Hosting;
using Microsoft.Maui.Platform;
using System.Collections.Generic;
using System.Threading.Tasks;
using Xunit;
using ASoftInput = Android.Views.SoftInput;
using AApplication = Android.App.Application;
using AActivity = Android.App.Activity;

namespace Microsoft.Maui.DeviceTests
{
[Category(TestCategory.Application)]
public partial class ApplicationTests : ControlsHandlerTestBase
{
[Category(TestCategory.Application)]
public class SoftInputModeTests : ControlsHandlerTestBase
{
[Fact]
public async Task SoftInputModeDefaultsToAdjustPan()
{
await InvokeOnMainThreadAsync(() =>
{
Assert.Equal(ASoftInput.AdjustPan, GetSoftInput());
});
}

[Fact]
public async Task SoftInputModeSetOnApplicationPropagatesToWindowHandlers()
{
EnsureHandlerCreated((builder) =>
{
builder.Services.AddSingleton<IApplication>((_) => new SoftInputModeApplication());
builder.ConfigureMauiHandlers(handler =>
{
handler.AddHandler<SoftInputModeWindow, SoftInputWindowHandlerStub>();
handler.AddHandler<SoftInputModeApplication, SoftInputApplicationHandlerStub>();
});
});

await InvokeOnMainThreadAsync(() =>
{
var handlers = new List<IElementHandler>();

try
{
// Setup application stub
var app = MauiContext.Services.GetService<IApplication>() as SoftInputModeApplication;
app.Handler = app.ToHandler(MauiContext);

handlers.Add(app.Handler);

// Setup window
var windowHandler = (SoftInputWindowHandlerStub)app.Window.ToHandler(MauiContext);
app.Window.Handler = windowHandler;

handlers.Insert(0, app.Window.Handler);

// Validate that the Soft Input initializes to AdjustPan
Assert.Equal(ASoftInput.AdjustPan, windowHandler.LastASoftInputSet);

// Set to Resize
Controls.PlatformConfiguration.AndroidSpecific.Application.SetWindowSoftInputModeAdjust(
app,
Controls.PlatformConfiguration.AndroidSpecific.WindowSoftInputModeAdjust.Resize);

// Validate the mapper on the window handler is called with correct value
Assert.Equal(ASoftInput.AdjustResize, windowHandler.LastASoftInputSet);

// Set to Pan
Controls.PlatformConfiguration.AndroidSpecific.Application.SetWindowSoftInputModeAdjust(
app,
Controls.PlatformConfiguration.AndroidSpecific.WindowSoftInputModeAdjust.Pan);

// Validate the mapper on the window handler is called with correct value
Assert.Equal(ASoftInput.AdjustPan, windowHandler.LastASoftInputSet);
}
finally
{
foreach (var handler in handlers)
{
handler.DisconnectHandler();
}
}
});
}

ASoftInput GetSoftInput() =>
GetSoftInput(MauiContext.Context.GetActivity());

ASoftInput GetSoftInput(AActivity aActivity) =>
aActivity.Window.Attributes.SoftInputMode;

class SoftInputApplicationHandlerStub : ApplicationHandler
{
public SoftInputApplicationHandlerStub() : base(Application.ControlsApplicationMapper)
{
}

protected override AApplication CreatePlatformElement()
{
return new AApplication();
}
}

class SoftInputModeApplication : Application, IApplication
{
public SoftInputModeWindow Window { get; } = new SoftInputModeWindow();

public SoftInputModeApplication() : base(false)
{
Window.Parent = this;
}

IReadOnlyList<IWindow> IApplication.Windows
{
get
{
return new List<IWindow>() { Window };
}
}
}

class SoftInputModeWindow : Window
{

}

class SoftInputWindowHandlerStub : ElementHandler<IWindow, AActivity>, IWindowHandler
{
public ASoftInput LastASoftInputSet { get; private set; } = ASoftInput.AdjustUnspecified;

public static IPropertyMapper<IWindow, SoftInputWindowHandlerStub> StubMapper =
new PropertyMapper<IWindow, SoftInputWindowHandlerStub>()
{
[Controls.PlatformConfiguration.AndroidSpecific.Application.WindowSoftInputModeAdjustProperty.PropertyName] = MapWindowSoftInputModeAdjust
};

public static void MapWindowSoftInputModeAdjust(SoftInputWindowHandlerStub arg1, IWindow arg2)
{
if (arg2.Parent is Application app)
{
var setting = Controls.PlatformConfiguration.AndroidSpecific.Application.GetWindowSoftInputModeAdjust(app);
arg1.LastASoftInputSet = setting.ToPlatform();
}
}

public SoftInputWindowHandlerStub() : base(StubMapper, null)
{

}

protected override AActivity CreatePlatformElement()
{
return new AActivity();
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xunit;

namespace Microsoft.Maui.DeviceTests
{
public partial class ApplicationTests : ControlsHandlerTestBase
{
}
}
2 changes: 1 addition & 1 deletion src/Controls/tests/DeviceTests/MauiProgram.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public static class MauiProgram
public static Microsoft.UI.Xaml.Window CurrentWindow => MauiProgramDefaults.DefaultWindow;
#endif

public static IApplication DefaultTestApp { get; private set; }
public static IApplication DefaultTestApp => MauiProgramDefaults.DefaultTestApp;

public static MauiApp CreateMauiApp() =>
MauiProgramDefaults.CreateMauiApp(new List<Assembly>()
Expand Down
1 change: 1 addition & 0 deletions src/Controls/tests/DeviceTests/TestCategory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
{
public static class TestCategory
{
public const string Application = "Application";
public const string Behavior = "Behavior";
public const string Button = "Button";
public const string CheckBox = "CheckBox";
Expand Down
12 changes: 12 additions & 0 deletions src/Core/src/Platform/Android/WindowExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using Android.App;
using Android.Content;
using Android.Content.Res;
using Android.Views;
using Microsoft.Maui.Devices;
using Microsoft.Maui.Platform;

Expand All @@ -20,5 +22,15 @@ internal static DisplayOrientation GetOrientation(this IWindow? window)
_ => DisplayOrientation.Unknown
};
}

internal static void UpdateWindowSoftInputModeAdjust(this IWindow platformView, SoftInput inputMode)
{
var activity = platformView?.Handler?.PlatformView as Activity ??
platformView?.Handler?.MauiContext?.GetPlatformWindow();

activity?
.Window?
.SetSoftInputMode(inputMode);
}
}
}

0 comments on commit 6115146

Please sign in to comment.