Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
Public Member Functions | Package Functions | List of all members
io.flutter.embedding.android.KeyEmbedderResponder Class Reference
Inheritance diagram for io.flutter.embedding.android.KeyEmbedderResponder:
io.flutter.embedding.android.KeyboardManager.Responder

Public Member Functions

 KeyEmbedderResponder (BinaryMessenger messenger)
 
void handleEvent ( @NonNull KeyEvent event, @NonNull OnKeyEventHandledCallback onKeyEventHandledCallback)
 
Map< Long, Long > getPressedState ()
 

Package Functions

void updatePressingState (@NonNull Long physicalKey, @Nullable Long logicalKey)
 
void synchronizePressingKey (PressingGoal goal, boolean truePressed, long eventLogicalKey, long eventPhysicalKey, KeyEvent event, ArrayList< Runnable > postSynchronize)
 
void synchronizeTogglingKey (TogglingGoal goal, boolean trueEnabled, long eventLogicalKey, KeyEvent event)
 

Detailed Description

A KeyboardManager.Responder of KeyboardManager that handles events by sending processed information in KeyData.

This class corresponds to the HardwareKeyboard API in the framework.

Definition at line 26 of file KeyEmbedderResponder.java.

Constructor & Destructor Documentation

◆ KeyEmbedderResponder()

io.flutter.embedding.android.KeyEmbedderResponder.KeyEmbedderResponder ( BinaryMessenger  messenger)
inline

Definition at line 59 of file KeyEmbedderResponder.java.

59 {
60 this.messenger = messenger;
61 for (final TogglingGoal goal : KeyboardMap.getTogglingGoals()) {
62 togglingGoals.put(goal.logicalKey, goal);
63 }
64 }

Member Function Documentation

◆ getPressedState()

Map< Long, Long > io.flutter.embedding.android.KeyEmbedderResponder.getPressedState ( )
inline

Returns an unmodifiable view of the pressed state.

Returns
A map whose keys are physical keyboard key IDs and values are the corresponding logical keyboard key IDs.

Definition at line 445 of file KeyEmbedderResponder.java.

445 {
446 return Collections.unmodifiableMap(pressingRecords);
447 }

◆ handleEvent()

void io.flutter.embedding.android.KeyEmbedderResponder.handleEvent ( @NonNull KeyEvent  event,
@NonNull OnKeyEventHandledCallback  onKeyEventHandledCallback 
)
inline

Parses an Android key event, performs synchronization, and dispatches Flutter events through the messenger to the framework with the given callback.

At least one event will be dispatched. If there are no others, an empty event with 0 physical key and 0 logical key will be synthesized.

Parameters
eventThe Android key event to be handled.
onKeyEventHandledCallbackthe method to call when the framework has decided whether to handle this event. This callback will always be called once and only once. If there are no non-synthesized out of this event, this callback will be called during this method with true.

Implements io.flutter.embedding.android.KeyboardManager.Responder.

Definition at line 430 of file KeyEmbedderResponder.java.

431 {
432 final boolean sentAny = handleEventImpl(event, onKeyEventHandledCallback);
433 if (!sentAny) {
434 synthesizeEvent(true, 0L, 0L, 0L);
435 onKeyEventHandledCallback.onKeyEventHandled(true);
436 }
437 }
FlKeyEvent * event

◆ synchronizePressingKey()

void io.flutter.embedding.android.KeyEmbedderResponder.synchronizePressingKey ( PressingGoal  goal,
boolean  truePressed,
long  eventLogicalKey,
long  eventPhysicalKey,
KeyEvent  event,
ArrayList< Runnable >  postSynchronize 
)
inlinepackage

Definition at line 136 of file KeyEmbedderResponder.java.

