1616
1717package android .widget .focus ;
1818
19- import android . widget . focus . RequestFocus ;
20- import com .android . frameworks . coretests . R ;
19+ import static com . google . testing . littlemock . LittleMock . inOrder ;
20+ import static com .google . testing . littlemock . LittleMock . mock ;
2121
2222import android .os .Handler ;
23- import android .test .ActivityInstrumentationTestCase ;
23+ import android .test .ActivityInstrumentationTestCase2 ;
24+ import android .test .UiThreadTest ;
2425import android .test .suitebuilder .annotation .LargeTest ;
2526import android .test .suitebuilder .annotation .MediumTest ;
26- import android .widget .Button ;
2727import android .util .AndroidRuntimeException ;
28+ import android .view .View ;
29+ import android .view .View .OnFocusChangeListener ;
30+ import android .view .ViewTreeObserver .OnGlobalFocusChangeListener ;
31+ import android .widget .Button ;
32+
33+ import com .android .frameworks .coretests .R ;
34+ import com .google .testing .littlemock .LittleMock .InOrder ;
2835
2936/**
3037 * {@link RequestFocusTest} is set up to exercise cases where the views that
3138 * have focus become invisible or GONE.
3239 */
33- public class RequestFocusTest extends ActivityInstrumentationTestCase <RequestFocus > {
40+ public class RequestFocusTest extends ActivityInstrumentationTestCase2 <RequestFocus > {
3441
3542 private Button mTopLeftButton ;
3643 private Button mBottomLeftButton ;
@@ -39,7 +46,7 @@ public class RequestFocusTest extends ActivityInstrumentationTestCase<RequestFoc
3946 private Handler mHandler ;
4047
4148 public RequestFocusTest () {
42- super ("com.android.frameworks.coretests" , RequestFocus .class );
49+ super (RequestFocus .class );
4350 }
4451
4552 @ Override
@@ -94,4 +101,90 @@ public void testWrongThreadRequestFocusFails() throws Exception {
94101 e .getClass ().getName ());
95102 }
96103 }
104+
105+ /**
106+ * This tests checks the case in which the first focusable View clears focus.
107+ * In such a case the framework tries to give the focus to another View starting
108+ * from the top. Hence, the framework will try to give focus to the view that
109+ * wants to clear its focus.
110+ *
111+ * @throws Exception If an error occurs.
112+ */
113+ @ UiThreadTest
114+ public void testOnFocusChangeCallbackOrderWhenClearingFocusOfFirstFocusable ()
115+ throws Exception {
116+ // Get the first focusable.
117+ Button clearingFocusButton = mTopLeftButton ;
118+ Button gainingFocusButton = mTopLeftButton ;
119+
120+ // Make sure that the clearing focus View is the first focusable.
121+ View focusCandidate = clearingFocusButton .getRootView ().getParent ().focusSearch (null ,
122+ View .FOCUS_FORWARD );
123+ assertSame ("The clearing focus button is the first focusable." ,
124+ clearingFocusButton , focusCandidate );
125+ assertSame ("The gaining focus button is the first focusable." ,
126+ gainingFocusButton , focusCandidate );
127+
128+ // Focus the clearing focus button.
129+ clearingFocusButton .requestFocus ();
130+ assertTrue (clearingFocusButton .hasFocus ());
131+
132+ // Register the invocation order checker.
133+ CombinedListeners mock = mock (CombinedListeners .class );
134+ clearingFocusButton .setOnFocusChangeListener (mock );
135+ gainingFocusButton .setOnFocusChangeListener (mock );
136+ clearingFocusButton .getViewTreeObserver ().addOnGlobalFocusChangeListener (mock );
137+
138+ // Try to clear focus.
139+ clearingFocusButton .clearFocus ();
140+
141+ // Check that no callback was invoked since focus did not move.
142+ InOrder inOrder = inOrder (mock );
143+ inOrder .verify (mock ).onFocusChange (clearingFocusButton , false );
144+ inOrder .verify (mock ).onGlobalFocusChanged (clearingFocusButton , gainingFocusButton );
145+ inOrder .verify (mock ).onFocusChange (gainingFocusButton , true );
146+ }
147+
148+ public interface CombinedListeners extends OnFocusChangeListener , OnGlobalFocusChangeListener {}
149+
150+ /**
151+ * This tests check whether the on focus change callbacks are invoked in
152+ * the proper order when a View loses focus and the framework gives it to
153+ * the fist focusable one.
154+ *
155+ * @throws Exception
156+ */
157+ @ UiThreadTest
158+ public void testOnFocusChangeCallbackOrderWhenClearingFocusOfNotFirstFocusable ()
159+ throws Exception {
160+ Button clearingFocusButton = mTopRightButton ;
161+ Button gainingFocusButton = mTopLeftButton ;
162+
163+ // Make sure that the clearing focus View is not the first focusable.
164+ View focusCandidate = clearingFocusButton .getRootView ().getParent ().focusSearch (null ,
165+ View .FOCUS_FORWARD );
166+ assertNotSame ("The clearing focus button is not the first focusable." ,
167+ clearingFocusButton , focusCandidate );
168+ assertSame ("The gaining focus button is the first focusable." ,
169+ gainingFocusButton , focusCandidate );
170+
171+ // Focus the clearing focus button.
172+ clearingFocusButton .requestFocus ();
173+ assertTrue (clearingFocusButton .hasFocus ());
174+
175+ // Register the invocation order checker.
176+ CombinedListeners mock = mock (CombinedListeners .class );
177+ clearingFocusButton .setOnFocusChangeListener (mock );
178+ gainingFocusButton .setOnFocusChangeListener (mock );
179+ clearingFocusButton .getViewTreeObserver ().addOnGlobalFocusChangeListener (mock );
180+
181+ // Try to clear focus.
182+ clearingFocusButton .clearFocus ();
183+
184+ // Check that no callback was invoked since focus did not move.
185+ InOrder inOrder = inOrder (mock );
186+ inOrder .verify (mock ).onFocusChange (clearingFocusButton , false );
187+ inOrder .verify (mock ).onGlobalFocusChanged (clearingFocusButton , gainingFocusButton );
188+ inOrder .verify (mock ).onFocusChange (gainingFocusButton , true );
189+ }
97190}
0 commit comments