diff --git a/api/current.txt b/api/current.txt index ec16f5a5f56b8..443ac9dbaac9f 100644 --- a/api/current.txt +++ b/api/current.txt @@ -41816,7 +41816,7 @@ package android.view { method public android.view.View findNearestTouchable(android.view.ViewGroup, int, int, int, int[]); method public final android.view.View findNextFocus(android.view.ViewGroup, android.view.View, int); method public android.view.View findNextFocusFromRect(android.view.ViewGroup, android.graphics.Rect, int); - method public android.view.View findNextKeyboardNavigationCluster(android.view.ViewGroup, android.view.View, int); + method public android.view.View findNextKeyboardNavigationCluster(int, android.view.View, android.view.View, int); method public static android.view.FocusFinder getInstance(); } @@ -43109,7 +43109,7 @@ package android.view { method public void addChildrenForAccessibility(java.util.ArrayList); method public void addFocusables(java.util.ArrayList, int); method public void addFocusables(java.util.ArrayList, int, int); - method public void addKeyboardNavigationClusters(java.util.Collection, int); + method public void addKeyboardNavigationClusters(int, java.util.Collection, int); method public void addOnAttachStateChangeListener(android.view.View.OnAttachStateChangeListener); method public void addOnLayoutChangeListener(android.view.View.OnLayoutChangeListener); method public void addTouchables(java.util.ArrayList); @@ -43397,7 +43397,7 @@ package android.view { method public boolean isVerticalFadingEdgeEnabled(); method public boolean isVerticalScrollBarEnabled(); method public void jumpDrawablesToCurrentState(); - method public android.view.View keyboardNavigationClusterSearch(int); + method public android.view.View keyboardNavigationClusterSearch(int, android.view.View, int); method public void layout(int, int, int, int); method public final void measure(int, int); method protected static int[] mergeDrawableStates(int[], int[]); @@ -43679,6 +43679,8 @@ package android.view { field public static final int FOCUS_BACKWARD = 1; // 0x1 field public static final int FOCUS_DOWN = 130; // 0x82 field public static final int FOCUS_FORWARD = 2; // 0x2 + field public static final int FOCUS_GROUP_CLUSTER = 1; // 0x1 + field public static final int FOCUS_GROUP_SECTION = 2; // 0x2 field public static final int FOCUS_LEFT = 17; // 0x11 field public static final int FOCUS_RIGHT = 66; // 0x42 field public static final int FOCUS_UP = 33; // 0x21 @@ -44046,7 +44048,6 @@ package android.view { method protected deprecated boolean isChildrenDrawnWithCacheEnabled(); method public boolean isMotionEventSplittingEnabled(); method public boolean isTransitionGroup(); - method public android.view.View keyboardNavigationClusterSearch(android.view.View, int); method public final void layout(int, int, int, int); method protected void measureChild(android.view.View, int, int); method protected void measureChildWithMargins(android.view.View, int, int, int, int); @@ -44209,7 +44210,7 @@ package android.view { method public abstract boolean isLayoutRequested(); method public abstract boolean isTextAlignmentResolved(); method public abstract boolean isTextDirectionResolved(); - method public abstract android.view.View keyboardNavigationClusterSearch(android.view.View, int); + method public abstract android.view.View keyboardNavigationClusterSearch(int, android.view.View, int); method public abstract void notifySubtreeAccessibilityStateChanged(android.view.View, android.view.View, int); method public abstract boolean onNestedFling(android.view.View, float, float, boolean); method public abstract boolean onNestedPreFling(android.view.View, float, float); diff --git a/api/system-current.txt b/api/system-current.txt index 4bba111f93921..bc0e3ef5ac274 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -45008,7 +45008,7 @@ package android.view { method public android.view.View findNearestTouchable(android.view.ViewGroup, int, int, int, int[]); method public final android.view.View findNextFocus(android.view.ViewGroup, android.view.View, int); method public android.view.View findNextFocusFromRect(android.view.ViewGroup, android.graphics.Rect, int); - method public android.view.View findNextKeyboardNavigationCluster(android.view.ViewGroup, android.view.View, int); + method public android.view.View findNextKeyboardNavigationCluster(int, android.view.View, android.view.View, int); method public static android.view.FocusFinder getInstance(); } @@ -46301,7 +46301,7 @@ package android.view { method public void addChildrenForAccessibility(java.util.ArrayList); method public void addFocusables(java.util.ArrayList, int); method public void addFocusables(java.util.ArrayList, int, int); - method public void addKeyboardNavigationClusters(java.util.Collection, int); + method public void addKeyboardNavigationClusters(int, java.util.Collection, int); method public void addOnAttachStateChangeListener(android.view.View.OnAttachStateChangeListener); method public void addOnLayoutChangeListener(android.view.View.OnLayoutChangeListener); method public void addTouchables(java.util.ArrayList); @@ -46589,7 +46589,7 @@ package android.view { method public boolean isVerticalFadingEdgeEnabled(); method public boolean isVerticalScrollBarEnabled(); method public void jumpDrawablesToCurrentState(); - method public android.view.View keyboardNavigationClusterSearch(int); + method public android.view.View keyboardNavigationClusterSearch(int, android.view.View, int); method public void layout(int, int, int, int); method public final void measure(int, int); method protected static int[] mergeDrawableStates(int[], int[]); @@ -46871,6 +46871,8 @@ package android.view { field public static final int FOCUS_BACKWARD = 1; // 0x1 field public static final int FOCUS_DOWN = 130; // 0x82 field public static final int FOCUS_FORWARD = 2; // 0x2 + field public static final int FOCUS_GROUP_CLUSTER = 1; // 0x1 + field public static final int FOCUS_GROUP_SECTION = 2; // 0x2 field public static final int FOCUS_LEFT = 17; // 0x11 field public static final int FOCUS_RIGHT = 66; // 0x42 field public static final int FOCUS_UP = 33; // 0x21 @@ -47238,7 +47240,6 @@ package android.view { method protected deprecated boolean isChildrenDrawnWithCacheEnabled(); method public boolean isMotionEventSplittingEnabled(); method public boolean isTransitionGroup(); - method public android.view.View keyboardNavigationClusterSearch(android.view.View, int); method public final void layout(int, int, int, int); method protected void measureChild(android.view.View, int, int); method protected void measureChildWithMargins(android.view.View, int, int, int, int); @@ -47401,7 +47402,7 @@ package android.view { method public abstract boolean isLayoutRequested(); method public abstract boolean isTextAlignmentResolved(); method public abstract boolean isTextDirectionResolved(); - method public abstract android.view.View keyboardNavigationClusterSearch(android.view.View, int); + method public abstract android.view.View keyboardNavigationClusterSearch(int, android.view.View, int); method public abstract void notifySubtreeAccessibilityStateChanged(android.view.View, android.view.View, int); method public abstract boolean onNestedFling(android.view.View, float, float, boolean); method public abstract boolean onNestedPreFling(android.view.View, float, float); diff --git a/api/test-current.txt b/api/test-current.txt index d4fb027bcb4de..c545584380d17 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -42103,7 +42103,7 @@ package android.view { method public android.view.View findNearestTouchable(android.view.ViewGroup, int, int, int, int[]); method public final android.view.View findNextFocus(android.view.ViewGroup, android.view.View, int); method public android.view.View findNextFocusFromRect(android.view.ViewGroup, android.graphics.Rect, int); - method public android.view.View findNextKeyboardNavigationCluster(android.view.ViewGroup, android.view.View, int); + method public android.view.View findNextKeyboardNavigationCluster(int, android.view.View, android.view.View, int); method public static android.view.FocusFinder getInstance(); } @@ -43398,7 +43398,7 @@ package android.view { method public void addChildrenForAccessibility(java.util.ArrayList); method public void addFocusables(java.util.ArrayList, int); method public void addFocusables(java.util.ArrayList, int, int); - method public void addKeyboardNavigationClusters(java.util.Collection, int); + method public void addKeyboardNavigationClusters(int, java.util.Collection, int); method public void addOnAttachStateChangeListener(android.view.View.OnAttachStateChangeListener); method public void addOnLayoutChangeListener(android.view.View.OnLayoutChangeListener); method public void addTouchables(java.util.ArrayList); @@ -43687,7 +43687,7 @@ package android.view { method public boolean isVerticalFadingEdgeEnabled(); method public boolean isVerticalScrollBarEnabled(); method public void jumpDrawablesToCurrentState(); - method public android.view.View keyboardNavigationClusterSearch(int); + method public android.view.View keyboardNavigationClusterSearch(int, android.view.View, int); method public void layout(int, int, int, int); method public final void measure(int, int); method protected static int[] mergeDrawableStates(int[], int[]); @@ -43969,6 +43969,8 @@ package android.view { field public static final int FOCUS_BACKWARD = 1; // 0x1 field public static final int FOCUS_DOWN = 130; // 0x82 field public static final int FOCUS_FORWARD = 2; // 0x2 + field public static final int FOCUS_GROUP_CLUSTER = 1; // 0x1 + field public static final int FOCUS_GROUP_SECTION = 2; // 0x2 field public static final int FOCUS_LEFT = 17; // 0x11 field public static final int FOCUS_RIGHT = 66; // 0x42 field public static final int FOCUS_UP = 33; // 0x21 @@ -44340,7 +44342,6 @@ package android.view { method protected deprecated boolean isChildrenDrawnWithCacheEnabled(); method public boolean isMotionEventSplittingEnabled(); method public boolean isTransitionGroup(); - method public android.view.View keyboardNavigationClusterSearch(android.view.View, int); method public final void layout(int, int, int, int); method protected void measureChild(android.view.View, int, int); method protected void measureChildWithMargins(android.view.View, int, int, int, int); @@ -44503,7 +44504,7 @@ package android.view { method public abstract boolean isLayoutRequested(); method public abstract boolean isTextAlignmentResolved(); method public abstract boolean isTextDirectionResolved(); - method public abstract android.view.View keyboardNavigationClusterSearch(android.view.View, int); + method public abstract android.view.View keyboardNavigationClusterSearch(int, android.view.View, int); method public abstract void notifySubtreeAccessibilityStateChanged(android.view.View, android.view.View, int); method public abstract boolean onNestedFling(android.view.View, float, float, boolean); method public abstract boolean onNestedPreFling(android.view.View, float, float); diff --git a/core/java/android/view/FocusFinder.java b/core/java/android/view/FocusFinder.java index 3f3d5190fac70..1a4f0d1ae803d 100644 --- a/core/java/android/view/FocusFinder.java +++ b/core/java/android/view/FocusFinder.java @@ -16,12 +16,16 @@ package android.view; +import static android.view.View.FOCUS_GROUP_CLUSTER; +import static android.view.View.FOCUS_GROUP_SECTION; + import android.annotation.NonNull; import android.annotation.Nullable; import android.graphics.Rect; import android.util.ArrayMap; import android.util.SparseArray; import android.util.SparseBooleanArray; +import android.view.View.FocusGroupType; import java.util.ArrayList; import java.util.Collections; @@ -107,21 +111,26 @@ public class FocusFinder { /** * Find the root of the next keyboard navigation cluster after the current one. - * @param root Thew view tree to look inside. Cannot be null + * @param focusGroupType Type of the focus group + * @param root The view tree to look inside. Cannot be null * @param currentCluster The starting point of the search. Null means the default cluster * @param direction Direction to look * @return The next cluster, or null if none exists */ public View findNextKeyboardNavigationCluster( - @NonNull ViewGroup root, @Nullable View currentCluster, int direction) { + @FocusGroupType int focusGroupType, + @NonNull View root, + @Nullable View currentCluster, + int direction) { View next = null; final ArrayList clusters = mTempList; try { clusters.clear(); - root.addKeyboardNavigationClusters(clusters, direction); + root.addKeyboardNavigationClusters(focusGroupType, clusters, direction); if (!clusters.isEmpty()) { - next = findNextKeyboardNavigationCluster(root, currentCluster, clusters, direction); + next = findNextKeyboardNavigationCluster( + focusGroupType, root, currentCluster, clusters, direction); } } finally { clusters.clear(); @@ -197,19 +206,25 @@ public class FocusFinder { } } - private View findNextKeyboardNavigationCluster(ViewGroup root, View currentCluster, - List clusters, int direction) { + private View findNextKeyboardNavigationCluster( + @FocusGroupType int focusGroupType, + View root, + View currentCluster, + List clusters, + int direction) { final int count = clusters.size(); switch (direction) { case View.FOCUS_FORWARD: case View.FOCUS_DOWN: case View.FOCUS_RIGHT: - return getNextKeyboardNavigationCluster(root, currentCluster, clusters, count); + return getNextKeyboardNavigationCluster( + focusGroupType, root, currentCluster, clusters, count); case View.FOCUS_BACKWARD: case View.FOCUS_UP: case View.FOCUS_LEFT: - return getPreviousKeyboardNavigationCluster(root, currentCluster, clusters, count); + return getPreviousKeyboardNavigationCluster( + focusGroupType, root, currentCluster, clusters, count); default: throw new IllegalArgumentException("Unknown direction: " + direction); } @@ -315,8 +330,12 @@ public class FocusFinder { return null; } - private static View getNextKeyboardNavigationCluster(ViewGroup root, View currentCluster, - List clusters, int count) { + private static View getNextKeyboardNavigationCluster( + @FocusGroupType int focusGroupType, + View root, + View currentCluster, + List clusters, + int count) { if (currentCluster == null) { // The current cluster is the default one. // The next cluster after the default one is the first one. @@ -330,12 +349,25 @@ public class FocusFinder { return clusters.get(position + 1); } - // The current cluster is the last one. The next one is the default one, i.e. the root. - return root; + switch (focusGroupType) { + case FOCUS_GROUP_CLUSTER: + // The current cluster is the last one. The next one is the default one, i.e. the + // root. + return root; + case FOCUS_GROUP_SECTION: + // There is no "default section", hence returning the first one. + return clusters.get(0); + default: + throw new IllegalArgumentException("Unknown focus group type: " + focusGroupType); + } } - private static View getPreviousKeyboardNavigationCluster(ViewGroup root, View currentCluster, - List clusters, int count) { + private static View getPreviousKeyboardNavigationCluster( + @FocusGroupType int focusGroupType, + View root, + View currentCluster, + List clusters, + int count) { if (currentCluster == null) { // The current cluster is the default one. // The previous cluster before the default one is the last one. @@ -349,9 +381,17 @@ public class FocusFinder { return clusters.get(position - 1); } - // The current cluster is the first one. The previous one is the default one, i.e. the - // root. - return root; + switch (focusGroupType) { + case FOCUS_GROUP_CLUSTER: + // The current cluster is the first one. The previous one is the default one, i.e. + // the root. + return root; + case FOCUS_GROUP_SECTION: + // There is no "default section", hence returning the last one. + return clusters.get(count - 1); + default: + throw new IllegalArgumentException("Unknown focus group type: " + focusGroupType); + } } /** diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index f51e029321471..aa941b8fcfb4a 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -1248,6 +1248,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback, @Retention(RetentionPolicy.SOURCE) public @interface FocusRealDirection {} // Like @FocusDirection, but without forward/backward + /** @hide */ + @IntDef({ + FOCUS_GROUP_CLUSTER, + FOCUS_GROUP_SECTION + }) + @Retention(RetentionPolicy.SOURCE) + public @interface FocusGroupType {} + /** * Use with {@link #focusSearch(int)}. Move focus to the previous selectable * item. @@ -1280,6 +1288,18 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ public static final int FOCUS_DOWN = 0x00000082; + /** + * Use with {@link #keyboardNavigationClusterSearch(int, View, int)}. Search for a keyboard + * navigation cluster. + */ + public static final int FOCUS_GROUP_CLUSTER = 1; + + /** + * Use with {@link #keyboardNavigationClusterSearch(int, View, int)}. Search for a keyboard + * navigation section. + */ + public static final int FOCUS_GROUP_SECTION = 2; + /** * Bits of {@link #getMeasuredWidthAndState()} and * {@link #getMeasuredWidthAndState()} that provide the actual measured size. @@ -9096,22 +9116,47 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } } + final boolean isFocusGroupOfType(@FocusGroupType int focusGroupType) { + switch (focusGroupType) { + case FOCUS_GROUP_CLUSTER: + return isKeyboardNavigationCluster(); + case FOCUS_GROUP_SECTION: + return isKeyboardNavigationSection(); + default: + throw new IllegalArgumentException("Unknown focus group type: " + focusGroupType); + } + } + /** * Find the nearest keyboard navigation cluster in the specified direction. * This does not actually give focus to that cluster. * + * @param focusGroupType Type of the focus group + * @param currentCluster The starting point of the search. Null means the current cluster is not + * found yet * @param direction Direction to look * * @return The nearest keyboard navigation cluster in the specified direction, or null if none * can be found */ - public View keyboardNavigationClusterSearch(int direction) { - if (mParent != null) { - final View currentCluster = isKeyboardNavigationCluster() ? this : null; - return mParent.keyboardNavigationClusterSearch(currentCluster, direction); - } else { - return null; + public View keyboardNavigationClusterSearch( + @FocusGroupType int focusGroupType, View currentCluster, int direction) { + if (isFocusGroupOfType(focusGroupType)) { + currentCluster = this; } + if (isRootNamespace() + || focusGroupType == FOCUS_GROUP_SECTION && isKeyboardNavigationCluster()) { + // Root namespace means we should consider ourselves the top of the + // tree for cluster searching; otherwise we could be focus searching + // into other tabs. see LocalActivityManager and TabHost for more info. + // In addition, a cluster node works as a root for section searches. + return FocusFinder.getInstance().findNextKeyboardNavigationCluster( + focusGroupType, this, currentCluster, direction); + } else if (mParent != null) { + return mParent.keyboardNavigationClusterSearch( + focusGroupType, currentCluster, direction); + } + return null; } /** @@ -9240,11 +9285,15 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * Adds any keyboard navigation cluster roots that are descendants of this view (possibly * including this view if it is a cluster root itself) to views. * + * @param focusGroupType Type of the focus group * @param views Cluster roots found so far * @param direction Direction to look */ - public void addKeyboardNavigationClusters(@NonNull Collection views, int direction) { - if (!isKeyboardNavigationCluster()) { + public void addKeyboardNavigationClusters( + @FocusGroupType int focusGroupType, + @NonNull Collection views, + int direction) { + if (!(isFocusGroupOfType(focusGroupType))) { return; } views.add(this); diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 7c133ac5b7a9f..7835899d14196 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -901,23 +901,6 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager return null; } - @Override - public View keyboardNavigationClusterSearch(View currentCluster, int direction) { - if (isKeyboardNavigationCluster()) { - currentCluster = this; - } - if (isRootNamespace()) { - // root namespace means we should consider ourselves the top of the - // tree for cluster searching; otherwise we could be focus searching - // into other tabs. see LocalActivityManager and TabHost for more info - return FocusFinder.getInstance().findNextKeyboardNavigationCluster( - this, currentCluster, direction); - } else if (mParent != null) { - return mParent.keyboardNavigationClusterSearch(currentCluster, direction); - } - return null; - } - @Override public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) { return false; @@ -1164,10 +1147,11 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } @Override - public void addKeyboardNavigationClusters(Collection views, int direction) { + public void addKeyboardNavigationClusters( + @FocusGroupType int focusGroupType, Collection views, int direction) { final int focusableCount = views.size(); - super.addKeyboardNavigationClusters(views, direction); + super.addKeyboardNavigationClusters(focusGroupType, views, direction); if (focusableCount != views.size()) { // No need to look for clusters inside a cluster. @@ -1183,8 +1167,13 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager for (int i = 0; i < count; i++) { final View child = children[i]; + if (focusGroupType == FOCUS_GROUP_SECTION && child.isKeyboardNavigationCluster()) { + // When the current cluster is the default cluster, and we are searching for + // sections, ignore sections inside non-default clusters. + continue; + } if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) { - child.addKeyboardNavigationClusters(views, direction); + child.addKeyboardNavigationClusters(focusGroupType, views, direction); } } } diff --git a/core/java/android/view/ViewParent.java b/core/java/android/view/ViewParent.java index 79b05cdb6e503..c5414e925e0e6 100644 --- a/core/java/android/view/ViewParent.java +++ b/core/java/android/view/ViewParent.java @@ -18,6 +18,7 @@ package android.view; import android.graphics.Rect; import android.os.Bundle; +import android.view.View.FocusGroupType; import android.view.accessibility.AccessibilityEvent; /** @@ -150,6 +151,7 @@ public interface ViewParent { * Find the nearest keyboard navigation cluster in the specified direction. * This does not actually give focus to that cluster. * + * @param focusGroupType Type of the focus group * @param currentCluster The starting point of the search. Null means the current cluster is not * found yet * @param direction Direction to look @@ -157,7 +159,8 @@ public interface ViewParent { * @return The nearest keyboard navigation cluster in the specified direction, or null if none * can be found */ - View keyboardNavigationClusterSearch(View currentCluster, int direction); + View keyboardNavigationClusterSearch( + @FocusGroupType int focusGroupType, View currentCluster, int direction); /** * Change the z order of the child so it's on top of all other children. diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 4f2020311ff46..18d59aa12d08e 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -16,6 +16,8 @@ package android.view; +import static android.view.View.FOCUS_GROUP_CLUSTER; +import static android.view.View.FOCUS_GROUP_SECTION; import static android.view.WindowCallbacks.RESIZE_MODE_DOCKED_DIVIDER; import static android.view.WindowCallbacks.RESIZE_MODE_FREEFORM; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY; @@ -71,6 +73,7 @@ import android.util.TimeUtils; import android.util.TypedValue; import android.view.Surface.OutOfResourcesException; import android.view.View.AttachInfo; +import android.view.View.FocusGroupType; import android.view.View.MeasureSpec; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; @@ -4392,11 +4395,12 @@ public final class ViewRootImpl implements ViewParent, return false; } - private boolean performClusterNavigation(int direction) { + private boolean performClusterNavigation( + @FocusGroupType int focusGroupType, int direction) { final View focused = mView.findFocus(); final View cluster = focused != null - ? focused.keyboardNavigationClusterSearch(direction) - : keyboardNavigationClusterSearch(null, direction); + ? focused.keyboardNavigationClusterSearch(focusGroupType, null, direction) + : keyboardNavigationClusterSearch(focusGroupType, null, direction); if (cluster != null && cluster.restoreLastFocus()) { return true; @@ -4418,15 +4422,32 @@ public final class ViewRootImpl implements ViewParent, } int clusterNavigationDirection = 0; + @FocusGroupType int focusGroupType = 0; if (event.getAction() == KeyEvent.ACTION_DOWN && event.isCtrlPressed()) { final int character = event.getUnicodeChar(event.getMetaState() & ~KeyEvent.META_CTRL_MASK); if (character == '+') { + focusGroupType = FOCUS_GROUP_CLUSTER; clusterNavigationDirection = View.FOCUS_FORWARD; } if (character == '_') { + focusGroupType = FOCUS_GROUP_CLUSTER; + clusterNavigationDirection = View.FOCUS_BACKWARD; + } + } + + if (event.getAction() == KeyEvent.ACTION_DOWN && event.isAltPressed()) { + final int character = + event.getUnicodeChar(event.getMetaState() & ~KeyEvent.META_ALT_MASK); + if (character == '+') { + focusGroupType = FOCUS_GROUP_SECTION; + clusterNavigationDirection = View.FOCUS_FORWARD; + } + + if (character == '_') { + focusGroupType = FOCUS_GROUP_SECTION; clusterNavigationDirection = View.FOCUS_BACKWARD; } } @@ -4456,7 +4477,7 @@ public final class ViewRootImpl implements ViewParent, // Handle automatic focus changes. if (event.getAction() == KeyEvent.ACTION_DOWN) { if (clusterNavigationDirection != 0) { - if (performClusterNavigation(clusterNavigationDirection)) { + if (performClusterNavigation(focusGroupType, clusterNavigationDirection)) { return FINISH_HANDLED; } } else { @@ -5887,13 +5908,11 @@ public final class ViewRootImpl implements ViewParent, * {@inheritDoc} */ @Override - public View keyboardNavigationClusterSearch(View currentCluster, int direction) { + public View keyboardNavigationClusterSearch( + @FocusGroupType int focusGroupType, View currentCluster, int direction) { checkThread(); - if (!(mView instanceof ViewGroup)) { - return null; - } - return FocusFinder.getInstance().findNextKeyboardNavigationCluster( - (ViewGroup) mView, currentCluster, direction); + return FocusFinder.getInstance().findNextKeyboardNavigationCluster(focusGroupType, + mView, currentCluster, direction); } public void debug() {