Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
Classes | Public Member Functions | Package Functions | List of all members
io.flutter.plugin.editing.TextInputPlugin Class Reference
Inheritance diagram for io.flutter.plugin.editing.TextInputPlugin:
io.flutter.plugin.editing.ListenableEditingState.EditingStateWatcher

Public Member Functions

 TextInputPlugin ( @NonNull View view, @NonNull TextInputChannel textInputChannel, @NonNull PlatformViewsController platformViewsController)
 
InputMethodManager getInputMethodManager ()
 

Package Functions

Editable getEditable ()
 
ImeSyncDeferringInsetsCallback getImeSyncCallback ()
 
void lockPlatformViewInputConnection ()
 
void unlockPlatformViewInputConnection ()
 
void destroy ()
 
InputConnection createInputConnection ( @NonNull View view, @NonNull KeyboardManager keyboardManager, @NonNull EditorInfo outAttrs)
 
InputConnection getLastInputConnection ()
 
void clearPlatformViewClient (int platformViewId)
 
void sendTextInputAppPrivateCommand (@NonNull String action, @NonNull Bundle data)
 
boolean handleKeyEvent (@NonNull KeyEvent keyEvent)
 
void didChangeEditingState (boolean textChanged, boolean selectionChanged, boolean composingRegionChanged)
 
void onProvideAutofillVirtualStructure (@NonNull ViewStructure structure, int flags)
 
void autofill (@NonNull SparseArray< AutofillValue > values)
 
void showTextInput (View view)
 
void setTextInputClient (int client, TextInputChannel.Configuration configuration)
 
void setTextInputEditingState (View view, TextInputChannel.TextEditState state)
 
void clearTextInputClient ()
 

Detailed Description

Android implementation of the text input plugin.

Definition at line 39 of file TextInputPlugin.java.

Constructor & Destructor Documentation

◆ TextInputPlugin()

io.flutter.plugin.editing.TextInputPlugin.TextInputPlugin ( @NonNull View  view,
@NonNull TextInputChannel  textInputChannel,
@NonNull PlatformViewsController  platformViewsController 
)
inline

Definition at line 66 of file TextInputPlugin.java.

69 {
70 mView = view;
71 // Create a default object.
72 mEditable = new ListenableEditingState(null, mView);
73 mImm = (InputMethodManager) view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
74 if (Build.VERSION.SDK_INT >= API_LEVELS.API_26) {
75 afm = view.getContext().getSystemService(AutofillManager.class);
76 } else {
77 afm = null;
78 }
79
80 // Sets up syncing ime insets with the framework, allowing
81 // the Flutter view to grow and shrink to accommodate Android
82 // controlled keyboard animations.
83 if (Build.VERSION.SDK_INT >= API_LEVELS.API_30) {
84 imeSyncCallback = new ImeSyncDeferringInsetsCallback(view);
85 imeSyncCallback.install();
86 }
87
88 this.textInputChannel = textInputChannel;
89 textInputChannel.setTextInputMethodHandler(
90 new TextInputChannel.TextInputMethodHandler() {
91 @Override
92 public void show() {
93 showTextInput(mView);
94 }
95
96 @Override
97 public void hide() {
98 if (inputTarget.type == InputTarget.Type.PHYSICAL_DISPLAY_PLATFORM_VIEW) {
99 notifyViewExited();
100 } else {
101 hideTextInput(mView);
102 }
103 }
104
105 @Override
106 public void requestAutofill() {
107 notifyViewEntered();
108 }
109
110 @Override
111 public void finishAutofillContext(boolean shouldSave) {
112 if (Build.VERSION.SDK_INT < API_LEVELS.API_26 || afm == null) {
113 return;
114 }
115 if (shouldSave) {
116 afm.commit();
117 } else {
118 afm.cancel();
119 }
120 }
121
122 @Override
123 public void setClient(
124 int textInputClientId, TextInputChannel.Configuration configuration) {
125 setTextInputClient(textInputClientId, configuration);
126 }
127
128 @Override
129 public void setPlatformViewClient(int platformViewId, boolean usesVirtualDisplay) {
130 setPlatformViewTextInputClient(platformViewId, usesVirtualDisplay);
131 }
132
133 @Override
134 public void setEditingState(TextInputChannel.TextEditState editingState) {
135 setTextInputEditingState(mView, editingState);
136 }
137
138 @Override
139 public void setEditableSizeAndTransform(double width, double height, double[] transform) {
140 saveEditableSizeAndTransform(width, height, transform);
141 }
142
143 @Override
144 public void clearClient() {
146 }
147
148 @Override
149 public void sendAppPrivateCommand(String action, Bundle data) {
151 }
152 });
153
154 textInputChannel.requestExistingInputState();
155
156 this.platformViewsController = platformViewsController;
157 this.platformViewsController.attachTextInputPlugin(this);
158 }
void sendTextInputAppPrivateCommand(@NonNull String action, @NonNull Bundle data)
void setTextInputClient(int client, TextInputChannel.Configuration configuration)
void setTextInputEditingState(View view, TextInputChannel.TextEditState state)
static FlMethodResponse * hide(FlTextInputPlugin *self)
Build(configs, env, options)
Definition build.py:232
static SkColor4f transform(SkColor4f c, SkColorSpace *src, SkColorSpace *dst)
Definition p3.cpp:47
int32_t height
int32_t width

