AndroidInputMethod.cs 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. using System;
  2. using Android.Content;
  3. using Android.Runtime;
  4. using Android.Text;
  5. using Android.Views;
  6. using Android.Views.InputMethods;
  7. using Avalonia.Android.Platform.SkiaPlatform;
  8. using Avalonia.Input.TextInput;
  9. namespace Avalonia.Android
  10. {
  11. internal interface IAndroidInputMethod
  12. {
  13. public View View { get; }
  14. public TextInputMethodClient Client { get; }
  15. public bool IsActive { get; }
  16. public InputMethodManager IMM { get; }
  17. }
  18. enum CustomImeFlags
  19. {
  20. ActionNone = 0x00000001,
  21. ActionGo = 0x00000002,
  22. ActionSearch = 0x00000003,
  23. ActionSend = 0x00000004,
  24. ActionNext = 0x00000005,
  25. ActionDone = 0x00000006,
  26. ActionPrevious = 0x00000007,
  27. }
  28. internal class AndroidInputMethod<TView> : ITextInputMethodImpl, IAndroidInputMethod
  29. where TView : View, IInitEditorInfo
  30. {
  31. private readonly TView _host;
  32. private readonly InputMethodManager _imm;
  33. private TextInputMethodClient _client;
  34. private AvaloniaInputConnection _inputConnection;
  35. public AndroidInputMethod(TView host)
  36. {
  37. if (host.OnCheckIsTextEditor() == false)
  38. throw new InvalidOperationException("Host should return true from OnCheckIsTextEditor()");
  39. _host = host;
  40. _imm = host.Context.GetSystemService(Context.InputMethodService).JavaCast<InputMethodManager>();
  41. _host.Focusable = true;
  42. _host.FocusableInTouchMode = true;
  43. _host.ViewTreeObserver.AddOnGlobalLayoutListener(new SoftKeyboardListener(_host));
  44. }
  45. public View View => _host;
  46. public bool IsActive => Client != null;
  47. public TextInputMethodClient Client => _client;
  48. public InputMethodManager IMM => _imm;
  49. public void Reset()
  50. {
  51. }
  52. public void SetClient(TextInputMethodClient client)
  53. {
  54. _client = client;
  55. if (IsActive)
  56. {
  57. _host.RequestFocus();
  58. _imm.RestartInput(View);
  59. _imm.ShowSoftInput(_host, ShowFlags.Implicit);
  60. var selection = Client.Selection;
  61. _imm.UpdateSelection(_host, selection.Start, selection.End, selection.Start, selection.End);
  62. var surroundingText = _client.SurroundingText ?? "";
  63. var extractedText = new ExtractedText
  64. {
  65. Text = new Java.Lang.String(surroundingText),
  66. SelectionStart = selection.Start,
  67. SelectionEnd = selection.End,
  68. PartialEndOffset = surroundingText.Length
  69. };
  70. _imm.UpdateExtractedText(_host, _inputConnection?.ExtractedTextToken ?? 0, extractedText);
  71. _client.SurroundingTextChanged += _client_SurroundingTextChanged;
  72. _client.SelectionChanged += _client_SelectionChanged;
  73. }
  74. else
  75. {
  76. _imm.HideSoftInputFromWindow(_host.WindowToken, HideSoftInputFlags.ImplicitOnly);
  77. }
  78. }
  79. private void _client_SelectionChanged(object sender, EventArgs e)
  80. {
  81. var selection = Client.Selection;
  82. _imm.UpdateSelection(_host, selection.Start, selection.End, selection.Start, selection.End);
  83. }
  84. private void _client_SurroundingTextChanged(object sender, EventArgs e)
  85. {
  86. var surroundingText = _client.SurroundingText ?? "";
  87. _inputConnection.EditableWrapper.IgnoreChange = true;
  88. _inputConnection.Editable.Replace(0, _inputConnection.Editable.Length(), surroundingText);
  89. _inputConnection.EditableWrapper.IgnoreChange = false;
  90. var selection = Client.Selection;
  91. _imm.UpdateSelection(_host, selection.Start, selection.End, selection.Start, selection.End);
  92. //Debug.WriteLine($"SurroundingText: {surroundingText}, CaretIndex: {selection.Start}");
  93. }
  94. public void SetCursorRect(Rect rect)
  95. {
  96. }
  97. public void SetOptions(TextInputOptions options)
  98. {
  99. _host.InitEditorInfo((topLevel, outAttrs) =>
  100. {
  101. if (_client == null)
  102. return null;
  103. _inputConnection = new AvaloniaInputConnection(topLevel, this);
  104. outAttrs.InputType = options.ContentType switch
  105. {
  106. TextInputContentType.Email => global::Android.Text.InputTypes.TextVariationEmailAddress,
  107. TextInputContentType.Number => global::Android.Text.InputTypes.ClassNumber,
  108. TextInputContentType.Password => global::Android.Text.InputTypes.TextVariationPassword,
  109. TextInputContentType.Digits => global::Android.Text.InputTypes.ClassPhone,
  110. TextInputContentType.Url => global::Android.Text.InputTypes.TextVariationUri,
  111. _ => global::Android.Text.InputTypes.ClassText
  112. };
  113. if (options.AutoCapitalization)
  114. {
  115. outAttrs.InitialCapsMode = global::Android.Text.CapitalizationMode.Sentences;
  116. outAttrs.InputType |= global::Android.Text.InputTypes.TextFlagCapSentences;
  117. }
  118. if (options.Multiline)
  119. outAttrs.InputType |= global::Android.Text.InputTypes.TextFlagMultiLine;
  120. outAttrs.ImeOptions = options.ReturnKeyType switch
  121. {
  122. TextInputReturnKeyType.Return => ImeFlags.NoEnterAction,
  123. TextInputReturnKeyType.Go => (ImeFlags)CustomImeFlags.ActionGo,
  124. TextInputReturnKeyType.Send => (ImeFlags)CustomImeFlags.ActionSend,
  125. TextInputReturnKeyType.Search => (ImeFlags)CustomImeFlags.ActionSearch,
  126. TextInputReturnKeyType.Next => (ImeFlags)CustomImeFlags.ActionNext,
  127. TextInputReturnKeyType.Previous => (ImeFlags)CustomImeFlags.ActionPrevious,
  128. TextInputReturnKeyType.Done => (ImeFlags)CustomImeFlags.ActionDone,
  129. _ => options.Multiline ? ImeFlags.NoEnterAction : (ImeFlags)CustomImeFlags.ActionDone
  130. };
  131. outAttrs.ImeOptions |= ImeFlags.NoFullscreen | ImeFlags.NoExtractUi;
  132. return _inputConnection;
  133. });
  134. }
  135. }
  136. }