Adding public API for marking nodes as clusters and sections.

Clusters:
We need clusters to limit the size of the “tab loop” by
widgets related to the current context: working in the
app’s client area, choosing a command in the action bar
etc.

Clusters are a generalization of the current action bar’s
behavior.
An activity can have several clusters.
A cluster is a view or a view group group marked as such.
Pressing Tab loops inside the cluster, but you can exit it via
arrows.
You can teleport between clusters via special key combos.

Sections:
Sections live inside clusters.
They are needed for simplifying navigation in complex
hierarchies: instead of tabbing or arrowing, you can simply
teleport to the the next/previous section by pressing a
special key combo.
Example: think about panes in GMail app or dir/files
panels in a file manager.

Otherwise, sections are normal view groups: for example,
they don’t limit keyboard navigation in any way.

Bug: 32151632
Test: Checking for syntax errors and that Android starts.
Change-Id: Ic78495d0749db65d5177017553d37f870354c6bc
diff --git a/api/current.txt b/api/current.txt
index 460b99c..052924f 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -750,6 +750,8 @@
     field public static final int keyWidth = 16843325; // 0x101023d
     field public static final int keyboardLayout = 16843691; // 0x10103ab
     field public static final int keyboardMode = 16843341; // 0x101024d
+    field public static final int keyboardNavigationCluster = 16844096; // 0x1010540
+    field public static final int keyboardNavigationSection = 16844097; // 0x1010541
     field public static final int keycode = 16842949; // 0x10100c5
     field public static final int killAfterRestore = 16843420; // 0x101029c
     field public static final int label = 16842753; // 0x1010001
@@ -893,11 +895,13 @@
     field public static final int negativeButtonText = 16843254; // 0x10101f6
     field public static final int nestedScrollingEnabled = 16843830; // 0x1010436
     field public static final int networkSecurityConfig = 16844071; // 0x1010527
+    field public static final int nextClusterForward = 16844098; // 0x1010542
     field public static final int nextFocusDown = 16842980; // 0x10100e4
     field public static final int nextFocusForward = 16843580; // 0x101033c
     field public static final int nextFocusLeft = 16842977; // 0x10100e1
     field public static final int nextFocusRight = 16842978; // 0x10100e2
     field public static final int nextFocusUp = 16842979; // 0x10100e3
+    field public static final int nextSectionForward = 16844099; // 0x1010543
     field public static final int noHistory = 16843309; // 0x101022d
     field public static final int normalScreens = 16843397; // 0x1010285
     field public static final int notificationTimeout = 16843651; // 0x1010383
@@ -43076,11 +43080,13 @@
     method public final int getMeasuredWidthAndState();
     method public int getMinimumHeight();
     method public int getMinimumWidth();
+    method public int getNextClusterForwardId();
     method public int getNextFocusDownId();
     method public int getNextFocusForwardId();
     method public int getNextFocusLeftId();
     method public int getNextFocusRightId();
     method public int getNextFocusUpId();
+    method public int getNextSectionForwardId();
     method public android.view.View.OnFocusChangeListener getOnFocusChangeListener();
     method public android.view.ViewOutlineProvider getOutlineProvider();
     method public int getOverScrollMode();
@@ -43183,6 +43189,8 @@
     method public boolean isInEditMode();
     method public boolean isInLayout();
     method public boolean isInTouchMode();
+    method public final boolean isKeyboardNavigationCluster();
+    method public final boolean isKeyboardNavigationSection();
     method public boolean isLaidOut();
     method public boolean isLayoutDirectionResolved();
     method public boolean isLayoutRequested();
@@ -43351,6 +43359,8 @@
     method public void setId(int);
     method public void setImportantForAccessibility(int);
     method public void setKeepScreenOn(boolean);
+    method public void setKeyboardNavigationCluster(boolean);
+    method public void setKeyboardNavigationSection(boolean);
     method public void setLabelFor(int);
     method public void setLayerPaint(android.graphics.Paint);
     method public void setLayerType(int, android.graphics.Paint);
@@ -43362,11 +43372,13 @@
     method public void setMinimumHeight(int);
     method public void setMinimumWidth(int);
     method public void setNestedScrollingEnabled(boolean);
+    method public void setNextClusterForwardId(int);
     method public void setNextFocusDownId(int);
     method public void setNextFocusForwardId(int);
     method public void setNextFocusLeftId(int);
     method public void setNextFocusRightId(int);
     method public void setNextFocusUpId(int);