Member Function Documentation

◆ autofill()

void io.flutter.plugin.editing.TextInputPlugin.autofill ( @NonNull SparseArray< AutofillValue >  values)
inline

Definition at line 793 of file TextInputPlugin.java.

793 {
794 if (Build.VERSION.SDK_INT < API_LEVELS.API_26) {
795 return;
796 }
797
798 if (configuration == null || autofillConfiguration == null || configuration.autofill == null) {
799 return;
800 }
801
802 final TextInputChannel.Configuration.Autofill currentAutofill = configuration.autofill;
803 final HashMap<String, TextInputChannel.TextEditState> editingValues = new HashMap<>();
804 for (int i = 0; i < values.size(); i++) {
805 int virtualId = values.keyAt(i);
806
807 final TextInputChannel.Configuration config = autofillConfiguration.get(virtualId);
808 if (config == null || config.autofill == null) {
809 continue;
810 }
811
812 final TextInputChannel.Configuration.Autofill autofill = config.autofill;
813 final String value = values.valueAt(i).getTextValue().toString();
814 final TextInputChannel.TextEditState newState =
815 new TextInputChannel.TextEditState(value, value.length(), value.length(), -1, -1);
816
817 if (autofill.uniqueIdentifier.equals(currentAutofill.uniqueIdentifier)) {
818 // Autofilling the current client is the same as handling user input
819 // from the virtual keyboard. Setting the editable to newState and an
820 // update will be sent to the framework.
821 mEditable.setEditingState(newState);
822 } else {
823 editingValues.put(autofill.uniqueIdentifier, newState);
824 }
825 }
826 textInputChannel.updateEditingStateWithTag(inputTarget.id, editingValues);
827 }
void setEditingState(TextInputChannel.TextEditState newState)
void autofill(@NonNull SparseArray< AutofillValue > values)
uint8_t value

◆ clearPlatformViewClient()

void io.flutter.plugin.editing.TextInputPlugin.clearPlatformViewClient ( int  platformViewId)
inline

Clears a platform view text input client if it is the current input target.

This is called when a platform view is disposed to make sure we're not hanging to a stale input connection.

Definition at line 365 of file TextInputPlugin.java.

365 {
366 if ((inputTarget.type == InputTarget.Type.VIRTUAL_DISPLAY_PLATFORM_VIEW
367 || inputTarget.type == InputTarget.Type.PHYSICAL_DISPLAY_PLATFORM_VIEW)
368 && inputTarget.id == platformViewId) {
369 inputTarget = new InputTarget(InputTarget.Type.NO_TARGET, 0);
370 notifyViewExited();
371 mImm.hideSoftInputFromWindow(mView.getApplicationWindowToken(), 0);
372 mImm.restartInput(mView);
373 mRestartInputPending = false;
374 }
375 }

