Merge "Revert "Incorrect behavior of View clear focus.""
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index ba1b7af..87104f4 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -3766,14 +3766,6 @@
         }
 
         if ((mPrivateFlags & FOCUSED) != 0) {
-            // If this is the first focusable do not clear focus since the we
-            // try to give it focus every time a view clears its focus. Hence,
-            // the view that would gain focus already has it.
-            View firstFocusable = getFirstFocusable();
-            if (firstFocusable == this) {
-                return;
-            }
-
             mPrivateFlags &= ~FOCUSED;
 
             if (mParent != null) {
@@ -3782,24 +3774,9 @@
 
             onFocusChanged(false, 0, null);
             refreshDrawableState();
-
-            // The view cleared focus and invoked the callbacks, so  now is the
-            // time to give focus to the the first focusable to ensure that the
-            // gain focus is announced after clear focus.
-            if (firstFocusable != null) {
-                firstFocusable.requestFocus(FOCUS_FORWARD);
-            }
         }
     }
 
-    private View getFirstFocusable() {
-        ViewRootImpl viewRoot = getViewRootImpl();
-        if (viewRoot != null) {
-            return viewRoot.focusSearch(null, FOCUS_FORWARD);
-        }
-        return null;
-    }
-
     /**
      * Called to clear the focus of a view that is about to be removed.
      * Doesn't call clearChildFocus, which prevents this view from taking
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index a2e85a3..bc147ac 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -675,14 +675,11 @@
      */
     @Override
     public void clearFocus() {
-        if (DBG) {
-            System.out.println(this + " clearFocus()");
-        }
-        if (mFocused == null) {
-            super.clearFocus();
-        } else {
+        super.clearFocus();
+
+        // clear any child focus if it exists
+        if (mFocused != null) {
             mFocused.clearFocus();
-            mFocused = null;
         }
     }
 
@@ -694,12 +691,12 @@
         if (DBG) {
             System.out.println(this + " unFocus()");
         }
-        if (mFocused == null) {
-            super.unFocus();
-        } else {
+
+        super.unFocus();
+        if (mFocused != null) {
             mFocused.unFocus();
-            mFocused = null;
         }
+        mFocused = null;
     }
 
     /**
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 1930a5e..1c3bbfa 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -174,7 +174,6 @@
     View mView;
     View mFocusedView;
     View mRealFocusedView;  // this is not set to null in touch mode
-    View mOldFocusedView;
     int mViewVisibility;
     boolean mAppVisible = true;
     int mOrigWindowType = -1;
@@ -2273,34 +2272,33 @@
 
     public void requestChildFocus(View child, View focused) {
         checkThread();
-
-        if (DEBUG_INPUT_RESIZE) {
-            Log.v(TAG, "Request child focus: focus now " + focused);
+        if (mFocusedView != focused) {
+            mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(mFocusedView, focused);
+            scheduleTraversals();
         }
-
-        mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(mOldFocusedView, focused);
-        scheduleTraversals();
-
         mFocusedView = mRealFocusedView = focused;
+        if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Request child focus: focus now "
+                + mFocusedView);
     }
 
     public void clearChildFocus(View child) {
         checkThread();
 
-        if (DEBUG_INPUT_RESIZE) {
-            Log.v(TAG, "Clearing child focus");
-        }
+        View oldFocus = mFocusedView;
 
-        mOldFocusedView = mFocusedView;
-
-        // Invoke the listener only if there is no view to take focus
-        if (focusSearch(null, View.FOCUS_FORWARD) == null) {
-            mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(mOldFocusedView, null);
-        }
-
+        if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Clearing child focus");
         mFocusedView = mRealFocusedView = null;
+        if (mView != null && !mView.hasFocus()) {
+            // If a view gets the focus, the listener will be invoked from requestChildFocus()
+            if (!mView.requestFocus(View.FOCUS_FORWARD)) {
+                mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(oldFocus, null);
+            }
+        } else if (oldFocus != null) {
+            mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(oldFocus, null);
+        }
     }
 
+
     public void focusableViewAvailable(View v) {
         checkThread();
 
@@ -2772,7 +2770,6 @@
                         mView.unFocus();
                         mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(focused, null);
                         mFocusedView = null;
-                        mOldFocusedView = null;
                         return true;
                     }
                 }
diff --git a/core/tests/coretests/src/android/widget/focus/RequestFocus.java b/core/tests/coretests/src/android/widget/focus/RequestFocus.java
index 21d762a..af9ee17 100644
--- a/core/tests/coretests/src/android/widget/focus/RequestFocus.java
+++ b/core/tests/coretests/src/android/widget/focus/RequestFocus.java
@@ -21,7 +21,9 @@
 import android.app.Activity;
 import android.os.Bundle;
 import android.os.Handler;
+import android.widget.LinearLayout;
 import android.widget.Button;
+import android.view.View;
 
 /**
  * Exercises cases where elements of the UI are requestFocus()ed.
diff --git a/core/tests/coretests/src/android/widget/focus/RequestFocusTest.java b/core/tests/coretests/src/android/widget/focus/RequestFocusTest.java
index baf587e..a78b0c9 100644
--- a/core/tests/coretests/src/android/widget/focus/RequestFocusTest.java
+++ b/core/tests/coretests/src/android/widget/focus/RequestFocusTest.java
@@ -16,27 +16,21 @@
 
 package android.widget.focus;
 
-import android.os.Handler;
-import android.test.ActivityInstrumentationTestCase2;
-import android.test.UiThreadTest;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.util.AndroidRuntimeException;
-import android.view.View;
-import android.view.View.OnFocusChangeListener;
-import android.view.ViewTreeObserver.OnGlobalFocusChangeListener;
-import android.widget.Button;
-
+import android.widget.focus.RequestFocus;
 import com.android.frameworks.coretests.R;
 
-import java.util.ArrayList;
-import java.util.List;
+import android.os.Handler;
+import android.test.ActivityInstrumentationTestCase;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.widget.Button;
+import android.util.AndroidRuntimeException;
 
 /**
  * {@link RequestFocusTest} is set up to exercise cases where the views that
  * have focus become invisible or GONE.
  */
-public class RequestFocusTest extends ActivityInstrumentationTestCase2<RequestFocus> {
+public class RequestFocusTest extends ActivityInstrumentationTestCase<RequestFocus> {
 
     private Button mTopLeftButton;
     private Button mBottomLeftButton;
@@ -45,7 +39,7 @@
     private Handler mHandler;
 
     public RequestFocusTest() {
-        super(RequestFocus.class);
+        super("com.android.frameworks.coretests", RequestFocus.class);
     }
 
     @Override
@@ -100,145 +94,4 @@
                          e.getClass().getName());
         }
     }