+    method public void setNextSectionForwardId(int);
     method public void setOnApplyWindowInsetsListener(android.view.View.OnApplyWindowInsetsListener);
     method public void setOnClickListener(android.view.View.OnClickListener);
     method public void setOnContextClickListener(android.view.View.OnContextClickListener);
diff --git a/api/system-current.txt b/api/system-current.txt
index 1ced79c..f29e5fa 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -857,6 +857,8 @@
     field public static final int keyWidth = 16843325; // 0x101023d
     field public static final int keyboardLayout = 16843691; // 0x10103ab
     field public static final int keyboardMode = 16843341; // 0x101024d
+    field public static final int keyboardNavigationCluster = 16844096; // 0x1010540
+    field public static final int keyboardNavigationSection = 16844097; // 0x1010541
     field public static final int keycode = 16842949; // 0x10100c5
     field public static final int killAfterRestore = 16843420; // 0x101029c
     field public static final int label = 16842753; // 0x1010001
@@ -1000,11 +1002,13 @@
     field public static final int negativeButtonText = 16843254; // 0x10101f6
     field public static final int nestedScrollingEnabled = 16843830; // 0x1010436
     field public static final int networkSecurityConfig = 16844071; // 0x1010527
+    field public static final int nextClusterForward = 16844098; // 0x1010542
     field public static final int nextFocusDown = 16842980; // 0x10100e4
     field public static final int nextFocusForward = 16843580; // 0x101033c
     field public static final int nextFocusLeft = 16842977; // 0x10100e1
     field public static final int nextFocusRight = 16842978; // 0x10100e2
     field public static final int nextFocusUp = 16842979; // 0x10100e3
+    field public static final int nextSectionForward = 16844099; // 0x1010543
     field public static final int noHistory = 16843309; // 0x101022d
     field public static final int normalScreens = 16843397; // 0x1010285
     field public static final int notificationTimeout = 16843651; // 0x1010383
@@ -46269,11 +46273,13 @@
     method public final int getMeasuredWidthAndState();
     method public int getMinimumHeight();
     method public int getMinimumWidth();
+    method public int getNextClusterForwardId();
     method public int getNextFocusDownId();
     method public int getNextFocusForwardId();
     method public int getNextFocusLeftId();
     method public int getNextFocusRightId();
     method public int getNextFocusUpId();
+    method public int getNextSectionForwardId();
     method public android.view.View.OnFocusChangeListener getOnFocusChangeListener();
     method public android.view.ViewOutlineProvider getOutlineProvider();
     method public int getOverScrollMode();
@@ -46376,6 +46382,8 @@
     method public boolean isInEditMode();
     method public boolean isInLayout();
     method public boolean isInTouchMode();
+    method public final boolean isKeyboardNavigationCluster();
+    method public final boolean isKeyboardNavigationSection();
     method public boolean isLaidOut();
     method public boolean isLayoutDirectionResolved();
     method public boolean isLayoutRequested();
@@ -46544,6 +46552,8 @@
     method public void setId(int);
     method public void setImportantForAccessibility(int);
     method public void setKeepScreenOn(boolean);
+    method public void setKeyboardNavigationCluster(boolean);
+    method public void setKeyboardNavigationSection(boolean);
     method public void setLabelFor(int);
     method public void setLayerPaint(android.graphics.Paint);
     method public void setLayerType(int, android.graphics.Paint);
@@ -46555,11 +46565,13 @@
     method public void setMinimumHeight(int);
     method public void setMinimumWidth(int);
     method public void setNestedScrollingEnabled(boolean);
+    method public void setNextClusterForwardId(int);
     method public void setNextFocusDownId(int);
     method public void setNextFocusForwardId(int);
     method public void setNextFocusLeftId(int);
     method public void setNextFocusRightId(int);
     method public void setNextFocusUpId(int);
+    method public void setNextSectionForwardId(int);
     method public void setOnApplyWindowInsetsListener(android.view.View.OnApplyWindowInsetsListener);
     method public void setOnClickListener(android.view.View.OnClickListener);
     method public void setOnContextClickListener(android.view.View.OnContextClickListener);
diff --git a/api/test-current.txt b/api/test-current.txt
index 49f48d8..69beee5 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -750,6 +750,8 @@
     field public static final int keyWidth = 16843325; // 0x101023d
     field public static final int keyboardLayout = 16843691; // 0x10103ab
     field public static final int keyboardMode = 16843341; // 0x101024d