◆ clearTextInputClient()

void io.flutter.plugin.editing.TextInputPlugin.clearTextInputClient ( )
inlinepackage

Definition at line 529 of file TextInputPlugin.java.

529 {
530 if (inputTarget.type == InputTarget.Type.VIRTUAL_DISPLAY_PLATFORM_VIEW) {
531 // This only applies to platform views that use a virtual display.
532 // Focus changes in the framework tree have no guarantees on the order focus nodes are
533 // notified. A node that lost focus may be notified before or after a node that gained focus.
534 // When moving the focus from a Flutter text field to an AndroidView, it is possible that the
535 // Flutter text field's focus node will be notified that it lost focus after the AndroidView
536 // was notified that it gained focus. When this happens the text field will send a
537 // clearTextInput command which we ignore.
538 // By doing this we prevent the framework from clearing a platform view input client (the only
539 // way to do so is to set a new framework text client). I don't see an obvious use case for
540 // "clearing" a platform view's text input client, and it may be error prone as we don't know
541 // how the platform view manages the input connection and we probably shouldn't interfere.
542 // If we ever want to allow the framework to clear a platform view text client we should
543 // probably consider changing the focus manager such that focus nodes that lost focus are
544 // notified before focus nodes that gained focus as part of the same focus event.
545 return;
546 }
547 mEditable.removeEditingStateListener(this);
548 notifyViewExited();
549 configuration = null;
550 updateAutofillConfigurationIfNeeded(null);
551 inputTarget = new InputTarget(InputTarget.Type.NO_TARGET, 0);
553 lastClientRect = null;
554
555 // Call restartInput to reset IME internal states. Otherwise some IMEs (Gboard for instance)
556 // keep reacting based on the previous input configuration until a new configuration is set.
557 mImm.restartInput(mView);
558 }
void removeEditingStateListener(EditingStateWatcher listener)

◆ createInputConnection()

InputConnection io.flutter.plugin.editing.TextInputPlugin.createInputConnection ( @NonNull View  view,
@NonNull KeyboardManager  keyboardManager,
@NonNull EditorInfo  outAttrs 
)
inline

Definition at line 285 of file TextInputPlugin.java.

286 {
287 if (inputTarget.type == InputTarget.Type.NO_TARGET) {
288 lastInputConnection = null;
289 return null;
290 }
291
292 if (inputTarget.type == InputTarget.Type.PHYSICAL_DISPLAY_PLATFORM_VIEW) {
293 return null;
294 }
295
296 if (inputTarget.type == InputTarget.Type.VIRTUAL_DISPLAY_PLATFORM_VIEW) {
297 if (isInputConnectionLocked) {
298 return lastInputConnection;
299 }
300 lastInputConnection =
301 platformViewsController
302 .getPlatformViewById(inputTarget.id)
303 .onCreateInputConnection(outAttrs);
304 return lastInputConnection;
305 }
306
307 outAttrs.inputType =
308 inputTypeFromTextInputType(
309 configuration.inputType,
310 configuration.obscureText,
311 configuration.autocorrect,
312 configuration.enableSuggestions,
313 configuration.enableIMEPersonalizedLearning,
314 configuration.textCapitalization);
315 outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_FULLSCREEN;
316
317 if (Build.VERSION.SDK_INT >= API_LEVELS.API_26
318 && !configuration.enableIMEPersonalizedLearning) {
319 outAttrs.imeOptions |= EditorInfo.IME_FLAG_NO_PERSONALIZED_LEARNING;
320 }
321
322 int enterAction;
323 if (configuration.inputAction == null) {
324 // If an explicit input action isn't set, then default to none for multi-line fields
325 // and done for single line fields.
326 enterAction =
327 (InputType.TYPE_TEXT_FLAG_MULTI_LINE & outAttrs.inputType) != 0
328 ? EditorInfo.IME_ACTION_NONE
329 : EditorInfo.IME_ACTION_DONE;
330 } else {
331 enterAction = configuration.inputAction;
332 }
333 if (configuration.actionLabel != null) {
334 outAttrs.actionLabel = configuration.actionLabel;
335 outAttrs.actionId = enterAction;
336 }
337 outAttrs.imeOptions |= enterAction;
338
339 if (configuration.contentCommitMimeTypes != null) {
340 String[] imgTypeString = configuration.contentCommitMimeTypes;
341 EditorInfoCompat.setContentMimeTypes(outAttrs, imgTypeString);
342 }
343
344 InputConnectionAdaptor connection =
345 new InputConnectionAdaptor(
346 view, inputTarget.id, textInputChannel, keyboardManager, mEditable, outAttrs);
347 outAttrs.initialSelStart = mEditable.getSelectionStart();
348 outAttrs.initialSelEnd = mEditable.getSelectionEnd();
349
350 lastInputConnection = connection;
351 return lastInputConnection;
352 }

