55package io .flutter .embedding .android ;
66
77import android .view .KeyCharacterMap ;
8- import android .view .KeyEvent ;
9- import android .view .View ;
10- import androidx .annotation .NonNull ;
118import androidx .annotation .Nullable ;
12- import io .flutter .Log ;
13- import io .flutter .embedding .engine .systemchannels .KeyEventChannel ;
14- import io .flutter .plugin .editing .TextInputPlugin ;
15- import java .util .ArrayDeque ;
16- import java .util .Deque ;
17- import java .util .Iterator ;
189
1910/**
2011 * A class to process key events from Android, passing them to the framework as messages using
3021 * framework, and if the framework responds that it has not handled the event, then this class
3122 * synthesizes a new event to send to Android, without handling it this time.
3223 */
33- public class AndroidKeyProcessor {
34- private static final String TAG = "AndroidKeyProcessor" ;
35-
36- @ NonNull private final KeyEventChannel keyEventChannel ;
37- @ NonNull private final TextInputPlugin textInputPlugin ;
24+ class AndroidKeyProcessor {
3825 private int combiningCharacter ;
39- @ NonNull private EventResponder eventResponder ;
4026
4127 /**
4228 * Constructor for AndroidKeyProcessor.
@@ -58,74 +44,6 @@ public class AndroidKeyProcessor {
5844 * and if it has a valid input connection and is accepting text, then it will handle the event
5945 * and the framework will not receive it.
6046 */
61- public AndroidKeyProcessor (
62- @ NonNull View view ,
63- @ NonNull KeyEventChannel keyEventChannel ,
64- @ NonNull TextInputPlugin textInputPlugin ) {
65- this .keyEventChannel = keyEventChannel ;
66- this .textInputPlugin = textInputPlugin ;
67- textInputPlugin .setKeyEventProcessor (this );
68- this .eventResponder = new EventResponder (view , textInputPlugin );
69- this .keyEventChannel .setEventResponseHandler (eventResponder );
70- }
71-
72- /**
73- * Detaches the key processor from the Flutter engine.
74- *
75- * <p>The AndroidKeyProcessor instance should not be used after calling this.
76- */
77- public void destroy () {
78- keyEventChannel .setEventResponseHandler (null );
79- }
80-
81- /**
82- * Called when a key event is received by the {@link FlutterView} or the {@link
83- * InputConnectionAdaptor}.
84- *
85- * @param keyEvent the Android key event to respond to.
86- * @return true if the key event should not be propagated to other Android components. Delayed
87- * synthesis events will return false, so that other components may handle them.
88- */
89- public boolean onKeyEvent (@ NonNull KeyEvent keyEvent ) {
90- int action = keyEvent .getAction ();
91- if (action != KeyEvent .ACTION_DOWN && action != KeyEvent .ACTION_UP ) {
92- // There is theoretically a KeyEvent.ACTION_MULTIPLE, but theoretically
93- // that isn't sent by Android anymore, so this is just for protection in
94- // case the theory is wrong.
95- return false ;
96- }
97- if (isPendingEvent (keyEvent )) {
98- // If the keyEvent is in the queue of pending events we've seen, and has
99- // the same id, then we know that this is a re-dispatched keyEvent, and we
100- // shouldn't respond to it, but we should remove it from tracking now.
101- eventResponder .removePendingEvent (keyEvent );
102- return false ;
103- }
104-
105- Character complexCharacter = applyCombiningCharacterToBaseCharacter (keyEvent .getUnicodeChar ());
106- KeyEventChannel .FlutterKeyEvent flutterEvent =
107- new KeyEventChannel .FlutterKeyEvent (keyEvent , complexCharacter );
108-
109- eventResponder .addEvent (keyEvent );
110- if (action == KeyEvent .ACTION_DOWN ) {
111- keyEventChannel .keyDown (flutterEvent );
112- } else {
113- keyEventChannel .keyUp (flutterEvent );
114- }
115- return true ;
116- }
117-
118- /**
119- * Returns whether or not the given event is currently being processed by this key processor. This
120- * is used to determine if a new key event sent to the {@link InputConnectionAdaptor} originates
121- * from a hardware key event, or a soft keyboard editing event.
122- *
123- * @param event the event to check for being the current event.
124- * @return
125- */
126- public boolean isPendingEvent (@ NonNull KeyEvent event ) {
127- return eventResponder .findPendingEvent (event ) != null ;
128- }
12947
13048 /**
13149 * Applies the given Unicode character in {@code newCharacterCodePoint} to a previously entered
@@ -155,11 +73,7 @@ public boolean isPendingEvent(@NonNull KeyEvent event) {
15573 * https://en.wikipedia.org/wiki/Combining_character
15674 */
15775 @ Nullable
158- private Character applyCombiningCharacterToBaseCharacter (int newCharacterCodePoint ) {
159- if (newCharacterCodePoint == 0 ) {
160- return null ;
161- }
162-
76+ Character applyCombiningCharacterToBaseCharacter (int newCharacterCodePoint ) {
16377 char complexCharacter = (char ) newCharacterCodePoint ;
16478 boolean isNewCodePointACombiningCharacter =
16579 (newCharacterCodePoint & KeyCharacterMap .COMBINING_ACCENT ) != 0 ;
@@ -185,91 +99,4 @@ private Character applyCombiningCharacterToBaseCharacter(int newCharacterCodePoi
18599
186100 return complexCharacter ;
187101 }
188-
189- private static class EventResponder implements KeyEventChannel .EventResponseHandler {
190- // The maximum number of pending events that are held before starting to
191- // complain.
192- private static final long MAX_PENDING_EVENTS = 1000 ;
193- final Deque <KeyEvent > pendingEvents = new ArrayDeque <KeyEvent >();
194- @ NonNull private final View view ;
195- @ NonNull private final TextInputPlugin textInputPlugin ;
196-
197- public EventResponder (@ NonNull View view , @ NonNull TextInputPlugin textInputPlugin ) {
198- this .view = view ;
199- this .textInputPlugin = textInputPlugin ;
200- }
201-
202- /** Removes the first pending event from the cache of pending events. */
203- private void removePendingEvent (KeyEvent event ) {
204- pendingEvents .remove (event );
205- }
206-
207- private KeyEvent findPendingEvent (KeyEvent event ) {
208- Iterator <KeyEvent > iter = pendingEvents .iterator ();
209- while (iter .hasNext ()) {
210- KeyEvent item = iter .next ();
211- if (item == event ) {
212- return item ;
213- }
214- }
215- return null ;
216- }
217-
218- /**
219- * Called whenever the framework responds that a given key event was handled by the framework.
220- *
221- * @param event the event to be marked as being handled by the framework. Must not be null.
222- */
223- @ Override
224- public void onKeyEventHandled (KeyEvent event ) {
225- removePendingEvent (event );
226- }
227-
228- /**
229- * Called whenever the framework responds that a given key event wasn't handled by the
230- * framework.
231- *
232- * @param event the event to be marked as not being handled by the framework. Must not be null.
233- */
234- @ Override
235- public void onKeyEventNotHandled (KeyEvent event ) {
236- redispatchKeyEvent (findPendingEvent (event ));
237- }
238-
239- /** Adds an Android key event to the event responder to wait for a response. */
240- public void addEvent (@ NonNull KeyEvent event ) {
241- pendingEvents .addLast (event );
242- if (pendingEvents .size () > MAX_PENDING_EVENTS ) {
243- Log .e (
244- TAG ,
245- "There are "
246- + pendingEvents .size ()
247- + " keyboard events that have not yet received a response. Are responses being "
248- + "sent?" );
249- }
250- }
251-
252- /**
253- * Dispatches the event to the activity associated with the context.
254- *
255- * @param event the event to be dispatched to the activity.
256- */
257- private void redispatchKeyEvent (KeyEvent event ) {
258- // If the textInputPlugin is still valid and accepting text, then we'll try
259- // and send the key event to it, assuming that if the event can be sent,
260- // that it has been handled.
261- if (textInputPlugin .getInputMethodManager ().isAcceptingText ()
262- && textInputPlugin .getLastInputConnection () != null
263- && textInputPlugin .getLastInputConnection ().sendKeyEvent (event )) {
264- // The event was handled, so we can remove it from the queue.
265- removePendingEvent (event );
266- return ;
267- }
268-
269- // Since the framework didn't handle it, dispatch the event again.
270- if (view != null ) {
271- view .getRootView ().dispatchKeyEvent (event );
272- }
273- }
274- }
275102}
0 commit comments