Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b36a0ac9 authored by Svetoslav Ganov's avatar Svetoslav Ganov
Browse files

Incorrect behavior of View clear focus v2.0.

The framework tries to have a focused view all the time. For
that purpose when a view's focus is cleared the focus is given
to the first focusable found from the top. The implementation
of this behavior was causing the following issues:

1. If the fist focusable View tries to clear its focus it
   was getting focus but the onFocusChange callbacks were not
   properly invoked. Specifically, the onFocusChange for
   gaining focus was called first and then the same
   callback for clearing focus. Note that the callback
   for clearing focus is called when the View is already
   focused.

2. If not the first focusable View tries to clear focus,
   the focus is given to another one but the callback
   for getting focus was called before the one for clearing,
   so client code may be mislead that there is more than
   one focused view at a time.

3. (Nit) The implementaion of clearFocus and unFocus in ViewGroup
   was calling the super implementaion when there is a
   focused child. Since there could be only one focused View,
   having a focused child means that the group is not focused
   and the call to the super implementation is not needed.

4. Added unit tests that verify the correct behavior, i.e.
   the focus of the first focused view cannot be cleared
   which means that no focus change callbacks are invoked.
   The callbacks should be called in expected order.
   Now the view focus clear precedes the view focus gain
   callback. However, in between is invoked the global
   focus change callback with the correct values. We may
   want to call that one after the View callbacks. If
   needed we can revisit this.

Change-Id: I8cfb141c948141703093cf6fa2037be60861cee0
parent 91ec0b72
Loading
Loading
Loading
Loading
+5 −0
Original line number Original line Diff line number Diff line
@@ -3788,6 +3788,11 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
            onFocusChanged(false, 0, null);
            onFocusChanged(false, 0, null);
            refreshDrawableState();
            refreshDrawableState();
            // The view cleared focus and invoked the callbacks, so  now is the
            // time to give focus to the the first focusable from the top to
            // ensure that the gain focus is announced after clear focus.
            getRootView().requestFocus(FOCUS_FORWARD);
        }
        }
    }
    }
+11 −8
Original line number Original line Diff line number Diff line
@@ -675,11 +675,14 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
     */
     */
    @Override
    @Override
    public void clearFocus() {
    public void clearFocus() {
        if (DBG) {
            System.out.println(this + " clearFocus()");
        }
        if (mFocused == null) {
            super.clearFocus();
            super.clearFocus();

        } else {
        // clear any child focus if it exists
        if (mFocused != null) {
            mFocused.clearFocus();
            mFocused.clearFocus();
            mFocused = null;
        }
        }
    }
    }


@@ -691,13 +694,13 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
        if (DBG) {
        if (DBG) {
            System.out.println(this + " unFocus()");
            System.out.println(this + " unFocus()");
        }
        }

        if (mFocused == null) {
            super.unFocus();
            super.unFocus();
        if (mFocused != null) {
        } else {
            mFocused.unFocus();
            mFocused.unFocus();
        }
            mFocused = null;
            mFocused = null;
        }
        }
    }


    /**
    /**
     * Returns the focused child of this view, if any. The child may have focus
     * Returns the focused child of this view, if any. The child may have focus
+19 −16
Original line number Original line Diff line number Diff line
@@ -174,6 +174,7 @@ public final class ViewRootImpl extends Handler implements ViewParent,
    View mView;
    View mView;
    View mFocusedView;
    View mFocusedView;
    View mRealFocusedView;  // this is not set to null in touch mode
    View mRealFocusedView;  // this is not set to null in touch mode
    View mOldFocusedView;
    int mViewVisibility;
    int mViewVisibility;
    boolean mAppVisible = true;
    boolean mAppVisible = true;
    int mOrigWindowType = -1;
    int mOrigWindowType = -1;
@@ -2272,32 +2273,33 @@ public final class ViewRootImpl extends Handler implements ViewParent,


    public void requestChildFocus(View child, View focused) {
    public void requestChildFocus(View child, View focused) {
        checkThread();
        checkThread();
        if (mFocusedView != focused) {

            mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(mFocusedView, focused);
        if (DEBUG_INPUT_RESIZE) {
            scheduleTraversals();
            Log.v(TAG, "Request child focus: focus now " + focused);
        }
        }

        mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(mOldFocusedView, focused);
        scheduleTraversals();

        mFocusedView = mRealFocusedView = focused;
        mFocusedView = mRealFocusedView = focused;
        if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Request child focus: focus now "
                + mFocusedView);
    }
    }


    public void clearChildFocus(View child) {
    public void clearChildFocus(View child) {
        checkThread();
        checkThread();


        View oldFocus = mFocusedView;
        if (DEBUG_INPUT_RESIZE) {

            Log.v(TAG, "Clearing child focus");
        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);
        }
        }

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


        mFocusedView = mRealFocusedView = null;
    }


    public void focusableViewAvailable(View v) {
    public void focusableViewAvailable(View v) {
        checkThread();
        checkThread();
@@ -2770,6 +2772,7 @@ public final class ViewRootImpl extends Handler implements ViewParent,
                        mView.unFocus();
                        mView.unFocus();
                        mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(focused, null);
                        mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(focused, null);
                        mFocusedView = null;
                        mFocusedView = null;
                        mOldFocusedView = null;
                        return true;
                        return true;
                    }
                    }
                }
                }
+1 −1
Original line number Original line Diff line number Diff line
@@ -22,7 +22,7 @@ LOCAL_SRC_FILES := \
	$(call all-java-files-under, EnabledTestApp/src)
	$(call all-java-files-under, EnabledTestApp/src)


LOCAL_DX_FLAGS := --core-library
LOCAL_DX_FLAGS := --core-library
LOCAL_STATIC_JAVA_LIBRARIES := core-tests android-common frameworks-core-util-lib mockwebserver guava
LOCAL_STATIC_JAVA_LIBRARIES := core-tests android-common frameworks-core-util-lib mockwebserver guava littlemock
LOCAL_JAVA_LIBRARIES := android.test.runner
LOCAL_JAVA_LIBRARIES := android.test.runner
LOCAL_PACKAGE_NAME := FrameworksCoreTests
LOCAL_PACKAGE_NAME := FrameworksCoreTests


+0 −2
Original line number Original line Diff line number Diff line
@@ -21,9 +21,7 @@ import com.android.frameworks.coretests.R;
import android.app.Activity;
import android.app.Activity;
import android.os.Bundle;
import android.os.Bundle;
import android.os.Handler;
import android.os.Handler;
import android.widget.LinearLayout;
import android.widget.Button;
import android.widget.Button;
import android.view.View;


/**
/**
 * Exercises cases where elements of the UI are requestFocus()ed.
 * Exercises cases where elements of the UI are requestFocus()ed.
Loading