From 88c11754c19a6bbdbf9f822d928761d23029f31f Mon Sep 17 00:00:00 2001 From: Adam Powell Date: Mon, 21 Jul 2014 17:19:16 -0700 Subject: [PATCH] Allow focusable in touch mode views to ignore touchscreen focus blocks Bug 16284253 Change-Id: I7cddf1988b0c24ae3050a13d2ce6fb3143d8aade --- core/java/android/view/View.java | 12 ++++++++- core/java/android/view/ViewGroup.java | 39 ++++++++++++++++++--------- 2 files changed, 37 insertions(+), 14 deletions(-) diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 90eb5161cddf0..0079cc9a337b4 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -5048,8 +5048,17 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * View, false otherwise. * * @see ViewGroup#FOCUS_BLOCK_DESCENDANTS + * @see ViewGroup#getTouchscreenBlocksFocus() */ public boolean hasFocusable() { + if (!isFocusableInTouchMode()) { + for (ViewParent p = mParent; p instanceof ViewGroup; p = p.getParent()) { + final ViewGroup g = (ViewGroup) p; + if (g.shouldBlockFocusForTouchscreen()) { + return false; + } + } + } return (mViewFlags & VISIBILITY_MASK) == VISIBLE && isFocusable(); } @@ -7439,11 +7448,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @return Whether any ancestor of this view blocks descendant focus. */ private boolean hasAncestorThatBlocksDescendantFocus() { + final boolean focusableInTouchMode = isFocusableInTouchMode(); ViewParent ancestor = mParent; while (ancestor instanceof ViewGroup) { final ViewGroup vgAncestor = (ViewGroup) ancestor; if (vgAncestor.getDescendantFocusability() == ViewGroup.FOCUS_BLOCK_DESCENDANTS - || vgAncestor.shouldBlockFocusForTouchscreen()) { + || (!focusableInTouchMode && vgAncestor.shouldBlockFocusForTouchscreen())) { return true; } else { ancestor = vgAncestor.getParent(); diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 04c8b0b36c51e..1028a0c1e8e30 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -669,7 +669,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager // shortcut: don't report a new focusable view if we block our descendants from // getting focus && (getDescendantFocusability() != FOCUS_BLOCK_DESCENDANTS) - && !shouldBlockFocusForTouchscreen() + && (isFocusableInTouchMode() || !shouldBlockFocusForTouchscreen()) // shortcut: don't report a new focusable view if we already are focused // (and we don't prefer our descendants) // @@ -865,6 +865,17 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager return mFocused; } + View getDeepestFocusedChild() { + View v = this; + while (v != null) { + if (v.isFocused()) { + return v; + } + v = v instanceof ViewGroup ? ((ViewGroup) v).getFocusedChild() : null; + } + return null; + } + /** * Returns true if this view has or contains focus * @@ -911,8 +922,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } final int descendantFocusability = getDescendantFocusability(); - if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS && - !shouldBlockFocusForTouchscreen()) { + if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS) { final int count = mChildrenCount; final View[] children = mChildren; @@ -936,8 +946,11 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager final int descendantFocusability = getDescendantFocusability(); - if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS && - !shouldBlockFocusForTouchscreen()) { + if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS) { + if (shouldBlockFocusForTouchscreen()) { + focusableMode |= FOCUSABLES_TOUCH_MODE; + } + final int count = mChildrenCount; final View[] children = mChildren; @@ -955,7 +968,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager // among the focusable children would be more interesting. if ((descendantFocusability != FOCUS_AFTER_DESCENDANTS // No focusable descendants - || (focusableCount == views.size())) && !shouldBlockFocusForTouchscreen()) { + || (focusableCount == views.size())) && + (isFocusableInTouchMode() || !shouldBlockFocusForTouchscreen())) { super.addFocusables(views, direction, focusableMode); } } @@ -971,9 +985,12 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager if (touchscreenBlocksFocus) { mGroupFlags |= FLAG_TOUCHSCREEN_BLOCKS_FOCUS; if (hasFocus()) { - final View newFocus = focusSearch(FOCUS_FORWARD); - if (newFocus != null) { - newFocus.requestFocus(); + final View focusedChild = getDeepestFocusedChild(); + if (!focusedChild.isFocusableInTouchMode()) { + final View newFocus = focusSearch(FOCUS_FORWARD); + if (newFocus != null) { + newFocus.requestFocus(); + } } } } else { @@ -2485,10 +2502,6 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } int descendantFocusability = getDescendantFocusability(); - if (shouldBlockFocusForTouchscreen()) { - return false; - } - switch (descendantFocusability) { case FOCUS_BLOCK_DESCENDANTS: return super.requestFocus(direction, previouslyFocusedRect);