Merge "Fixing a memory leak in ViewRootImpl and a focus change callback issue."
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 97287c3..a4e4f37 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -4376,10 +4376,16 @@
if ((mPrivateFlags & PFLAG_FOCUSED) == 0) {
mPrivateFlags |= PFLAG_FOCUSED;
+ View oldFocus = (mAttachInfo != null) ? getRootView().findFocus() : null;
+
if (mParent != null) {
mParent.requestChildFocus(this, this);
}
+ if (mAttachInfo != null) {
+ mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(oldFocus, this);
+ }
+
onFocusChanged(true, direction, previouslyFocusedRect);
refreshDrawableState();
@@ -4486,7 +4492,9 @@
refreshDrawableState();
- ensureInputFocusOnFirstFocusable();
+ if (!rootViewRequestFocus()) {
+ notifyGlobalFocusCleared(this);
+ }
if (AccessibilityManager.getInstance(mContext).isEnabled()) {
notifyAccessibilityStateChanged();
@@ -4494,11 +4502,18 @@
}
}
- void ensureInputFocusOnFirstFocusable() {
+ void notifyGlobalFocusCleared(View oldFocus) {
+ if (oldFocus != null && mAttachInfo != null) {
+ mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(oldFocus, null);
+ }
+ }
+
+ boolean rootViewRequestFocus() {
View root = getRootView();
if (root != null) {
- root.requestFocus();
+ return root.requestFocus();
}
+ return false;
}
/**
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index c274f27..a4898fc 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -3697,7 +3697,9 @@
clearChildFocus = true;
}
- view.clearAccessibilityFocus();
+ if (view.isAccessibilityFocused()) {
+ view.clearAccessibilityFocus();
+ }
cancelTouchTarget(view);
cancelHoverTarget(view);
@@ -3719,11 +3721,9 @@
if (clearChildFocus) {
clearChildFocus(view);
- ensureInputFocusOnFirstFocusable();
- }
-
- if (view.isAccessibilityFocused()) {
- view.clearAccessibilityFocus();
+ if (!rootViewRequestFocus()) {
+ notifyGlobalFocusCleared(this);
+ }
}
onViewRemoved(view);
@@ -3765,7 +3765,7 @@
private void removeViewsInternal(int start, int count) {
final View focused = mFocused;
final boolean detach = mAttachInfo != null;
- View clearChildFocus = null;
+ boolean clearChildFocus = false;
final View[] children = mChildren;
final int end = start + count;
@@ -3779,10 +3779,12 @@
if (view == focused) {
view.unFocus();
- clearChildFocus = view;
+ clearChildFocus = true;
}
- view.clearAccessibilityFocus();
+ if (view.isAccessibilityFocused()) {
+ view.clearAccessibilityFocus();
+ }
cancelTouchTarget(view);
cancelHoverTarget(view);
@@ -3805,9 +3807,11 @@
removeFromArray(start, count);
- if (clearChildFocus != null) {
- clearChildFocus(clearChildFocus);
- ensureInputFocusOnFirstFocusable();
+ if (clearChildFocus) {
+ clearChildFocus(focused);
+ if (!rootViewRequestFocus()) {
+ notifyGlobalFocusCleared(focused);
+ }
}
}
@@ -3849,7 +3853,7 @@
final View focused = mFocused;
final boolean detach = mAttachInfo != null;
- View clearChildFocus = null;
+ boolean clearChildFocus = false;
needGlobalAttributesUpdate(false);
@@ -3862,10 +3866,12 @@
if (view == focused) {
view.unFocus();
- clearChildFocus = view;
+ clearChildFocus = true;
}
- view.clearAccessibilityFocus();
+ if (view.isAccessibilityFocused()) {
+ view.clearAccessibilityFocus();
+ }
cancelTouchTarget(view);
cancelHoverTarget(view);
@@ -3887,9 +3893,11 @@
children[i] = null;
}
- if (clearChildFocus != null) {
- clearChildFocus(clearChildFocus);
- ensureInputFocusOnFirstFocusable();
+ if (clearChildFocus) {
+ clearChildFocus(focused);
+ if (!rootViewRequestFocus()) {
+ notifyGlobalFocusCleared(focused);
+ }
}
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index f9f955e..6a96893 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -169,9 +169,6 @@
int mSeq;
View mView;
- View mFocusedView;
- View mRealFocusedView; // this is not set to null in touch mode
- View mOldFocusedView;
View mAccessibilityFocusedHost;
AccessibilityNodeInfo mAccessibilityFocusedVirtualView;
@@ -269,7 +266,7 @@
boolean mScrollMayChange;
int mSoftInputMode;
- View mLastScrolledFocus;
+ WeakReference<View> mLastScrolledFocus;
int mScrollY;
int mCurScrollY;
Scroller mScroller;
@@ -1526,7 +1523,9 @@
} else if (!mSurface.isValid()) {
// If the surface has been removed, then reset the scroll
// positions.
- mLastScrolledFocus = null;
+ if (mLastScrolledFocus != null) {
+ mLastScrolledFocus.clear();
+ }
mScrollY = mCurScrollY = 0;
if (mScroller != null) {
mScroller.abortAnimation();
@@ -1802,13 +1801,11 @@
if (mView != null) {
if (!mView.hasFocus()) {
mView.requestFocus(View.FOCUS_FORWARD);
- mFocusedView = mRealFocusedView = mView.findFocus();
if (DEBUG_INPUT_RESIZE) Log.v(TAG, "First: requested focused view="
- + mFocusedView);
+ + mView.findFocus());
} else {
- mRealFocusedView = mView.findFocus();
if (DEBUG_INPUT_RESIZE) Log.v(TAG, "First: existing focused view="
- + mRealFocusedView);
+ + mView.findFocus());
}
}
if ((relayoutResult & WindowManagerGlobal.RELAYOUT_RES_ANIMATING) != 0) {
@@ -2514,17 +2511,12 @@
// requestChildRectangleOnScreen() call (in which case 'rectangle'
// is non-null and we just want to scroll to whatever that
// rectangle is).
- View focus = mRealFocusedView;
-
- // When in touch mode, focus points to the previously focused view,
- // which may have been removed from the view hierarchy. The following
- // line checks whether the view is still in our hierarchy.
- if (focus == null || focus.mAttachInfo != mAttachInfo) {
- mRealFocusedView = null;
+ View focus = mView.findFocus();
+ if (focus == null) {
return false;
}
-
- if (focus != mLastScrolledFocus) {
+ View lastScrolledFocus = (mLastScrolledFocus != null) ? mLastScrolledFocus.get() : null;
+ if (lastScrolledFocus != null && focus != lastScrolledFocus) {
// If the focus has changed, then ignore any requests to scroll
// to a rectangle; first we want to make sure the entire focus
// view is visible.
@@ -2533,8 +2525,7 @@
if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Eval scroll: focus=" + focus
+ " rectangle=" + rectangle + " ci=" + ci
+ " vi=" + vi);
- if (focus == mLastScrolledFocus && !mScrollMayChange
- && rectangle == null) {
+ if (focus == lastScrolledFocus && !mScrollMayChange && rectangle == null) {
// Optimization: if the focus hasn't changed since last
// time, and no layout has happened, then just leave things
// as they are.
@@ -2544,7 +2535,7 @@
// We need to determine if the currently focused view is
// within the visible part of the window and, if not, apply
// a pan so it can be seen.
- mLastScrolledFocus = focus;
+ mLastScrolledFocus = new WeakReference<View>(focus);
mScrollMayChange = false;
if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Need to scroll?");
// Try to find the rectangle from the focus view.
@@ -2671,33 +2662,19 @@
}
public void requestChildFocus(View child, View focused) {
- checkThread();
-
if (DEBUG_INPUT_RESIZE) {
Log.v(TAG, "Request child focus: focus now " + focused);
}
-
- mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(mOldFocusedView, focused);
+ checkThread();
scheduleTraversals();
-
- mFocusedView = mRealFocusedView = focused;
}
public void clearChildFocus(View child) {
- checkThread();
-
if (DEBUG_INPUT_RESIZE) {
Log.v(TAG, "Clearing child focus");
}
-
- 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;
+ checkThread();
+ scheduleTraversals();
}
@Override
@@ -2714,14 +2691,13 @@
// the one case where will transfer focus away from the current one
// is if the current view is a view group that prefers to give focus
// to its children first AND the view is a descendant of it.
- mFocusedView = mView.findFocus();
- boolean descendantsHaveDibsOnFocus =
- (mFocusedView instanceof ViewGroup) &&
- (((ViewGroup) mFocusedView).getDescendantFocusability() ==
- ViewGroup.FOCUS_AFTER_DESCENDANTS);
- if (descendantsHaveDibsOnFocus && isViewDescendantOf(v, mFocusedView)) {
- // If a view gets the focus, the listener will be invoked from requestChildFocus()
- v.requestFocus();
+ View focused = mView.findFocus();
+ if (focused instanceof ViewGroup) {
+ ViewGroup group = (ViewGroup) focused;
+ if (group.getDescendantFocusability() == ViewGroup.FOCUS_AFTER_DESCENDANTS
+ && isViewDescendantOf(v, focused)) {
+ v.requestFocus();
+ }
}
}
}
@@ -3199,7 +3175,6 @@
// set yet.
final View focused = mView.findFocus();
if (focused != null && !focused.isFocusableInTouchMode()) {
-
final ViewGroup ancestorToTakeFocus =
findAncestorToTakeFocusInTouchMode(focused);
if (ancestorToTakeFocus != null) {
@@ -3208,10 +3183,7 @@
return ancestorToTakeFocus.requestFocus();
} else {
// nothing appropriate to have focus in touch mode, clear it out
- mView.unFocus();
- mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(focused, null);
- mFocusedView = null;
- mOldFocusedView = null;
+ focused.unFocus();
return true;
}
}
@@ -3246,12 +3218,11 @@
private boolean leaveTouchMode() {
if (mView != null) {
if (mView.hasFocus()) {
- // i learned the hard way to not trust mFocusedView :)
- mFocusedView = mView.findFocus();
- if (!(mFocusedView instanceof ViewGroup)) {
+ View focusedView = mView.findFocus();
+ if (!(focusedView instanceof ViewGroup)) {
// some view has focus, let it keep it
return false;
- } else if (((ViewGroup)mFocusedView).getDescendantFocusability() !=
+ } else if (((ViewGroup) focusedView).getDescendantFocusability() !=
ViewGroup.FOCUS_AFTER_DESCENDANTS) {
// some view group has focus, and doesn't prefer its children
// over itself for focus, so let them keep it.