142 {
143 // During an incoming event, there might be a synthesized Flutter event for each key of each
144 // pressing goal, followed by an eventual main Flutter event.
145 //
146 // NowState ----------------> PreEventState --------------> -------------->TrueState
147 // PreSynchronize Event PostSynchronize
148 //
149 // The goal of the synchronization algorithm is to derive a pre-event state that can satisfy the
150 // true state (`truePressed`) after the event, and that requires as few synthesized events based
151 // on the current state (`nowStates`) as possible.
152 final boolean[] nowStates = new boolean[goal.keys.length];
153 final Boolean[] preEventStates = new Boolean[goal.keys.length];
154 boolean postEventAnyPressed = false;
155 // 1. Find the current states of all keys.
156 // 2. Derive the pre-event state of the event key (if applicable.)
157 for (int keyIdx = 0; keyIdx < goal.keys.length; keyIdx += 1) {
158 final KeyboardMap.KeyPair key = goal.keys[keyIdx];
159 nowStates[keyIdx] = pressingRecords.containsKey(key.physicalKey);
160 if (key.logicalKey == eventLogicalKey) {
161 switch (getEventType(event)) {
162 case kDown:
163 preEventStates[keyIdx] = false;
164 postEventAnyPressed = true;
165 if (!truePressed) {
166 postSynchronize.add(
167 () ->
168 synthesizeEvent(
169 false, key.logicalKey, eventPhysicalKey, event.getEventTime()));
170 }
171 break;
172 case kUp:
173 // Incoming event is an up. Although the previous state should be pressed, don't
174 // synthesize a down event even if it's not. The later code will handle such cases by
175 // skipping abrupt up events. Obviously don't synthesize up events either.
176 preEventStates[keyIdx] = nowStates[keyIdx];
177 break;
178 case kRepeat:
179 // Incoming event is repeat. The previous state can be either pressed or released. Don't
180 // synthesize a down event here, or there will be a down event *and* a repeat event,
181 // both of which have printable characters. Obviously don't synthesize up events either.
182 if (!truePressed) {
183 postSynchronize.add(
184 () ->
185 synthesizeEvent(
186 false, key.logicalKey, key.physicalKey, event.getEventTime()));
187 }
188 preEventStates[keyIdx] = nowStates[keyIdx];
189 postEventAnyPressed = true;
190 break;
191 }
192 } else {
193 postEventAnyPressed = postEventAnyPressed || nowStates[keyIdx];
194 }
195 }
196
197 // Fill the rest of the pre-event states to match the true state.
198 if (truePressed) {
199 // It is required that at least one key is pressed.
200 for (int keyIdx = 0; keyIdx < goal.keys.length; keyIdx += 1) {
201 if (preEventStates[keyIdx] != null) {
202 continue;
203 }
204 if (postEventAnyPressed) {
205 preEventStates[keyIdx] = nowStates[keyIdx];
206 } else {
207 preEventStates[keyIdx] = true;
208 postEventAnyPressed = true;
209 }
210 }
211 if (!postEventAnyPressed) {
212 preEventStates[0] = true;
213 }
214 } else {
215 for (int keyIdx = 0; keyIdx < goal.keys.length; keyIdx += 1) {
216 if (preEventStates[keyIdx] != null) {
217 continue;
218 }
219 preEventStates[keyIdx] = false;
220 }
221 }
222
223 // Dispatch synthesized events for state differences.
224 for (int keyIdx = 0; keyIdx < goal.keys.length; keyIdx += 1) {
225 if (nowStates[keyIdx] != preEventStates[keyIdx]) {
226 final KeyboardMap.KeyPair key = goal.keys[keyIdx];
227 synthesizeEvent(
228 preEventStates[keyIdx], key.logicalKey, key.physicalKey, event.getEventTime());
229 }
230 }
231 }
@ kUp
Definition embedder.h:971
@ kDown
Definition embedder.h:978

◆ synchronizeTogglingKey()

void io.flutter.embedding.android.KeyEmbedderResponder.synchronizeTogglingKey ( TogglingGoal  goal,
boolean  trueEnabled,
long  eventLogicalKey,
KeyEvent  event 
)
inlinepackage

Definition at line 246 of file KeyEmbedderResponder.java.

247 {
248 if (goal.logicalKey == eventLogicalKey) {
249 // Don't synthesize for self events, because the self events have weird metaStates on
250 // ChromeOS.
251 return;
252 }
253 if (goal.enabled != trueEnabled) {
254 final boolean firstIsDown = !pressingRecords.containsKey(goal.physicalKey);
255 if (firstIsDown) {
256 goal.enabled = !goal.enabled;
257 }
258 synthesizeEvent(firstIsDown, goal.logicalKey, goal.physicalKey, event.getEventTime());
259 if (!firstIsDown) {
260 goal.enabled = !goal.enabled;
261 }
262 synthesizeEvent(!firstIsDown, goal.logicalKey, goal.physicalKey, event.getEventTime());
263 }
264 }

◆ updatePressingState()

void io.flutter.embedding.android.KeyEmbedderResponder.updatePressingState ( @NonNull Long  physicalKey,
@Nullable Long  logicalKey 
)
inlinepackage

Definition at line 107 of file KeyEmbedderResponder.java.

107 {
108 if (logicalKey != null) {
109 final Long previousValue = pressingRecords.put(physicalKey, logicalKey);
110 if (previousValue != null) {
111 throw new AssertionError("The key was not empty");
112 }
113 } else {
114 final Long previousValue = pressingRecords.remove(physicalKey);
115 if (previousValue == null) {
116 throw new AssertionError("The key was empty");
117 }
118 }
119 }

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