◆ destroy()

void io.flutter.plugin.editing.TextInputPlugin.destroy ( )
inline

Detaches the text input plugin from the platform views controller.

The TextInputPlugin instance should not be used after calling this.

Definition at line 211 of file TextInputPlugin.java.

211 {
212 platformViewsController.detachTextInputPlugin();
213 textInputChannel.setTextInputMethodHandler(null);
214 notifyViewExited();
215 mEditable.removeEditingStateListener(this);
216 if (imeSyncCallback != null) {
217 imeSyncCallback.remove();
218 }
219 }

◆ didChangeEditingState()

void io.flutter.plugin.editing.TextInputPlugin.didChangeEditingState ( boolean  textChanged,
boolean  selectionChanged,
boolean  composingRegionChanged 
)
inline

Implements io.flutter.plugin.editing.ListenableEditingState.EditingStateWatcher.

Definition at line 607 of file TextInputPlugin.java.

608 {
609 if (textChanged) {
610 // Notify the autofill manager of the value change.
611 notifyValueChanged(mEditable.toString());
612 }
613
614 final int selectionStart = mEditable.getSelectionStart();
615 final int selectionEnd = mEditable.getSelectionEnd();
616 final int composingStart = mEditable.getComposingStart();
617 final int composingEnd = mEditable.getComposingEnd();
618
619 final ArrayList<TextEditingDelta> batchTextEditingDeltas =
621 final boolean skipFrameworkUpdate =
622 // The framework needs to send its editing state first.
623 mLastKnownFrameworkTextEditingState == null
624 || (mEditable.toString().equals(mLastKnownFrameworkTextEditingState.text)
625 && selectionStart == mLastKnownFrameworkTextEditingState.selectionStart
626 && selectionEnd == mLastKnownFrameworkTextEditingState.selectionEnd
627 && composingStart == mLastKnownFrameworkTextEditingState.composingStart
628 && composingEnd == mLastKnownFrameworkTextEditingState.composingEnd);
629 if (!skipFrameworkUpdate) {
630 Log.v(TAG, "send EditingState to flutter: " + mEditable.toString());
631
632 if (configuration.enableDeltaModel) {
633 textInputChannel.updateEditingStateWithDeltas(inputTarget.id, batchTextEditingDeltas);
634 mEditable.clearBatchDeltas();
635 } else {
636 textInputChannel.updateEditingState(
637 inputTarget.id,
638 mEditable.toString(),
639 selectionStart,
640 selectionEnd,
641 composingStart,
642 composingEnd);
643 }
644 mLastKnownFrameworkTextEditingState =
645 new TextEditState(
646 mEditable.toString(), selectionStart, selectionEnd, composingStart, composingEnd);
647 } else {
648 // Don't accumulate deltas if they are not sent to the framework.
649 mEditable.clearBatchDeltas();
650 }
651 }
ArrayList< TextEditingDelta > extractBatchTextEditingDeltas()
void Log(const char *format,...) SK_PRINTF_LIKE(1

◆ getEditable()

Editable io.flutter.plugin.editing.TextInputPlugin.getEditable ( )
inlinepackage

Definition at line 166 of file TextInputPlugin.java.

166 {
167 return mEditable;
168 }

◆ getImeSyncCallback()

ImeSyncDeferringInsetsCallback io.flutter.plugin.editing.TextInputPlugin.getImeSyncCallback ( )
inlinepackage

Definition at line 171 of file TextInputPlugin.java.

171 {
172 return imeSyncCallback;
173 }

◆ getInputMethodManager()

InputMethodManager io.flutter.plugin.editing.TextInputPlugin.getInputMethodManager ( )
inline

Definition at line 161 of file TextInputPlugin.java.

161 {
162 return mImm;
163 }

◆ getLastInputConnection()

InputConnection io.flutter.plugin.editing.TextInputPlugin.getLastInputConnection ( )
inline

Definition at line 355 of file TextInputPlugin.java.

355 {
356 return lastInputConnection;
357 }

◆ handleKeyEvent()

boolean io.flutter.plugin.editing.TextInputPlugin.handleKeyEvent ( @NonNull KeyEvent  keyEvent)
inline

Definition at line 588 of file TextInputPlugin.java.

588 {
589 if (!getInputMethodManager().isAcceptingText() || lastInputConnection == null) {
590 return false;
591 }
592
593 // Send the KeyEvent as an IME KeyEvent. If the input connection is an
594 // InputConnectionAdaptor then call its handleKeyEvent method (because
595 // this method will be called by the keyboard manager, and
596 // InputConnectionAdaptor#sendKeyEvent forwards the key event back to the
597 // keyboard manager).
598 return (lastInputConnection instanceof InputConnectionAdaptor)
599 ? ((InputConnectionAdaptor) lastInputConnection).handleKeyEvent(keyEvent)
600 : lastInputConnection.sendKeyEvent(keyEvent);
601 }

◆ lockPlatformViewInputConnection()

void io.flutter.plugin.editing.TextInputPlugin.lockPlatformViewInputConnection ( )
inline

Use the current platform view input connection until unlockPlatformViewInputConnection is called.

The current input connection instance is cached and any following call tolink createInputConnection} returns the cached connection until unlockPlatformViewInputConnection is called.

