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

Commit ff0d298a authored by Adam Powell's avatar Adam Powell
Browse files

Add API to block focus in the presence of a touchscreen

For the sake of devices with touchscreens and optional keyboards, add
the touchscreenBlocksFocus attr and associated get/set methods to
ViewGroup. This will act much like FOCUS_BLOCKS_DESCENDANTS, but only
if the context reports that a touchscreen is present. This allows an
app to define much coarser-grained block elements for focus instead of
navigating between each individual (normally) focusable element, on
the theory that the readily available touchscreen or other keyboard
shortcuts allow for more fine-grained interaction. Keyboard focus
navigation thereby becomes more efficient at a coarse level.

Bug 13987814

Change-Id: Ie652b8845122a59046e96ad6074b3de163779adc
parent d3dc721c
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -1283,6 +1283,7 @@ package android {
    field public static final int topLeftRadius = 16843177; // 0x10101a9
    field public static final int topOffset = 16843352; // 0x1010258
    field public static final int topRightRadius = 16843178; // 0x10101aa
    field public static final int touchscreenBlocksFocus = 16843921; // 0x1010491
    field public static final int track = 16843631; // 0x101036f
    field public static final int transcriptMode = 16843008; // 0x1010100
    field public static final int transformPivotX = 16843552; // 0x1010320
@@ -33577,6 +33578,7 @@ package android.view {
    method public android.animation.LayoutTransition getLayoutTransition();
    method public int getNestedScrollAxes();
    method public int getPersistentDrawingCache();
    method public boolean getTouchscreenBlocksFocus();
    method public int indexOfChild(android.view.View);
    method public final void invalidateChild(android.view.View, android.graphics.Rect);
    method public android.view.ViewParent invalidateChildInParent(int[], android.graphics.Rect);
@@ -33636,6 +33638,7 @@ package android.view {
    method public void setOnHierarchyChangeListener(android.view.ViewGroup.OnHierarchyChangeListener);
    method public void setPersistentDrawingCache(int);
    method protected void setStaticTransformationsEnabled(boolean);
    method public void setTouchscreenBlocksFocus(boolean);
    method public void setTransitionGroup(boolean);
    method public boolean shouldDelayChildPressedState();
    method public boolean showContextMenuForChild(android.view.View);
+2 −1
Original line number Diff line number Diff line
@@ -7431,7 +7431,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
        ViewParent ancestor = mParent;
        while (ancestor instanceof ViewGroup) {
            final ViewGroup vgAncestor = (ViewGroup) ancestor;
            if (vgAncestor.getDescendantFocusability() == ViewGroup.FOCUS_BLOCK_DESCENDANTS) {
            if (vgAncestor.getDescendantFocusability() == ViewGroup.FOCUS_BLOCK_DESCENDANTS
                    || vgAncestor.shouldBlockFocusForTouchscreen()) {
                return true;
            } else {
                ancestor = vgAncestor.getParent();
+53 −4
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package android.view;

import android.animation.LayoutTransition;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
@@ -360,6 +361,11 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager

    static final int FLAG_IS_TRANSITION_GROUP_SET = 0x2000000;

    /**
     * When set, focus will not be permitted to enter this group if a touchscreen is present.
     */
    static final int FLAG_TOUCHSCREEN_BLOCKS_FOCUS = 0x4000000;

    /**
     * Indicates which types of drawing caches are to be kept in memory.
     * This field should be made private, so it is hidden from the SDK.
@@ -567,6 +573,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
                case R.styleable.ViewGroup_transitionGroup:
                    setTransitionGroup(a.getBoolean(attr, false));
                    break;
                case R.styleable.ViewGroup_touchscreenBlocksFocus:
                    setTouchscreenBlocksFocus(a.getBoolean(attr, false));
                    break;
            }
        }

@@ -660,6 +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()
                // shortcut: don't report a new focusable view if we already are focused
                // (and we don't prefer our descendants)
                //
@@ -901,7 +911,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
        }

        final int descendantFocusability = getDescendantFocusability();
        if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS) {
        if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS &&
                !shouldBlockFocusForTouchscreen()) {
            final int count = mChildrenCount;
            final View[] children = mChildren;

@@ -925,7 +936,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager

        final int descendantFocusability = getDescendantFocusability();

        if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS) {
        if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS &&
                !shouldBlockFocusForTouchscreen()) {
            final int count = mChildrenCount;
            final View[] children = mChildren;

@@ -941,13 +953,46 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
        // FOCUS_AFTER_DESCENDANTS and there are some descendants focusable.  this is
        // to avoid the focus search finding layouts when a more precise search
        // among the focusable children would be more interesting.
        if (descendantFocusability != FOCUS_AFTER_DESCENDANTS
        if ((descendantFocusability != FOCUS_AFTER_DESCENDANTS
                // No focusable descendants
                || (focusableCount == views.size())) {
                || (focusableCount == views.size())) && !shouldBlockFocusForTouchscreen()) {
            super.addFocusables(views, direction, focusableMode);
        }
    }

    /**
     * Set whether this ViewGroup should ignore focus requests for itself and its children.
     * If this option is enabled and the ViewGroup or a descendant currently has focus, focus
     * will proceed forward.
     *
     * @param touchscreenBlocksFocus true to enable blocking focus in the presence of a touchscreen
     */
    public void setTouchscreenBlocksFocus(boolean touchscreenBlocksFocus) {
        if (touchscreenBlocksFocus) {
            mGroupFlags |= FLAG_TOUCHSCREEN_BLOCKS_FOCUS;
            if (hasFocus()) {
                final View newFocus = focusSearch(FOCUS_FORWARD);
                if (newFocus != null) {
                    newFocus.requestFocus();
                }
            }
        } else {
            mGroupFlags &= ~FLAG_TOUCHSCREEN_BLOCKS_FOCUS;
        }
    }

    /**
     * Check whether this ViewGroup should ignore focus requests for itself and its children.
     */
    public boolean getTouchscreenBlocksFocus() {
        return (mGroupFlags & FLAG_TOUCHSCREEN_BLOCKS_FOCUS) != 0;
    }

    boolean shouldBlockFocusForTouchscreen() {
        return getTouchscreenBlocksFocus() &&
                mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN);
    }

    @Override
    public void findViewsWithText(ArrayList<View> outViews, CharSequence text, int flags) {
        super.findViewsWithText(outViews, text, flags);
@@ -2440,6 +2485,10 @@ 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);
+3 −0
Original line number Diff line number Diff line
@@ -2520,6 +2520,9 @@
            <enum name="blocksDescendants" value="2" />
        </attr>

        <!-- Set to true if this ViewGroup blocks focus in the presence of a touchscreen. -->
        <attr name="touchscreenBlocksFocus" format="boolean" />

        <!-- Sets whether this ViewGroup should split MotionEvents
             to separate child views during touch event dispatch.
             If false (default), touch events will be dispatched to
+1 −0
Original line number Diff line number Diff line
@@ -2510,4 +2510,5 @@
       a view visibility changes. -->
  <public type="transition" name="slide_left"/>
  <public type="attr" name="multiArch" />
  <public type="attr" name="touchscreenBlocksFocus" />
</resources>
Loading