Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
namespace Maui.Controls.Sample.Issues
{
[Issue(IssueTracker.Github, 32000, "Entry crash when setting Text in TextChanged event handler on Android", PlatformAffected.Android)]
public class IssueEntryTextChangedCrash : TestContentPage
{
private const string entryId = "TestEntry";
private const string labelId = "TestLabel";

protected override void Init()
{
var label = new Label
{
Text = "Delete the text in the Entry below. The app should not crash.",
AutomationId = labelId
};

var entry = new Entry
{
Text = "0",
Keyboard = Keyboard.Numeric,
AutomationId = entryId
};

entry.TextChanged += OnEntryTextChanged;

Content = new VerticalStackLayout
{
Padding = 20,
Spacing = 10,
Children = { label, entry }
};
}

void OnEntryTextChanged(object sender, TextChangedEventArgs e)
{
if (string.IsNullOrEmpty(e.NewTextValue))
{
((Entry)sender).Text = "0";
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using NUnit.Framework;
using UITest.Appium;
using UITest.Core;

namespace Microsoft.Maui.TestCases.Tests.Issues
{
public class IssueEntryTextChangedCrash : _IssuesUITest
{
const string EntryId = "TestEntry";

public IssueEntryTextChangedCrash(TestDevice testDevice) : base(testDevice)
{
}

public override string Issue => "Entry crash when setting Text in TextChanged event handler on Android";

[Test]
[Category(UITestCategories.Entry)]
public void EntryTextChangedShouldNotCrash()
{
App.WaitForElement(EntryId);

// Clear the entry text - this should trigger TextChanged which sets text to "0"
// The fix ensures this doesn't crash due to EmojiCompat bounds check
App.ClearText(EntryId);

// Verify the entry contains "0" after TextChanged handling
var entryText = App.FindElement(EntryId).GetText();
Assert.That(entryText, Is.EqualTo("0"), "Entry should contain '0' after clearing");

// Test should complete without crashing
}
}
}
16 changes: 13 additions & 3 deletions src/Core/src/Platform/Android/EditTextExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,23 @@ public static void UpdateText(this EditText editText, IEntry entry)
// User Types => VirtualView Updated => Triggers Native Update
// Then it will cause the cursor to reset to position zero as the user typed
editText.Text = entry.Text;
editText.SetSelection(editText.Text?.Length ?? 0);

// Ensure the selection position doesn't exceed the actual text length
// to prevent crashes when text is modified by text watchers (e.g., EmojiCompat)
int selectionPosition = Math.Min(editText.Text?.Length ?? 0, editText.Length());
editText.SetSelection(selectionPosition);

// TODO ezhart The renderer sets the text to selected and shows the keyboard if the EditText is focused
}

public static void UpdateText(this EditText editText, IEditor editor)
{
editText.Text = editor.Text;
editText.SetSelection(editText.Text?.Length ?? 0);

// Ensure the selection position doesn't exceed the actual text length
// to prevent crashes when text is modified by text watchers (e.g., EmojiCompat)
int selectionPosition = Math.Min(editText.Text?.Length ?? 0, editText.Length());
editText.SetSelection(selectionPosition);
}

public static void UpdateTextColor(this EditText editText, ITextStyle entry)
Expand Down Expand Up @@ -358,7 +366,9 @@ internal static void SetInputType(this EditText editText, ITextInput textInput)

// If we implement the OnSelectionChanged method, this method is called after a keyboard layout change with SelectionStart = 0,
// Let's restore the cursor position to its previous location.
editText.SetSelection(previousCursorPosition);
// Ensure the selection position doesn't exceed the actual text length
int selectionPosition = Math.Min(previousCursorPosition, editText.Length());
editText.SetSelection(selectionPosition);
}

internal static bool IsCompletedAction(this EditorActionEventArgs e, ImeAction currentInputImeFlag)
Expand Down
Loading