This is a no-op if the current input target isn't a platform view.

This is used to preserve an input connection when moving a platform view from one virtual display to another.

Definition at line 188 of file TextInputPlugin.java.

188 {
189 if (inputTarget.type == InputTarget.Type.VIRTUAL_DISPLAY_PLATFORM_VIEW) {
190 isInputConnectionLocked = true;
191 }
192 }

◆ onProvideAutofillVirtualStructure()

void io.flutter.plugin.editing.TextInputPlugin.onProvideAutofillVirtualStructure ( @NonNull ViewStructure  structure,
int  flags 
)
inline

Definition at line 745 of file TextInputPlugin.java.

745 {
746 if (Build.VERSION.SDK_INT < API_LEVELS.API_26 || !needsAutofill()) {
747 return;
748 }
749
750 final String triggerIdentifier = configuration.autofill.uniqueIdentifier;
751 final AutofillId parentId = structure.getAutofillId();
752 for (int i = 0; i < autofillConfiguration.size(); i++) {
753 final int autofillId = autofillConfiguration.keyAt(i);
754 final TextInputChannel.Configuration config = autofillConfiguration.valueAt(i);
755 final TextInputChannel.Configuration.Autofill autofill = config.autofill;
756 if (autofill == null) {
757 continue;
758 }
759
760 structure.addChildCount(1);
761 final ViewStructure child = structure.newChild(i);
762 child.setAutofillId(parentId, autofillId);
763 // Don't set hints when there's none.
764 // See https://github.com/flutter/flutter/issues/98505.
765 if (autofill.hints.length > 0) {
766 child.setAutofillHints(autofill.hints);
767 }
768 child.setAutofillType(View.AUTOFILL_TYPE_TEXT);
769 child.setVisibility(View.VISIBLE);
770 if (autofill.hintText != null) {
771 child.setHint(autofill.hintText);
772 }
773
774 // For some autofill services, only visible input fields are eligible for autofill.
775 // Reports the real size of the child if it's the current client, or 1x1 if we don't
776 // know the real dimensions of the child.
777 if (triggerIdentifier.hashCode() == autofillId && lastClientRect != null) {
778 child.setDimens(
779 lastClientRect.left,
780 lastClientRect.top,
781 0,
782 0,
783 lastClientRect.width(),
784 lastClientRect.height());
785 child.setAutofillValue(AutofillValue.forText(mEditable));
786 } else {
787 child.setDimens(0, 0, 0, 0, 1, 1);
788 child.setAutofillValue(AutofillValue.forText(autofill.editState.text));
789 }
790 }
791 }

