11package io .flutter .embedding .android ;
22
3- import static junit .framework .Assert .assertFalse ;
4- import static junit .framework .Assert .assertNotNull ;
53import static junit .framework .TestCase .assertEquals ;
64import static org .mockito .Mockito .any ;
75import static org .mockito .Mockito .mock ;
8- import static org .mockito .Mockito .spy ;
96import static org .mockito .Mockito .times ;
107import static org .mockito .Mockito .verify ;
118import static org .mockito .Mockito .when ;
129
1310import android .annotation .TargetApi ;
14- import android .app .Application ;
15- import android .content .Context ;
1611import android .view .KeyEvent ;
12+ import android .view .View ;
1713import androidx .annotation .NonNull ;
1814import io .flutter .embedding .engine .FlutterEngine ;
1915import io .flutter .embedding .engine .FlutterJNI ;
2016import io .flutter .embedding .engine .systemchannels .KeyEventChannel ;
2117import io .flutter .embedding .engine .systemchannels .TextInputChannel ;
2218import io .flutter .plugin .editing .TextInputPlugin ;
2319import io .flutter .util .FakeKeyEvent ;
24- import java .util .Map ;
2520import org .junit .Before ;
2621import org .junit .Test ;
2722import org .junit .runner .RunWith ;
23+ import org .mockito .ArgumentCaptor ;
2824import org .mockito .Mock ;
2925import org .mockito .MockitoAnnotations ;
26+ import org .mockito .invocation .InvocationOnMock ;
27+ import org .mockito .stubbing .Answer ;
3028import org .robolectric .RobolectricTestRunner ;
31- import org .robolectric .RuntimeEnvironment ;
3229import org .robolectric .annotation .Config ;
3330
3431@ Config (manifest = Config .NONE )
@@ -44,73 +41,97 @@ public void setUp() {
4441 }
4542
4643 @ Test
47- public void sendsKeyDownEventsToEventResponder () {
44+ public void respondsTrueWhenHandlingNewEvents () {
4845 FlutterEngine flutterEngine = mockFlutterEngine ();
4946 KeyEventChannel fakeKeyEventChannel = flutterEngine .getKeyEventChannel ();
50- TextInputPlugin fakeTextInputPlugin = mock (TextInputPlugin .class );
47+ View fakeView = mock (View .class );
5148
5249 AndroidKeyProcessor processor =
53- new AndroidKeyProcessor (
54- RuntimeEnvironment .application , fakeKeyEventChannel , fakeTextInputPlugin );
50+ new AndroidKeyProcessor (fakeView , fakeKeyEventChannel , mock (TextInputPlugin .class ));
5551
56- processor .onKeyDown (new FakeKeyEvent (KeyEvent .ACTION_DOWN , 65 ));
57- assertFalse ( processor . eventResponder . dispatchingKeyEvent );
52+ boolean result = processor .onKeyDown (new FakeKeyEvent (KeyEvent .ACTION_DOWN , 65 ));
53+ assertEquals ( true , result );
5854 verify (fakeKeyEventChannel , times (1 )).keyDown (any (KeyEventChannel .FlutterKeyEvent .class ));
5955 verify (fakeKeyEventChannel , times (0 )).keyUp (any (KeyEventChannel .FlutterKeyEvent .class ));
60- assertEquals (1 , processor .eventResponder .pendingEvents .size ());
61- Map .Entry <Long , KeyEvent > firstPendingEvent =
62- processor .eventResponder .pendingEvents .peekFirst ();
63- assertNotNull (firstPendingEvent );
64- processor .eventResponder .onKeyEventHandled (firstPendingEvent .getKey ());
65- assertEquals (0 , processor .eventResponder .pendingEvents .size ());
56+ verify (fakeView , times (0 )).dispatchKeyEvent (any (KeyEvent .class ));
6657 }
6758
68- @ Test
69- public void unhandledKeyEventsAreSynthesized () {
59+ public void synthesizesEventsWhenKeyDownNotHandled () {
7060 FlutterEngine flutterEngine = mockFlutterEngine ();
7161 KeyEventChannel fakeKeyEventChannel = flutterEngine .getKeyEventChannel ();
72- TextInputPlugin fakeTextInputPlugin = mock (TextInputPlugin .class );
73- Application spiedApplication = spy (RuntimeEnvironment .application );
74- Context spiedContext = spy (spiedApplication .getBaseContext ());
75- when (spiedApplication .getBaseContext ()).thenReturn (spiedContext );
76-
62+ View fakeView = mock (View .class );
63+ ArgumentCaptor <KeyEventChannel .EventResponseHandler > handlerCaptor =
64+ ArgumentCaptor .forClass (KeyEventChannel .EventResponseHandler .class );
65+ verify (fakeKeyEventChannel ).setEventResponseHandler (handlerCaptor .capture ());
7766 AndroidKeyProcessor processor =
78- new AndroidKeyProcessor (spiedApplication , fakeKeyEventChannel , fakeTextInputPlugin );
79- AndroidKeyProcessor .EventResponder eventResponder =
80- spy (new AndroidKeyProcessor .EventResponder (spiedContext ));
81- processor .setEventResponder (eventResponder );
82-
83- KeyEvent event = new FakeKeyEvent (KeyEvent .ACTION_DOWN , 65 );
84- processor .onKeyDown (event );
85- assertFalse (processor .eventResponder .dispatchingKeyEvent );
86- assertEquals (1 , processor .eventResponder .pendingEvents .size ());
87- Map .Entry <Long , KeyEvent > firstPendingEvent =
88- processor .eventResponder .pendingEvents .peekFirst ();
89- assertNotNull (firstPendingEvent );
90- processor .eventResponder .onKeyEventNotHandled (firstPendingEvent .getKey ());
91- assertEquals (0 , processor .eventResponder .pendingEvents .size ());
92- verify (eventResponder ).dispatchKeyEvent (event );
67+ new AndroidKeyProcessor (fakeView , fakeKeyEventChannel , mock (TextInputPlugin .class ));
68+ ArgumentCaptor <KeyEventChannel .FlutterKeyEvent > eventCaptor =
69+ ArgumentCaptor .forClass (KeyEventChannel .FlutterKeyEvent .class );
70+ FakeKeyEvent fakeKeyEvent = new FakeKeyEvent (KeyEvent .ACTION_DOWN , 65 );
71+
72+ boolean result = processor .onKeyDown (fakeKeyEvent );
73+ assertEquals (true , result );
74+
75+ // Capture the FlutterKeyEvent so we can find out its event ID to use when
76+ // faking our response.
77+ verify (fakeKeyEventChannel , times (1 )).keyDown (eventCaptor .capture ());
78+ boolean [] dispatchResult = {true };
79+ when (fakeView .dispatchKeyEvent (any (KeyEvent .class )))
80+ .then (
81+ new Answer <Boolean >() {
82+ @ Override
83+ public Boolean answer (InvocationOnMock invocation ) throws Throwable {
84+ KeyEvent event = (KeyEvent ) invocation .getArguments ()[0 ];
85+ assertEquals (fakeKeyEvent , event );
86+ dispatchResult [0 ] = processor .onKeyDown (event );
87+ return dispatchResult [0 ];
88+ }
89+ });
90+
91+ // Fake a response from the framework.
92+ handlerCaptor .getValue ().onKeyEventNotHandled (eventCaptor .getValue ().eventId );
93+ verify (fakeView , times (1 )).dispatchKeyEvent (fakeKeyEvent );
94+ assertEquals (false , dispatchResult [0 ]);
95+ verify (fakeKeyEventChannel , times (0 )).keyUp (any (KeyEventChannel .FlutterKeyEvent .class ));
9396 }
9497
95- @ Test
96- public void sendsKeyUpEventsToEventResponder () {
98+ public void synthesizesEventsWhenKeyUpNotHandled () {
9799 FlutterEngine flutterEngine = mockFlutterEngine ();
98100 KeyEventChannel fakeKeyEventChannel = flutterEngine .getKeyEventChannel ();
99- TextInputPlugin fakeTextInputPlugin = mock (TextInputPlugin .class );
100-
101+ View fakeView = mock (View .class );
102+ ArgumentCaptor <KeyEventChannel .EventResponseHandler > handlerCaptor =
103+ ArgumentCaptor .forClass (KeyEventChannel .EventResponseHandler .class );
104+ verify (fakeKeyEventChannel ).setEventResponseHandler (handlerCaptor .capture ());
101105 AndroidKeyProcessor processor =
102- new AndroidKeyProcessor (
103- RuntimeEnvironment .application , fakeKeyEventChannel , fakeTextInputPlugin );
104-
105- processor .onKeyUp (new FakeKeyEvent (KeyEvent .ACTION_UP , 65 ));
106- assertFalse (processor .eventResponder .dispatchingKeyEvent );
107- verify (fakeKeyEventChannel , times (0 )).keyDown (any (KeyEventChannel .FlutterKeyEvent .class ));
108- verify (fakeKeyEventChannel , times (1 )).keyUp (any (KeyEventChannel .FlutterKeyEvent .class ));
109- Map .Entry <Long , KeyEvent > firstPendingEvent =
110- processor .eventResponder .pendingEvents .peekFirst ();
111- assertNotNull (firstPendingEvent );
112- processor .eventResponder .onKeyEventHandled (firstPendingEvent .getKey ());
113- assertEquals (0 , processor .eventResponder .pendingEvents .size ());
106+ new AndroidKeyProcessor (fakeView , fakeKeyEventChannel , mock (TextInputPlugin .class ));
107+ ArgumentCaptor <KeyEventChannel .FlutterKeyEvent > eventCaptor =
108+ ArgumentCaptor .forClass (KeyEventChannel .FlutterKeyEvent .class );
109+ FakeKeyEvent fakeKeyEvent = new FakeKeyEvent (KeyEvent .ACTION_UP , 65 );
110+
111+ boolean result = processor .onKeyUp (fakeKeyEvent );
112+ assertEquals (true , result );
113+
114+ // Capture the FlutterKeyEvent so we can find out its event ID to use when
115+ // faking our response.
116+ verify (fakeKeyEventChannel , times (1 )).keyUp (eventCaptor .capture ());
117+ boolean [] dispatchResult = {true };
118+ when (fakeView .dispatchKeyEvent (any (KeyEvent .class )))
119+ .then (
120+ new Answer <Boolean >() {
121+ @ Override
122+ public Boolean answer (InvocationOnMock invocation ) throws Throwable {
123+ KeyEvent event = (KeyEvent ) invocation .getArguments ()[0 ];
124+ assertEquals (fakeKeyEvent , event );
125+ dispatchResult [0 ] = processor .onKeyUp (event );
126+ return dispatchResult [0 ];
127+ }
128+ });
129+
130+ // Fake a response from the framework.
131+ handlerCaptor .getValue ().onKeyEventNotHandled (eventCaptor .getValue ().eventId );
132+ verify (fakeView , times (1 )).dispatchKeyEvent (fakeKeyEvent );
133+ assertEquals (false , dispatchResult [0 ]);
134+ verify (fakeKeyEventChannel , times (0 )).keyUp (any (KeyEventChannel .FlutterKeyEvent .class ));
114135 }
115136
116137 @ NonNull
0 commit comments