+    field public static final int keyboardNavigationCluster = 16844096; // 0x1010540
+    field public static final int keyboardNavigationSection = 16844097; // 0x1010541
     field public static final int keycode = 16842949; // 0x10100c5
     field public static final int killAfterRestore = 16843420; // 0x101029c
     field public static final int label = 16842753; // 0x1010001
@@ -893,11 +895,13 @@
     field public static final int negativeButtonText = 16843254; // 0x10101f6
     field public static final int nestedScrollingEnabled = 16843830; // 0x1010436
     field public static final int networkSecurityConfig = 16844071; // 0x1010527
+    field public static final int nextClusterForward = 16844098; // 0x1010542
     field public static final int nextFocusDown = 16842980; // 0x10100e4
     field public static final int nextFocusForward = 16843580; // 0x101033c
     field public static final int nextFocusLeft = 16842977; // 0x10100e1
     field public static final int nextFocusRight = 16842978; // 0x10100e2
     field public static final int nextFocusUp = 16842979; // 0x10100e3
+    field public static final int nextSectionForward = 16844099; // 0x1010543
     field public static final int noHistory = 16843309; // 0x101022d
     field public static final int normalScreens = 16843397; // 0x1010285
     field public static final int notificationTimeout = 16843651; // 0x1010383
@@ -43344,11 +43348,13 @@
     method public final int getMeasuredWidthAndState();
     method public int getMinimumHeight();
     method public int getMinimumWidth();
+    method public int getNextClusterForwardId();
     method public int getNextFocusDownId();
     method public int getNextFocusForwardId();
     method public int getNextFocusLeftId();
     method public int getNextFocusRightId();
     method public int getNextFocusUpId();
+    method public int getNextSectionForwardId();
     method public android.view.View.OnFocusChangeListener getOnFocusChangeListener();
     method public android.view.ViewOutlineProvider getOutlineProvider();
     method public int getOverScrollMode();
@@ -43452,6 +43458,8 @@
     method public boolean isInEditMode();
     method public boolean isInLayout();
     method public boolean isInTouchMode();
+    method public final boolean isKeyboardNavigationCluster();
+    method public final boolean isKeyboardNavigationSection();
     method public boolean isLaidOut();
     method public boolean isLayoutDirectionResolved();
     method public boolean isLayoutRequested();
@@ -43620,6 +43628,8 @@
     method public void setId(int);
     method public void setImportantForAccessibility(int);
     method public void setKeepScreenOn(boolean);
+    method public void setKeyboardNavigationCluster(boolean);
+    method public void setKeyboardNavigationSection(boolean);
     method public void setLabelFor(int);
     method public void setLayerPaint(android.graphics.Paint);
     method public void setLayerType(int, android.graphics.Paint);
@@ -43631,11 +43641,13 @@
     method public void setMinimumHeight(int);
     method public void setMinimumWidth(int);
     method public void setNestedScrollingEnabled(boolean);
+    method public void setNextClusterForwardId(int);
     method public void setNextFocusDownId(int);
     method public void setNextFocusForwardId(int);
     method public void setNextFocusLeftId(int);
     method public void setNextFocusRightId(int);
     method public void setNextFocusUpId(int);
+    method public void setNextSectionForwardId(int);
     method public void setOnApplyWindowInsetsListener(android.view.View.OnApplyWindowInsetsListener);
     method public void setOnClickListener(android.view.View.OnClickListener);
     method public void setOnContextClickListener(android.view.View.OnContextClickListener);
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 3f4c49b..9e4bb4c 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -2474,7 +2474,9 @@
      *                     1             PFLAG3_SCROLL_INDICATOR_START
      *                    1              PFLAG3_SCROLL_INDICATOR_END
      *                   1               PFLAG3_ASSIST_BLOCKED
-     *           xxxxxxxx                * NO LONGER NEEDED, SHOULD BE REUSED *
+     *                  1                PFLAG3_CLUSTER
+     *                 1                 PFLAG3_SECTION
+     *           xxxxxx                  * NO LONGER NEEDED, SHOULD BE REUSED *
      *          1                        PFLAG3_OVERLAPPING_RENDERING_FORCED_VALUE
      *         1                         PFLAG3_HAS_OVERLAPPING_RENDERING_FORCED
      *        1                          PFLAG3_TEMPORARY_DETACH
@@ -2673,6 +2675,22 @@
     static final int PFLAG3_ASSIST_BLOCKED = 0x4000;
 
     /**
+     * Flag indicating that the view is a root of a keyboard navigation cluster.
+     *
+     * @see #isKeyboardNavigationCluster()
+     * @see #setKeyboardNavigationCluster(boolean)
+     */
+    private static final int PFLAG3_CLUSTER = 0x8000;
+
+    /**
+     * Flag indicating that the view is a root of a keyboard navigation section.
+     *
+     * @see #isKeyboardNavigationSection()
+     * @see #setKeyboardNavigationSection(boolean)
+     */
+    private static final int PFLAG3_SECTION = 0x10000;
+
+    /**
      * Whether this view has rendered elements that overlap (see {@link
      * #hasOverlappingRendering()}, {@link #forceHasOverlappingRendering(boolean)}, and
      * {@link #getHasOverlappingRendering()} ). The value in this bit is only valid when
@@ -3740,6 +3758,16 @@
      */
     int mNextFocusForwardId = View.NO_ID;
 
+    /**
+     * User-specified next keyboard navigation cluster.
+     */
+    int mNextClusterForwardId = View.NO_ID;
+
+    /**
+     * User-specified next keyboard navigation section.
+     */
+    int mNextSectionForwardId = View.NO_ID;
+
     private CheckForLongPress mPendingCheckForLongPress;
     private CheckForTap mPendingCheckForTap = null;
     private PerformClick mPerformClick;
@@ -4531,6 +4559,12 @@
                 case R.styleable.View_nextFocusForward:
                     mNextFocusForwardId = a.getResourceId(attr, View.NO_ID);
                     break;
+                case R.styleable.View_nextClusterForward:
+                    mNextClusterForwardId = a.getResourceId(attr, View.NO_ID);
+                    break;
+                case R.styleable.View_nextSectionForward:
+                    mNextSectionForwardId = a.getResourceId(attr, View.NO_ID);
+                    break;
                 case R.styleable.View_minWidth:
                     mMinWidth = a.getDimensionPixelSize(attr, 0);
                     break;
@@ -4667,10 +4701,19 @@
                         forceHasOverlappingRendering(a.getBoolean(attr, true));
                     }
                     break;
-
                 case R.styleable.View_tooltip:
                     setTooltip(a.getText(attr));
                     break;
+                case R.styleable.View_keyboardNavigationCluster:
+                    if (a.peekValue(attr) != null) {
+                        setKeyboardNavigationCluster(a.getBoolean(attr, true));
+                    }
+                    break;
+                case R.styleable.View_keyboardNavigationSection:
+                    if (a.peekValue(attr) != null) {
+                        setKeyboardNavigationSection(a.getBoolean(attr, true));
+                    }
+                    break;
             }
         }
 
@@ -7852,6 +7895,50 @@
     }
 
     /**
+     * Gets the id of the root of the next keyboard navigation cluster.
+     * @return The next keyboard navigation cluster ID, or {@link #NO_ID} if the framework should
+     * decide automatically.
+     *
+     * @attr ref android.R.styleable#View_nextClusterForward
+     */
+    public int getNextClusterForwardId() {
+        return mNextClusterForwardId;
+    }
+
+    /**
+     * Sets the id of the view to use as the root of the next keyboard navigation cluster.
+     * @param nextClusterForwardId The next cluster ID, or {@link #NO_ID} if the framework should
+     * decide automatically.
+     *
+     * @attr ref android.R.styleable#View_nextClusterForward
+     */
+    public void setNextClusterForwardId(int nextClusterForwardId) {
+        mNextClusterForwardId = nextClusterForwardId;
+    }
+
+    /**
+     * Gets the id of the root of the next keyboard navigation section.
+     * @return The next keyboard navigation section ID, or {@link #NO_ID} if the framework should
+     * decide automatically.
+     *
+     * @attr ref android.R.styleable#View_nextSectionForward
+     */
+    public int getNextSectionForwardId() {
+        return mNextSectionForwardId;
+    }
+
+    /**
+     * Sets the id of the view to use as the root of the next keyboard navigation section.
+     * @param nextSectionForwardId The next section ID, or {@link #NO_ID} if the framework should
+     * decide automatically.
+     *
+     * @attr ref android.R.styleable#View_nextSectionForward
+     */
+    public void setNextSectionForwardId(int nextSectionForwardId) {
+        mNextSectionForwardId = nextSectionForwardId;
+    }
+
+    /**
      * Returns the visibility of this view and all of its ancestors
      *
      * @return True if this view and all of its ancestors are {@link #VISIBLE}
@@ -8947,6 +9034,58 @@
     }
 
     /**
+     * Returns whether this View is a root of a keyboard navigation cluster.
+     *
+     * @return True if this view is a root of a cluster, or false otherwise.
+     * @attr ref android.R.styleable#View_keyboardNavigationCluster
+     */
+    @ViewDebug.ExportedProperty(category = "keyboardNavigationCluster")
+    public final boolean isKeyboardNavigationCluster() {
+        return (mPrivateFlags3 & PFLAG3_CLUSTER) != 0;
+    }
+
+    /**
+     * Set whether this view is a root of a keyboard navigation cluster.
+     *
+     * @param isCluster If true, this view is a root of a cluster.
+     *
+     * @attr ref android.R.styleable#View_keyboardNavigationCluster
+     */
+    public void setKeyboardNavigationCluster(boolean isCluster) {
+        if (isCluster) {
+            mPrivateFlags3 |= PFLAG3_CLUSTER;
+        } else {
+            mPrivateFlags3 &= ~PFLAG3_CLUSTER;
+        }
+    }
+
+    /**
+     * Returns whether this View is a root of a keyboard navigation section.
+     *
+     * @return True if this view is a root of a section, or false otherwise.
+     * @attr ref android.R.styleable#View_keyboardNavigationSection
+     */
+    @ViewDebug.ExportedProperty(category = "keyboardNavigationSection")
+    public final boolean isKeyboardNavigationSection() {
+        return (mPrivateFlags3 & PFLAG3_SECTION) != 0;
+    }
+
+    /**
+     * Set whether this view is a root of a keyboard navigation section.
+     *
+     * @param isSection If true, this view is a root of a section.
+     *
+     * @attr ref android.R.styleable#View_keyboardNavigationSection
+     */
+    public void setKeyboardNavigationSection(boolean isSection) {
+        if (isSection) {
+            mPrivateFlags3 |= PFLAG3_SECTION;
+        } else {
+            mPrivateFlags3 &= ~PFLAG3_SECTION;
+        }
+    }
+
+    /**
      * This method is the last chance for the focused view and its ancestors to
      * respond to an arrow key. This is called when the focused view did not
      * consume the key internally, nor could the view system find a new view in
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 8c64484..aa050de 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -2887,6 +2887,28 @@
         <!-- Defines text displayed in a small popup window on hover or long press. -->
         <attr name="tooltip" format="string" localization="suggested" />
 
+        <!-- Whether this view is a root of a keyboard navigation cluster.
+             See {@link android.view.View#setKeyboardNavigationCluster(boolean)}. -->
+        <attr name="keyboardNavigationCluster" format="boolean" />
+
+        <!-- Whether this view is a root of a keyboard navigation section.
+             See {@link android.view.View#setKeyboardNavigationSection(boolean)}. -->
+        <attr name="keyboardNavigationSection" format="boolean" />
+
+        <!-- Defines the next keyboard navigation cluster.
+
+             If the reference refers to a view that does not exist or is part
+             of a hierarchy that is invisible, a {@link java.lang.RuntimeException}
+             will result when the reference is accessed.-->
+        <attr name="nextClusterForward" format="reference"/>
+
+        <!-- Defines the next keyboard navigation section.
+
+             If the reference refers to a view that does not exist or is part
+             of a hierarchy that is invisible, a {@link java.lang.RuntimeException}
+             will result when the reference is accessed.-->
+        <attr name="nextSectionForward" format="reference"/>
+
     </declare-styleable>
 
     <!-- Attributes that can be assigned to a tag for a particular View. -->
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 37c4fd1..4604f3f 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2776,6 +2776,10 @@
         <public name="paddingHorizontal" />
         <public name="paddingVertical" />
         <public name="visibleToEphemeral" />
+        <public name="keyboardNavigationCluster" />
+        <public name="keyboardNavigationSection" />
+        <public name="nextClusterForward" />
+        <public name="nextSectionForward" />
     </public-group>
 
     <public-group type="style" first-id="0x010302e0">