1/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.widget.focus;
18
19import static com.google.testing.littlemock.LittleMock.inOrder;
20import static com.google.testing.littlemock.LittleMock.mock;
21
22import android.os.Handler;
23import android.test.ActivityInstrumentationTestCase2;
24import android.test.UiThreadTest;
25import android.test.suitebuilder.annotation.LargeTest;
26import android.test.suitebuilder.annotation.MediumTest;
27import android.util.AndroidRuntimeException;
28import android.view.View;
29import android.view.View.OnFocusChangeListener;
30import android.view.ViewTreeObserver.OnGlobalFocusChangeListener;
31import android.widget.Button;
32
33import com.android.frameworks.coretests.R;
34import com.google.testing.littlemock.LittleMock.InOrder;
35
36/**
37 * {@link RequestFocusTest} is set up to exercise cases where the views that
38 * have focus become invisible or GONE.
39 */
40public class RequestFocusTest extends ActivityInstrumentationTestCase2<RequestFocus> {
41
42    private Button mTopLeftButton;
43    private Button mBottomLeftButton;
44    private Button mTopRightButton;
45    private Button mBottomRightButton;
46    private Handler mHandler;
47
48    public RequestFocusTest() {
49        super(RequestFocus.class);
50    }
51
52    @Override
53    public void setUp() throws Exception {
54        super.setUp();
55
56        final RequestFocus a = getActivity();
57        mHandler = a.getHandler();
58        mTopLeftButton = (Button) a.findViewById(R.id.topLeftButton);
59        mBottomLeftButton = (Button) a.findViewById(R.id.bottomLeftButton);
60        mTopRightButton = (Button) a.findViewById(R.id.topRightButton);
61        mBottomRightButton = (Button) a.findViewById(R.id.bottomRightButton);
62    }
63
64    // Test that setUp did what we expect it to do.  These asserts
65    // can't go in SetUp, or the test will hang.
66    @MediumTest
67    public void testSetUpConditions() throws Exception {
68        assertNotNull(mHandler);
69        assertNotNull(mTopLeftButton);
70        assertNotNull(mTopRightButton);
71        assertNotNull(mBottomLeftButton);
72        assertNotNull(mBottomRightButton);
73        assertTrue("requestFocus() should work from onCreate.", mBottomRightButton.hasFocus());
74    }
75
76    // Test that a posted requestFocus works.
77    @LargeTest
78    public void testPostedRequestFocus() throws Exception {
79        mHandler.post(new Runnable() { public void run() {
80            mBottomLeftButton.requestFocus();
81        }});
82        synchronized(this) {
83            try {
84                wait(500);
85            } catch (InterruptedException e) {
86                // Don't care.
87            }
88        }
89        assertTrue("Focus should move to bottom left", mBottomLeftButton.hasFocus());
90    }
91
92    // Test that a requestFocus from the wrong thread fails.
93    @MediumTest
94    public void testWrongThreadRequestFocusFails() throws Exception {
95        try {
96            mTopRightButton.requestFocus();
97            fail("requestFocus from wrong thread should raise exception.");
98        } catch (AndroidRuntimeException e) {
99            // Expected.  The actual exception is not public, so we can't catch it.
100            assertEquals("android.view.ViewRootImpl$CalledFromWrongThreadException",
101                         e.getClass().getName());
102        }
103    }
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    }
190}
191