◆ sendTextInputAppPrivateCommand()

void io.flutter.plugin.editing.TextInputPlugin.sendTextInputAppPrivateCommand ( @NonNull String  action,
@NonNull Bundle  data 
)
inline

Definition at line 377 of file TextInputPlugin.java.

377 {
378 mImm.sendAppPrivateCommand(mView, action, data);
379 }

◆ setTextInputClient()

void io.flutter.plugin.editing.TextInputPlugin.setTextInputClient ( int  client,
TextInputChannel.Configuration  configuration 
)
inlinepackage

Definition at line 405 of file TextInputPlugin.java.

405 {
406 // Call notifyViewExited on the previous field.
407 notifyViewExited();
408 this.configuration = configuration;
409 inputTarget = new InputTarget(InputTarget.Type.FRAMEWORK_CLIENT, client);
410
411 mEditable.removeEditingStateListener(this);
412 mEditable =
413 new ListenableEditingState(
414 configuration.autofill != null ? configuration.autofill.editState : null, mView);
415 updateAutofillConfigurationIfNeeded(configuration);
416
417 // setTextInputClient will be followed by a call to setTextInputEditingState.
418 // Do a restartInput at that time.
419 mRestartInputPending = true;
421 lastClientRect = null;
422 mEditable.addEditingStateListener(this);
423 }
void addEditingStateListener(EditingStateWatcher listener)

◆ setTextInputEditingState()

void io.flutter.plugin.editing.TextInputPlugin.setTextInputEditingState ( View  view,
TextInputChannel.TextEditState  state 
)
inlinepackage

Definition at line 460 of file TextInputPlugin.java.

460 {
461 if (!mRestartInputPending
462 && mLastKnownFrameworkTextEditingState != null
463 && mLastKnownFrameworkTextEditingState.hasComposing()) {
464 // Also restart input if the framework (or the developer) decides to
465 // change the composing region by itself (which is discouraged). Many IMEs
466 // don't expect editors to commit composing text, so a restart is needed
467 // to reset their internal states.
468 mRestartInputPending = composingChanged(mLastKnownFrameworkTextEditingState, state);
469 if (mRestartInputPending) {
470 Log.i(TAG, "Composing region changed by the framework. Restarting the input method.");
471 }
472 }
473
474 mLastKnownFrameworkTextEditingState = state;
475 mEditable.setEditingState(state);
476
477 // Restart if needed. Restarting will also update the selection.
478 if (mRestartInputPending) {
479 mImm.restartInput(view);
480 mRestartInputPending = false;
481 }
482 }
AtkStateType state

◆ showTextInput()

void io.flutter.plugin.editing.TextInputPlugin.showTextInput ( View  view)
inlinepackage

Definition at line 382 of file TextInputPlugin.java.

382 {
383 if (configuration == null
384 || configuration.inputType == null
385 || configuration.inputType.type != TextInputChannel.TextInputType.NONE) {
386 view.requestFocus();
387 mImm.showSoftInput(view, 0);
388 } else {
389 hideTextInput(view);
390 }
391 }

◆ unlockPlatformViewInputConnection()

void io.flutter.plugin.editing.TextInputPlugin.unlockPlatformViewInputConnection ( )
inline

Unlocks the input connection.

See also:link lockPlatformViewInputConnection}.

Definition at line 199 of file TextInputPlugin.java.

199 {
200 if (inputTarget.type == InputTarget.Type.VIRTUAL_DISPLAY_PLATFORM_VIEW) {
201 isInputConnectionLocked = false;
202 }
203 }

The documentation for this class was generated from the following file: