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]);
- }
- }
}