-
-    /**
-     * This tests checks the case in which the first focusable View clears focus.
-     * In such a case the framework tries to give the focus to another View starting
-     * from the top. Hence, the framework will try to give focus to the view that
-     * wants to clear its focus. From a client perspective, the view does not loose
-     * focus after the call, therefore no callback for focus change should be invoked.
-     *
-     * @throws Exception If an error occurs.
-     */
-    @UiThreadTest
-    public void testOnFocusChangeNotCalledIfFocusDoesNotMove() throws Exception {
-        // Get the first focusable.
-        Button button = mTopLeftButton;
-
-        // Make sure that the button is the first focusable and focus it.
-        button.getRootView().requestFocus(View.FOCUS_DOWN);
-        assertTrue(button.hasFocus());
-
-        // Attach on focus change listener that should not be called.
-        button.setOnFocusChangeListener(new OnFocusChangeListener() {
-            @Override
-            public void onFocusChange(View v, boolean hasFocus) {
-                throw new IllegalStateException("Unexpeced call to"
-                        + "OnFocusChangeListener#onFocusChange");
-            }
-        });
-
-        // Attach on global focus change listener that should not be called.
-        button.getViewTreeObserver().addOnGlobalFocusChangeListener(
-                new OnGlobalFocusChangeListener() {
-            @Override
-            public void onGlobalFocusChanged(View oldFocus, View newFocus) {
-                throw new IllegalStateException("Unexpeced call to"
-                        + "OnFocusChangeListener#onFocusChange");
-            }
-        });
-
-        // Try to clear focus.
-        button.clearFocus();
-    }
-
-    /**
-     * This tests check whether the on focus change callbacks are invoked in
-     * the proper order when a View loses focus and the framework gives it to
-     * the fist focusable one.
-     *
-     * @throws Exception
-     */
-    @UiThreadTest
-    public void testOnFocusChangeCallbackOrder() throws Exception {
-        // Get the first focusable.
-        Button clearingFocusButton = mTopRightButton;
-        Button gainingFocusButton = mTopLeftButton;
-
-        // Make sure that the clearing focus is not the first focusable.
-        View focusCandidate = clearingFocusButton.getRootView().getParent().focusSearch(null,
-                View.FOCUS_FORWARD);
-        assertNotSame("The clearing focus button is not the first focusable.",
-                clearingFocusButton, focusCandidate);
-        assertSame("The gaining focus button is the first focusable.",
-                gainingFocusButton, focusCandidate);
-
-        // Focus the clearing focus button.
-        clearingFocusButton.requestFocus();
-        assertTrue(clearingFocusButton.hasFocus());
-
-        // Register the invocation order checker.
-        CallbackOrderChecker checker = new CallbackOrderChecker(clearingFocusButton,
-                gainingFocusButton);
-        clearingFocusButton.setOnFocusChangeListener(checker);
-        gainingFocusButton.setOnFocusChangeListener(checker);
-        clearingFocusButton.getViewTreeObserver().addOnGlobalFocusChangeListener(checker);
-
-        // Try to clear focus.
-        clearingFocusButton.clearFocus();
-
-        // Check that no callback was invoked since focus did not move.
-        checker.verify();
-    }
-
-    /**
-     * This class check whether the focus change callback are invoked in order.
-     */
-    private class CallbackOrderChecker implements OnFocusChangeListener,
-            OnGlobalFocusChangeListener {
-
-        private class CallbackInvocation {
-            final String mMethodName;
-            final Object[] mArguments;
-
-            CallbackInvocation(String methodName, Object[] arguments) {
-                mMethodName = methodName;
-                mArguments = arguments;
-            }
-        }
-
-        private final View mClearingFocusView;
-        private final View mGainingFocusView;
-
-        private final List<CallbackInvocation> mInvocations = new ArrayList<CallbackInvocation>();
-
-        public CallbackOrderChecker(View clearingFocusView, View gainingFocusView) {
-            mClearingFocusView = clearingFocusView;
-            mGainingFocusView = gainingFocusView;
-        }
-
-        @Override
-        public void onFocusChange(View view, boolean hasFocus) {
-            CallbackInvocation invocation = new CallbackInvocation(
-                    "OnFocusChangeListener#onFocusChange", new Object[] {view, hasFocus});
-            mInvocations.add(invocation);
-        }
-
-        @Override
-        public void onGlobalFocusChanged(View oldFocus, View newFocus) {
-            CallbackInvocation invocation = new CallbackInvocation(
-                    "OnFocusChangeListener#onFocusChange", new Object[] {oldFocus, newFocus});
-            mInvocations.add(invocation);
-        }
-
-        public void verify() {
-            assertSame("All focus change callback should be invoked.", 3, mInvocations.size());
-            assertInvioked("Callback for View clearing focus explected.", 0,
-                    "OnFocusChangeListener#onFocusChange",
-                    new Object[] {mClearingFocusView, false});
-            assertInvioked("Callback for View global focus change explected.", 1,
-                    "OnFocusChangeListener#onFocusChange", new Object[] {mClearingFocusView,
-                    mGainingFocusView});
-            assertInvioked("Callback for View gaining focus explected.", 2,
-                    "OnFocusChangeListener#onFocusChange", new Object[] {mGainingFocusView, true});
-        }
-
-        private void assertInvioked(String message, int order, String methodName,
-                Object[] arguments) {
-            CallbackInvocation invocation = mInvocations.get(order);
-            assertEquals(message, methodName, invocation.mMethodName);
-            assertEquals(message, arguments[0], invocation.mArguments[0]);
-            assertEquals(message, arguments[1], invocation.mArguments[1]);
-        }
-    }
 }